Guest User

Untitled

a guest
Jun 20th, 2018
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 56.78 KB | None | 0 0
  1. //KI.cpp by Kaan Holat
  2.  
  3. #include "game.h"
  4.  
  5. extern int fog;
  6.  
  7. namespace ai
  8. {
  9.     using namespace game;
  10.  
  11.     avoidset obstacles;
  12.     int updatemillis = 0, updateiteration = 0, forcegun = -1;
  13.     vec aitarget(0, 0, 0);
  14.  
  15.     VAR(aidebug, 0, 0, 6);
  16.     VAR(aiforcegun, -1, -1, NUMGUNS-1);
  17.  
  18.     ICOMMAND(addbot, "s", (char *s), addmsg(N_ADDBOT, "ri", *s ? clamp(parseint(s), 1, 101) : -1));
  19.     ICOMMAND(delbot, "", (), addmsg(N_DELBOT, "r"));
  20.     ICOMMAND(botlimit, "i", (int *n), addmsg(N_BOTLIMIT, "ri", *n));
  21.     ICOMMAND(botbalance, "i", (int *n), addmsg(N_BOTBALANCE, "ri", *n));
  22.  
  23.     float viewdist(int x)
  24.     {
  25.         return x <= 100 ? clamp((SIGHTMIN+(SIGHTMAX-SIGHTMIN))/100.f*float(x), float(SIGHTMIN), float(fog)) : float(fog);
  26.     }
  27.  
  28.     float viewfieldx(int x)
  29.     {
  30.         return x <= 100 ? clamp((VIEWMIN+(VIEWMAX-VIEWMIN))/100.f*float(x), float(VIEWMIN), float(VIEWMAX)) : float(VIEWMAX);
  31.     }
  32.  
  33.     float viewfieldy(int x)
  34.     {
  35.         return viewfieldx(x)*3.f/4.f;
  36.     }
  37.  
  38.     bool canmove(fpsent *d)
  39.     {
  40.         return d->state != CS_DEAD && !intermission;
  41.     }
  42.  
  43.     float weapmindist(int weap)
  44.     {
  45.         return guns[weap].projspeed ? RL_DAMRAD : 2;
  46.     }
  47.  
  48.     float weapmaxdist(int weap)
  49.     {
  50.         return guns[weap].range + 4;
  51.     }
  52.  
  53.     bool weaprange(fpsent *d, int weap, float dist)
  54.     {
  55.         float mindist = weapmindist(weap), maxdist = weapmaxdist(weap);
  56.         return dist >= mindist*mindist && dist <= maxdist*maxdist;
  57.     }
  58.  
  59.     bool targetable(fpsent *d, fpsent *e)
  60.     {
  61.         if(d == e || !canmove(d)) return false;
  62.         return e->state == CS_ALIVE && !isteam(d->team, e->team);
  63.     }
  64.  
  65.     bool getsight(vec &o, float yaw, float pitch, vec &q, vec &v, float mdist, float fovx, float fovy)
  66.     {
  67.         float dist = o.dist(q);
  68.  
  69.         if(dist <= mdist)
  70.         {
  71.             float x = fmod(fabs(asin((q.z-o.z)/dist)/RAD-pitch), 360);
  72.             float y = fmod(fabs(-atan2(q.x-o.x, q.y-o.y)/RAD-yaw), 360);
  73.             if(min(x, 360-x) <= fovx && min(y, 360-y) <= fovy) return raycubelos(o, q, v);
  74.         }
  75.         return false;
  76.     }
  77.  
  78.     bool cansee(fpsent *d, vec &x, vec &y, vec &targ)
  79.     {
  80.         aistate &b = d->ai->getstate();
  81.         if(canmove(d) && b.type != AI_S_WAIT)
  82.             return getsight(x, d->yaw, d->pitch, y, targ, d->ai->views[2], d->ai->views[0], d->ai->views[1]);
  83.         return false;
  84.     }
  85.  
  86.     bool canshoot(fpsent *d, fpsent *e)
  87.     {
  88.         if(weaprange(d, d->gunselect, e->o.squaredist(d->o)) && targetable(d, e))
  89.             return d->ammo[d->gunselect] > 0 && lastmillis - d->lastaction >= d->gunwait;
  90.         return false;
  91.     }
  92.  
  93.     bool canshoot(fpsent *d)
  94.     {
  95.         return !d->ai->becareful && d->ammo[d->gunselect] > 0 && lastmillis - d->lastaction >= d->gunwait;
  96.     }
  97.  
  98.     bool hastarget(fpsent *d, aistate &b, fpsent *e, float yaw, float pitch, float dist)
  99.     { // add margins of error
  100.         if(weaprange(d, d->gunselect, dist) || (d->skill <= 100 && !rnd(d->skill)))
  101.         {
  102.             if(d->gunselect == GUN_FIST) return true;
  103.             float skew = clamp(float(lastmillis-d->ai->enemymillis)/float((d->skill*guns[d->gunselect].attackdelay/200.f)), 0.f, guns[d->gunselect].projspeed ? 0.25f : 1e16f),
  104.                 offy = yaw-d->yaw, offp = pitch-d->pitch;
  105.             if(offy > 180) offy -= 360;
  106.             else if(offy < -180) offy += 360;
  107.             if(fabs(offy) <= d->ai->views[0]*skew && fabs(offp) <= d->ai->views[1]*skew) return true;
  108.         }
  109.         return false;
  110.     }
  111.  
  112.     vec getaimpos(fpsent *d, fpsent *e)
  113.     {
  114.         vec o = e->o;
  115.         if(d->gunselect == GUN_RL) o.z += (e->aboveeye*0.2f)-(0.8f*d->eyeheight);
  116.         else if(d->gunselect != GUN_GL) o.z += (e->aboveeye-e->eyeheight)*0.5f;
  117.         if(d->skill <= 100)
  118.         {
  119.             if(lastmillis >= d->ai->lastaimrnd)
  120.             {
  121.                 const int aiskew[NUMGUNS] = { 1, 10, 50, 5, 20, 1, 100, 10, 10, 10, 1, 1 };
  122.                 #define rndaioffset(r) ((rnd(int(r*aiskew[d->gunselect]*2)+1)-(r*aiskew[d->gunselect]))*(1.f/float(max(d->skill, 1))))
  123.                 loopk(3) d->ai->aimrnd[k] = rndaioffset(e->radius);
  124.                 int dur = (d->skill+10)*10;
  125.                 d->ai->lastaimrnd = lastmillis+dur+rnd(dur);
  126.             }
  127.             loopk(3) o[k] += d->ai->aimrnd[k];
  128.         }
  129.         return o;
  130.     }
  131.  
  132.     void create(fpsent *d)
  133.     {
  134.         if(!d->ai) d->ai = new aiinfo;
  135.     }
  136.  
  137.     void destroy(fpsent *d)
  138.     {
  139.         if(d->ai) DELETEP(d->ai);
  140.     }
  141.  
  142.     void init(fpsent *d, int at, int ocn, int sk, int bn, int pm, const char *name, const char *team)
  143.     {
  144.         loadwaypoints();
  145.  
  146.         fpsent *o = newclient(ocn);
  147.  
  148.         d->aitype = at;
  149.  
  150.         bool resetthisguy = false;
  151.         if(!d->name[0])
  152.         {
  153.             if(aidebug) conoutf("%s assigned to %s at skill %d", colorname(d, name), o ? colorname(o) : "?", sk);
  154.             else conoutf("connected: %s", colorname(d, name));
  155.             resetthisguy = true;
  156.         }
  157.         else
  158.         {
  159.             if(d->ownernum != ocn)
  160.             {
  161.                 if(aidebug) conoutf("%s reassigned to %s", colorname(d, name), o ? colorname(o) : "?");
  162.                 resetthisguy = true;
  163.             }
  164.             if(d->skill != sk && aidebug) conoutf("%s changed skill to %d", colorname(d, name), sk);
  165.         }
  166.  
  167.         copystring(d->name, name, MAXNAMELEN+1);
  168.         copystring(d->team, team, MAXTEAMLEN+1);
  169.         d->ownernum = ocn;
  170.         d->skill = sk;
  171.         d->playermodel = chooserandomplayermodel(pm);
  172.  
  173.         if(resetthisguy) removeweapons(d);
  174.         if(d->ownernum >= 0 && player1->clientnum == d->ownernum)
  175.         {
  176.             create(d);
  177.             if(d->ai)
  178.             {
  179.                 d->ai->views[0] = viewfieldx(d->skill);
  180.                 d->ai->views[1] = viewfieldy(d->skill);
  181.                 d->ai->views[2] = viewdist(d->skill);
  182.             }
  183.         }
  184.         else if(d->ai) destroy(d);
  185.     }
  186.  
  187.     void update()
  188.     {
  189.         if(intermission) { loopv(players) if(players[i]->ai) players[i]->stopmoving(); }
  190.         else // fixed rate logic done out-of-sequence at 1 frame per second for each ai
  191.         {
  192.             if(!updateiteration && lastmillis-updatemillis > 1000)
  193.             {
  194.                 avoid();
  195.                 forcegun = multiplayer(false) ? -1 : aiforcegun;
  196.                 updateiteration = 1;
  197.                 updatemillis = lastmillis;
  198.             }
  199.             int count = 0;
  200.             loopv(players) if(players[i]->ai) think(players[i], ++count == updateiteration ? true : false);
  201.             if(++updateiteration > count) updateiteration = 0;
  202.         }
  203.     }
  204.  
  205.     bool checkothers(vector<int> &targets, fpsent *d, int state, int targtype, int target, bool teams)
  206.     { // checks the states of other ai for a match
  207.         targets.setsize(0);
  208.         loopv(players)
  209.         {
  210.             fpsent *e = players[i];
  211.             if(e == d || !e->ai || e->state != CS_ALIVE) continue;
  212.             if(targets.find(e->clientnum) >= 0) continue;
  213.             if(teams && d && !isteam(d->team, e->team)) continue;
  214.             aistate &b = e->ai->getstate();
  215.             if(state >= 0 && b.type != state) continue;
  216.             if(target >= 0 && b.target != target) continue;
  217.             if(targtype >=0 && b.targtype != targtype) continue;
  218.             targets.add(e->clientnum);
  219.         }
  220.         return !targets.empty();
  221.     }
  222.  
  223.     bool makeroute(fpsent *d, aistate &b, int node, bool changed, bool retry)
  224.     {
  225.         if(!waypoints.inrange(d->lastnode)) return false;
  226.         if(changed && d->ai->route.length() > 1 && d->ai->route[0] == node) return true;
  227.         if(route(d, d->lastnode, node, d->ai->route, obstacles, retry))
  228.         {
  229.             b.override = false;
  230.             return true;
  231.         }
  232.         d->ai->clear(true);
  233.         if(!retry) return makeroute(d, b, node, false, true);
  234.         return false;
  235.     }
  236.  
  237.     bool makeroute(fpsent *d, aistate &b, const vec &pos, bool changed, bool retry)
  238.     {
  239.         int node = closestwaypoint(pos, SIGHTMIN, true);
  240.         return makeroute(d, b, node, changed, retry);
  241.     }
  242.  
  243.     bool randomnode(fpsent *d, aistate &b, const vec &pos, float guard, float wander)
  244.     {
  245.         static vector<int> candidates;
  246.         candidates.setsize(0);
  247.         findwaypointswithin(pos, guard, wander, candidates);
  248.  
  249.         while(!candidates.empty())
  250.         {
  251.             int w = rnd(candidates.length()), n = candidates.removeunordered(w);
  252.             if(n != d->lastnode && !d->ai->hasprevnode(n) && !obstacles.find(n, d) && makeroute(d, b, n)) return true;
  253.         }
  254.         return false;
  255.     }
  256.  
  257.     bool randomnode(fpsent *d, aistate &b, float guard, float wander)
  258.     {
  259.         return randomnode(d, b, d->feetpos(), guard, wander);
  260.     }
  261.  
  262.     bool badhealth(fpsent *d)
  263.     {
  264.         if(d->skill <= 100) return d->health <= (111-d->skill)/4;
  265.         return false;
  266.     }
  267.  
  268.     bool enemy(fpsent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, bool pursue = false)
  269.     {
  270.         fpsent *t = NULL;
  271.         vec dp = d->headpos();
  272.         float mindist = guard*guard, bestdist = 1e16f;
  273.         loopv(players)
  274.         {
  275.             fpsent *e = players[i];
  276.             if(e == d || !targetable(d, e)) continue;
  277.             vec ep = getaimpos(d, e);
  278.             float dist = ep.squaredist(dp);
  279.             if(dist < bestdist && (cansee(d, dp, ep) || dist <= mindist))
  280.             {
  281.                 t = e;
  282.                 bestdist = dist;
  283.             }
  284.         }
  285.         if(t && violence(d, b, t, pursue)) return true;
  286.         return false;
  287.     }
  288.  
  289.     bool patrol(fpsent *d, aistate &b, const vec &pos, float guard, float wander, int walk, bool retry)
  290.     {
  291.         vec feet = d->feetpos();
  292.         if(walk == 2 || b.override || (walk && feet.squaredist(pos) <= guard*guard) || !makeroute(d, b, pos))
  293.         { // run away and back to keep ourselves busy
  294.             if(!b.override && randomnode(d, b, pos, guard, wander))
  295.             {
  296.                 b.override = true;
  297.                 return true;
  298.             }
  299.             else if(d->ai->route.empty())
  300.             {
  301.                 if(!retry)
  302.                 {
  303.                     b.override = false;
  304.                     return patrol(d, b, pos, guard, wander, walk, true);
  305.                 }
  306.                 b.override = false;
  307.                 return false;
  308.             }
  309.         }
  310.         b.override = false;
  311.         return true;
  312.     }
  313.  
  314.     bool defend(fpsent *d, aistate &b, const vec &pos, float guard, float wander, int walk)
  315.     {
  316.         bool hasenemy = enemy(d, b, pos, wander, d->gunselect == GUN_FIST);
  317.         if(!walk)
  318.         {
  319.             if(d->feetpos().squaredist(pos) <= guard*guard)
  320.             {
  321.                 b.idle = hasenemy ? 2 : 1;
  322.                 return true;
  323.             }
  324.             walk++;
  325.         }
  326.         return patrol(d, b, pos, guard, wander, walk);
  327.     }
  328.  
  329.     bool violence(fpsent *d, aistate &b, fpsent *e, bool pursue)
  330.     {
  331.         if(e && targetable(d, e))
  332.         {
  333.             if(pursue && waypoints.inrange(d->lastnode)) d->ai->switchstate(b, AI_S_PURSUE, AI_T_PLAYER, e->clientnum);
  334.             if(d->ai->enemy != e->clientnum)
  335.             {
  336.                 d->ai->enemyseen = d->ai->enemymillis = lastmillis;
  337.                 d->ai->enemy = e->clientnum;
  338.             }
  339.             return true;
  340.         }
  341.         return false;
  342.     }
  343.  
  344.     bool target(fpsent *d, aistate &b, bool pursue = false, bool force = false, float mindist = 0.f)
  345.     {
  346.         fpsent *t = NULL;
  347.         vec dp = d->headpos(), tp(0, 0, 0);
  348.         loopv(players)
  349.         {
  350.             fpsent *e = players[i];
  351.             if(e == d || !targetable(d, e)) continue;
  352.             vec ep = getaimpos(d, e);
  353.             float dist = ep.squaredist(dp);
  354.             if((!t || dist < tp.squaredist(dp)) && ((mindist > 0 && dist <= mindist) || force || cansee(d, dp, ep)))
  355.             {
  356.                 t = e;
  357.                 tp = ep;
  358.             }
  359.         }
  360.         if(t) return violence(d, b, t, pursue);
  361.         return false;
  362.     }
  363.  
  364.     int isgoodammo(int gun) { return gun >= GUN_SG && gun <= GUN_GL; }
  365.  
  366.     bool hasgoodammo(fpsent *d)
  367.     {
  368.         static const int goodguns[] = { GUN_CG, GUN_RL, GUN_SG, GUN_RIFLE };
  369.         loopi(sizeof(goodguns)/sizeof(goodguns[0])) if(d->hasammo(goodguns[0])) return true;
  370.         if(d->ammo[GUN_GL] > 5) return true;
  371.         return false;
  372.     }
  373.  
  374.     void assist(fpsent *d, aistate &b, vector<interest> &interests, bool all, bool force)
  375.     {
  376.         loopv(players)
  377.         {
  378.             fpsent *e = players[i];
  379.             if(e == d || (!all && e->aitype != AI_NONE) || !isteam(d->team, e->team)) continue;
  380.             interest &n = interests.add();
  381.             n.state = AI_S_DEFEND;
  382.             n.node = e->lastnode;
  383.             n.target = e->clientnum;
  384.             n.targtype = AI_T_PLAYER;
  385.             n.score = e->o.squaredist(d->o)/(hasgoodammo(d) ? 1e8f : (force ? 1e4f : 1e2f));
  386.         }
  387.     }
  388.  
  389.     static void tryitem(fpsent *d, extentity &e, int id, aistate &b, vector<interest> &interests, bool force = false)
  390.     {
  391.         float score = 0;
  392.         switch(e.type)
  393.         {
  394.             case I_HEALTH:
  395.                 if(d->health < min(d->skill, 75)) score = 1e3f;
  396.                 break;
  397.             case I_QUAD: score = 1e3f; break;
  398.             case I_BOOST: score = 1e2f; break;
  399.             case I_GREENARMOUR: case I_YELLOWARMOUR:
  400.             {
  401.                 int atype = A_GREEN + e.type - I_GREENARMOUR;
  402.                 if(atype > d->armourtype) score = atype == A_YELLOW ? 1e2f : 1e1f;
  403.                 else if(d->armour < 50) score = 1e1f;
  404.                 break;
  405.             }
  406.             default:
  407.             {
  408.                 if(e.type >= I_SHELLS && e.type <= I_CARTRIDGES && !d->hasmaxammo(e.type))
  409.                 {
  410.                     int gun = e.type - I_SHELLS + GUN_SG;
  411.                     // go get a weapon upgrade
  412.                     if(gun == d->ai->weappref) score = 1e8f;
  413.                     else if(isgoodammo(gun)) score = hasgoodammo(d) ? 1e2f : 1e4f;
  414.                 }
  415.                 break;
  416.             }
  417.         }
  418.         if(score != 0)
  419.         {
  420.             interest &n = interests.add();
  421.             n.state = AI_S_INTEREST;
  422.             n.node = closestwaypoint(e.o, SIGHTMIN, true);
  423.             n.target = id;
  424.             n.targtype = AI_T_ENTITY;
  425.             n.score = d->feetpos().squaredist(e.o)/(force ? -1 : score);
  426.         }
  427.     }
  428.  
  429.     void items(fpsent *d, aistate &b, vector<interest> &interests, bool force = false)
  430.     {
  431.         loopv(entities::ents)
  432.         {
  433.             extentity &e = *(extentity *)entities::ents[i];
  434.             if(!e.spawned || !d->canpickup(e.type)) continue;
  435.             tryitem(d, e, i, b, interests, force);
  436.         }
  437.     }
  438.  
  439.     static vector<int> targets;
  440.  
  441.     bool parseinterests(fpsent *d, aistate &b, vector<interest> &interests, bool override, bool ignore)
  442.     {
  443.         while(!interests.empty())
  444.         {
  445.             int q = interests.length()-1;
  446.             loopi(interests.length()-1) if(interests[i].score < interests[q].score) q = i;
  447.             interest n = interests.removeunordered(q);
  448.             bool proceed = true;
  449.             if(!ignore) switch(n.state)
  450.             {
  451.                 case AI_S_DEFEND: // don't get into herds
  452.                     proceed = !checkothers(targets, d, n.state, n.targtype, n.target, true);
  453.                     break;
  454.                 default: break;
  455.             }
  456.             if(proceed && makeroute(d, b, n.node, false))
  457.             {
  458.                 d->ai->switchstate(b, n.state, n.targtype, n.target);
  459.                 return true;
  460.             }
  461.         }
  462.         return false;
  463.     }
  464.  
  465.     bool find(fpsent *d, aistate &b, bool override = false)
  466.     {
  467.         static vector<interest> interests;
  468.         interests.setsize(0);
  469.         if(!m_noitems)
  470.         {
  471.             if((!m_noammo && !hasgoodammo(d)) || d->health < min(d->skill - 15, 75))
  472.                 items(d, b, interests);
  473.             else
  474.             {
  475.                 static vector<int> nearby;
  476.                 nearby.setsize(0);
  477.                 findents(I_SHELLS, I_QUAD, false, d->feetpos(), vec(32, 32, 24), nearby);
  478.                 loopv(nearby)
  479.                 {
  480.                     int id = nearby[i];
  481.                     extentity &e = *(extentity *)entities::ents[id];
  482.                     if(d->canpickup(e.type)) tryitem(d, e, id, b, interests);
  483.                 }
  484.             }
  485.         }
  486.         if(cmode) cmode->aifind(d, b, interests);
  487.         if(m_teammode) assist(d, b, interests);
  488.         return parseinterests(d, b, interests, override);
  489.     }
  490.  
  491.     bool findassist(fpsent *d, aistate &b, bool override = false)
  492.     {
  493.         static vector<interest> interests;
  494.         interests.setsize(0);
  495.         assist(d, b, interests);
  496.         while(!interests.empty())
  497.         {
  498.             int q = interests.length()-1;
  499.             loopi(interests.length()-1) if(interests[i].score < interests[q].score) q = i;
  500.             interest n = interests.removeunordered(q);
  501.             bool proceed = true;
  502.             switch(n.state)
  503.             {
  504.                 case AI_S_DEFEND: // don't get into herds
  505.                     proceed = !checkothers(targets, d, n.state, n.targtype, n.target, true);
  506.                     break;
  507.                 default: break;
  508.             }
  509.             if(proceed && makeroute(d, b, n.node, false))
  510.             {
  511.                 d->ai->switchstate(b, n.state, n.targtype, n.target);
  512.                 return true;
  513.             }
  514.         }
  515.         return false;
  516.     }
  517.  
  518.     void damaged(fpsent *d, fpsent *e)
  519.     {
  520.         if(d->ai && canmove(d) && targetable(d, e)) // see if this ai is interested in a grudge
  521.         {
  522.             aistate &b = d->ai->getstate();
  523.             if(violence(d, b, e, d->gunselect == GUN_FIST)) return;
  524.         }
  525.         if(checkothers(targets, d, AI_S_DEFEND, AI_T_PLAYER, d->clientnum, true))
  526.         {
  527.             loopv(targets)
  528.             {
  529.                 fpsent *t = getclient(targets[i]);
  530.                 if(!t->ai || !canmove(t) || !targetable(t, e)) continue;
  531.                 aistate &c = t->ai->getstate();
  532.                 if(violence(t, c, e, d->gunselect == GUN_FIST)) return;
  533.             }
  534.         }
  535.     }
  536.  
  537.     void findorientation(vec &o, float yaw, float pitch, vec &pos)
  538.     {
  539.         vec dir;
  540.         vecfromyawpitch(yaw, pitch, 1, 0, dir);
  541.         if(raycubepos(o, dir, pos, 0, RAY_CLIPMAT|RAY_SKIPFIRST) == -1)
  542.             pos = dir.mul(2*getworldsize()).add(o); //otherwise 3dgui won't work when outside of map
  543.     }
  544.  
  545.     void setup(fpsent *d, bool tryreset = false)
  546.     {
  547.         d->ai->clearsetup();
  548.         d->ai->reset(tryreset);
  549.         d->ai->lastrun = lastmillis;
  550.         if(m_insta) d->ai->weappref = GUN_RIFLE;
  551.         else
  552.         {
  553.             if(forcegun >= 0 && forcegun < NUMGUNS) d->ai->weappref = forcegun;
  554.             else if(m_noammo) d->ai->weappref = -1;
  555.             else d->ai->weappref = rnd(GUN_GL-GUN_SG+1)+GUN_SG;
  556.         }
  557.         vec dp = d->headpos();
  558.         findorientation(dp, d->yaw, d->pitch, d->ai->target);
  559.     }
  560.  
  561.     void spawned(fpsent *d)
  562.     {
  563.         if(d->ai) setup(d, false);
  564.     }
  565.  
  566.     void killed(fpsent *d, fpsent *e)
  567.     {
  568.         if(d->ai) d->ai->reset();
  569.     }
  570.  
  571.     void itemspawned(int ent)
  572.     {
  573.         if(entities::ents.inrange(ent) && entities::ents[ent]->type >= I_SHELLS && entities::ents[ent]->type <= I_QUAD)
  574.         {
  575.             loopv(players) if(players[i] && players[i]->ai && players[i]->aitype == AI_BOT && players[i]->canpickup(entities::ents[ent]->type))
  576.             {
  577.                 fpsent *d = players[i];
  578.                 bool wantsitem = false;
  579.                 switch(entities::ents[ent]->type)
  580.                 {
  581.                     case I_BOOST: case I_HEALTH: wantsitem = badhealth(d); break;
  582.                     case I_GREENARMOUR: case I_YELLOWARMOUR: case I_QUAD: break;
  583.                     default:
  584.                     {
  585.                         itemstat &is = itemstats[entities::ents[ent]->type-I_SHELLS];
  586.                         wantsitem = isgoodammo(is.info) && d->ammo[is.info] <= (d->ai->weappref == is.info ? is.add : is.add/2);
  587.                         break;
  588.                     }
  589.                 }
  590.                 if(wantsitem)
  591.                 {
  592.                     aistate &b = d->ai->getstate();
  593.                     if(b.type == AI_S_PURSUE && b.targtype == AI_T_AFFINITY) continue;
  594.                     if(b.type == AI_S_INTEREST && b.targtype == AI_T_ENTITY)
  595.                     {
  596.                         if(entities::ents.inrange(b.target))
  597.                         {
  598.                             if(d->o.squaredist(entities::ents[ent]->o) < d->o.squaredist(entities::ents[b.target]->o))
  599.                                 d->ai->switchstate(b, AI_S_INTEREST, AI_T_ENTITY, ent);
  600.                         }
  601.                         continue;
  602.                     }
  603.                     d->ai->switchstate(b, AI_S_INTEREST, AI_T_ENTITY, ent);
  604.                 }
  605.             }
  606.         }
  607.     }
  608.  
  609.     bool check(fpsent *d, aistate &b)
  610.     {
  611.         if(cmode && cmode->aicheck(d, b)) return true;
  612.         return false;
  613.     }
  614.  
  615.     int dowait(fpsent *d, aistate &b)
  616.     {
  617.         if(check(d, b) || find(d, b)) return 1;
  618.         if(target(d, b, true, true)) return 1;
  619.         if(randomnode(d, b, SIGHTMIN, 1e16f))
  620.         {
  621.             d->ai->switchstate(b, AI_S_INTEREST, AI_T_NODE, d->ai->route[0]);
  622.             return 1;
  623.         }
  624.         return 0; // but don't pop the state
  625.     }
  626.  
  627.     int dodefend(fpsent *d, aistate &b)
  628.     {
  629.         if(d->state == CS_ALIVE)
  630.         {
  631.             switch(b.targtype)
  632.             {
  633.                 case AI_T_NODE:
  634.                     if(check(d, b)) return 1;
  635.                     if(waypoints.inrange(b.target)) return defend(d, b, waypoints[b.target].o) ? 1 : 0;
  636.                     break;
  637.                 case AI_T_ENTITY:
  638.                     if(check(d, b)) return 1;
  639.                     if(entities::ents.inrange(b.target)) return defend(d, b, entities::ents[b.target]->o) ? 1 : 0;
  640.                     break;
  641.                 case AI_T_AFFINITY:
  642.                     if(cmode) return cmode->aidefend(d, b) ? 1 : 0;
  643.                     break;
  644.                 case AI_T_PLAYER:
  645.                 {
  646.                     if(check(d, b)) return 1;
  647.                     fpsent *e = getclient(b.target);
  648.                     if(e && e->state == CS_ALIVE) return defend(d, b, e->feetpos()) ? 1 : 0;
  649.                     break;
  650.                 }
  651.                 default: break;
  652.             }
  653.         }
  654.         return 0;
  655.     }
  656.  
  657.     int dointerest(fpsent *d, aistate &b)
  658.     {
  659.         if(d->state != CS_ALIVE) return 0;
  660.         switch(b.targtype)
  661.         {
  662.             case AI_T_NODE:
  663.                 if(check(d, b)) return 1;
  664.                 if(d->lastnode != b.target && waypoints.inrange(b.target))
  665.                     return makeroute(d, b, b.target) ? 1 : 0;
  666.                 break;
  667.             case AI_T_ENTITY:
  668.                 if(entities::ents.inrange(b.target))
  669.                 {
  670.                     extentity &e = *(extentity *)entities::ents[b.target];
  671.                     if(!e.spawned || e.type < I_SHELLS || e.type > I_CARTRIDGES || d->hasmaxammo(e.type)) return 0;
  672.                     if(d->feetpos().squaredist(e.o) <= CLOSEDIST*CLOSEDIST)
  673.                     {
  674.                         b.idle = 1;
  675.                         return true;
  676.                     }
  677.                     return makeroute(d, b, e.o) ? 1 : 0;
  678.                 }
  679.                 break;
  680.         }
  681.         return 0;
  682.     }
  683.  
  684.     int dopursue(fpsent *d, aistate &b)
  685.     {
  686.         if(d->state == CS_ALIVE)
  687.         {
  688.             switch(b.targtype)
  689.             {
  690.                 case AI_T_AFFINITY:
  691.                 {
  692.                     if(cmode) return cmode->aipursue(d, b) ? 1 : 0;
  693.                     break;
  694.                 }
  695.  
  696.                 case AI_T_PLAYER:
  697.                 {
  698.                     //if(check(d, b)) return 1;
  699.                     fpsent *e = getclient(b.target);
  700.                     if(e && e->state == CS_ALIVE)
  701.                     {
  702.                         float guard = SIGHTMIN, wander = guns[d->gunselect].range;
  703.                         if(d->gunselect == GUN_FIST) guard = 0.f;
  704.                         return patrol(d, b, e->feetpos(), guard, wander) ? 1 : 0;
  705.                     }
  706.                     break;
  707.                 }
  708.                 default: break;
  709.             }
  710.         }
  711.         return 0;
  712.     }
  713.  
  714.     int closenode(fpsent *d, bool retry = false)
  715.     {
  716.         vec pos = d->feetpos();
  717.         int node = -1;
  718.         float mindist = SIGHTMIN*SIGHTMIN;
  719.         loopv(d->ai->route) if(waypoints.inrange(d->ai->route[i]))
  720.         {
  721.             waypoint &w = waypoints[d->ai->route[i]];
  722.             vec wpos = w.o;
  723.             int id = obstacles.remap(d, d->ai->route[i], wpos, retry);
  724.             if(waypoints.inrange(id) && (retry || id == d->ai->route[i] || !d->ai->hasprevnode(id)))
  725.             {
  726.                 float dist = wpos.squaredist(pos);
  727.                 if(dist < mindist)
  728.                 {
  729.                     node = id;
  730.                     mindist = dist;
  731.                 }
  732.             }
  733.         }
  734.         return node;
  735.     }
  736.  
  737.     bool wpspot(fpsent *d, int n, bool retry = false)
  738.     {
  739.         if(waypoints.inrange(n))
  740.         {
  741.             waypoint &w = waypoints[n];
  742.             vec wpos = w.o;
  743.             int id = obstacles.remap(d, n, wpos, retry);
  744.             if(waypoints.inrange(id) && (retry || id == n || !d->ai->hasprevnode(id)))
  745.             {
  746.                 d->ai->spot = wpos;
  747.                 d->ai->targnode = n;
  748.                 return true;
  749.             }
  750.         }
  751.         return false;
  752.     }
  753.  
  754.     bool anynode(fpsent *d, aistate &b, bool retry = false)
  755.     {
  756.         if(!waypoints.inrange(d->lastnode)) return false;
  757.         waypoint &w = waypoints[d->lastnode];
  758.         static vector<int> anyremap; anyremap.setsize(0);
  759.         if(w.links[0])
  760.         {
  761.             loopi(MAXWAYPOINTLINKS)
  762.             {
  763.                 int link = w.links[i];
  764.                 if(!link) break;
  765.                 if(waypoints.inrange(link) && (retry || !d->ai->hasprevnode(link)))
  766.                     anyremap.add(link);
  767.             }
  768.         }
  769.         while(!anyremap.empty())
  770.         {
  771.             int r = rnd(anyremap.length()), t = anyremap[r];
  772.             if(wpspot(d, t, retry))
  773.             {
  774.                 d->ai->route.add(t);
  775.                 d->ai->route.add(d->lastnode);
  776.                 return true;
  777.             }
  778.             anyremap.remove(r);
  779.         }
  780.         if(!retry) return anynode(d, b, true);
  781.         return false;
  782.     }
  783.  
  784.     bool hunt(fpsent *d, aistate &b, int retries = 0)
  785.     {
  786.         if(!d->ai->route.empty() && waypoints.inrange(d->lastnode))
  787.         {
  788.             int n = retries%2 ? d->ai->route.find(d->lastnode) : closenode(d, retries >= 2);
  789.             if(retries%2 && d->ai->route.inrange(n))
  790.             {
  791.                 while(d->ai->route.length() > n+1) d->ai->route.pop(); // waka-waka-waka-waka
  792.                 if(!n)
  793.                 {
  794.                     if(wpspot(d, d->ai->route[n], retries >= 2))
  795.                     {
  796.                         d->ai->clear(false);
  797.                         return true;
  798.                     }
  799.                     else if(retries <= 2) return hunt(d, b, retries+1); // try again
  800.                 }
  801.                 else n--; // otherwise, we want the next in line
  802.             }
  803.             if(d->ai->route.inrange(n) && wpspot(d, d->ai->route[n], retries >= 2)) return true;
  804.             if(retries <= 2) return hunt(d, b, retries+1); // try again
  805.         }
  806.         b.override = false;
  807.         d->ai->clear(false);
  808.         return anynode(d, b);
  809.     }
  810.  
  811.     void jumpto(fpsent *d, aistate &b, const vec &pos)
  812.     {
  813.         vec off = vec(pos).sub(d->feetpos()), dir(off.x, off.y, 0);
  814.         bool offground = d->timeinair && !d->inwater, jumper = off.z >= JUMPMIN,
  815.             jump = !offground && (jumper || lastmillis >= d->ai->jumprand) && lastmillis >= d->ai->jumpseed;
  816.         if(jump)
  817.         {
  818.             vec old = d->o;
  819.             d->o = vec(pos).add(vec(0, 0, d->eyeheight));
  820.             if(!collide(d, vec(0, 0, 1))) jump = false;
  821.             d->o = old;
  822.             if(jump)
  823.             {
  824.                 float radius = 18*18;
  825.                 loopv(entities::ents) if(entities::ents[i]->type == JUMPPAD)
  826.                 {
  827.                     fpsentity &e = *(fpsentity *)entities::ents[i];
  828.                     if(e.o.squaredist(pos) <= radius) { jump = false; break; }
  829.                 }
  830.             }
  831.         }
  832.         if(jump)
  833.         {
  834.             d->jumping = true;
  835.             int seed = (111-d->skill)*(d->inwater ? 2 : 10);
  836.             d->ai->jumpseed = lastmillis+seed+rnd(seed);
  837.             seed *= b.idle ? 50 : 25;
  838.             d->ai->jumprand = lastmillis+seed+rnd(seed);
  839.         }
  840.     }
  841.  
  842.     void fixfullrange(float &yaw, float &pitch, float &roll, bool full)
  843.     {
  844.         if(full)
  845.         {
  846.             while(pitch < -180.0f) pitch += 360.0f;
  847.             while(pitch >= 180.0f) pitch -= 360.0f;
  848.             while(roll < -180.0f) roll += 360.0f;
  849.             while(roll >= 180.0f) roll -= 360.0f;
  850.         }
  851.         else
  852.         {
  853.             if(pitch > 89.9f) pitch = 89.9f;
  854.             if(pitch < -89.9f) pitch = -89.9f;
  855.             if(roll > 89.9f) roll = 89.9f;
  856.             if(roll < -89.9f) roll = -89.9f;
  857.         }
  858.         while(yaw < 0.0f) yaw += 360.0f;
  859.         while(yaw >= 360.0f) yaw -= 360.0f;
  860.     }
  861.  
  862.     void fixrange(float &yaw, float &pitch)
  863.     {
  864.         float r = 0.f;
  865.         fixfullrange(yaw, pitch, r, false);
  866.     }
  867.  
  868.     void getyawpitch(const vec &from, const vec &pos, float &yaw, float &pitch)
  869.     {
  870.         float dist = from.dist(pos);
  871.         yaw = -atan2(pos.x-from.x, pos.y-from.y)/RAD;
  872.         pitch = asin((pos.z-from.z)/dist)/RAD;
  873.     }
  874.  
  875.     void scaleyawpitch(float &yaw, float &pitch, float targyaw, float targpitch, float frame, float scale)
  876.     {
  877.         if(yaw < targyaw-180.0f) yaw += 360.0f;
  878.         if(yaw > targyaw+180.0f) yaw -= 360.0f;
  879.         float offyaw = fabs(targyaw-yaw)*frame, offpitch = fabs(targpitch-pitch)*frame*scale;
  880.         if(targyaw > yaw)
  881.         {
  882.             yaw += offyaw;
  883.             if(targyaw < yaw) yaw = targyaw;
  884.         }
  885.         else if(targyaw < yaw)
  886.         {
  887.             yaw -= offyaw;
  888.             if(targyaw > yaw) yaw = targyaw;
  889.         }
  890.         if(targpitch > pitch)
  891.         {
  892.             pitch += offpitch;
  893.             if(targpitch < pitch) pitch = targpitch;
  894.         }
  895.         else if(targpitch < pitch)
  896.         {
  897.             pitch -= offpitch;
  898.             if(targpitch > pitch) pitch = targpitch;
  899.         }
  900.         fixrange(yaw, pitch);
  901.     }
  902.  
  903.     bool lockon(fpsent *d, fpsent *e, float maxdist)
  904.     {
  905.         if(d->gunselect == GUN_FIST && !d->blocked && !d->timeinair)
  906.         {
  907.             vec dir = vec(e->o).sub(d->o);
  908.             float xydist = dir.x*dir.x+dir.y*dir.y, zdist = dir.z*dir.z, mdist = maxdist*maxdist, ddist = d->radius*d->radius+e->radius*e->radius;
  909.             if(zdist <= ddist && xydist >= ddist+4 && xydist <= mdist+ddist) return true;
  910.         }
  911.         return false;
  912.     }
  913.  
  914.     int process(fpsent *d, aistate &b)
  915.     {
  916.         int result = 0, stupify = d->skill <= 10+rnd(15) ? rnd(d->skill*1000) : 0, skmod = 101-d->skill;
  917.         float frame = d->skill <= 100 ? float(lastmillis-d->ai->lastrun)/float(max(skmod,1)*10) : 1;
  918.         vec dp = d->headpos();
  919.  
  920.         bool idle = b.idle == 1 || (stupify && stupify <= skmod);
  921.         d->ai->dontmove = false;
  922.         if(idle)
  923.         {
  924.             d->ai->lastaction = d->ai->lasthunt = lastmillis;
  925.             d->ai->dontmove = true;
  926.         }
  927.         else if(hunt(d, b))
  928.         {
  929.             getyawpitch(dp, vec(d->ai->spot).add(vec(0, 0, d->eyeheight)), d->ai->targyaw, d->ai->targpitch);
  930.             d->ai->lasthunt = lastmillis;
  931.         }
  932.         else idle = d->ai->dontmove = true;
  933.  
  934.         if(!d->ai->dontmove) jumpto(d, b, d->ai->spot);
  935.  
  936.         fpsent *e = getclient(d->ai->enemy);
  937.         bool enemyok = e && targetable(d, e);
  938.         if(!enemyok || d->skill >= 50)
  939.         {
  940.             fpsent *f = (fpsent *)intersectclosest(dp, d->ai->target, d);
  941.             if(f)
  942.             {
  943.                 if(targetable(d, f))
  944.                 {
  945.                     if(!enemyok) violence(d, b, f, d->gunselect == GUN_FIST);
  946.                     enemyok = true;
  947.                     e = f;
  948.                 }
  949.                 else enemyok = false;
  950.             }
  951.             else if(!enemyok && target(d, b, d->gunselect == GUN_FIST, false, SIGHTMIN))
  952.                 enemyok = (e = getclient(d->ai->enemy)) != NULL;
  953.         }
  954.         if(enemyok)
  955.         {
  956.             vec ep = getaimpos(d, e);
  957.             float yaw, pitch;
  958.             getyawpitch(dp, ep, yaw, pitch);
  959.             fixrange(yaw, pitch);
  960.             bool insight = cansee(d, dp, ep), hasseen = d->ai->enemyseen && lastmillis-d->ai->enemyseen <= (d->skill*10)+3000,
  961.                 quick = d->ai->enemyseen && lastmillis-d->ai->enemyseen <= (d->gunselect == GUN_CG ? 300 : skmod)+30;
  962.             if(insight) d->ai->enemyseen = lastmillis;
  963.             if(idle || insight || hasseen || quick)
  964.             {
  965.                 float sskew = insight || d->skill > 100 ? 1.5f : (hasseen ? 1.f : 0.5f);
  966.                 if(insight && lockon(d, e, 16))
  967.                 {
  968.                     d->ai->targyaw = yaw;
  969.                     d->ai->targpitch = pitch;
  970.                     if(!idle) frame *= 2;
  971.                     d->ai->becareful = false;
  972.                 }
  973.                 scaleyawpitch(d->yaw, d->pitch, yaw, pitch, frame, sskew);
  974.                 if(insight || quick)
  975.                 {
  976.                     if(canshoot(d, e) && hastarget(d, b, e, yaw, pitch, dp.squaredist(ep)))
  977.                     {
  978.                         d->attacking = true;
  979.                         d->ai->lastaction = lastmillis;
  980.                         result = 3;
  981.                     }
  982.                     else result = 2;
  983.                 }
  984.                 else result = 1;
  985.             }
  986.             else
  987.             {
  988.                 if(!d->ai->enemyseen || lastmillis-d->ai->enemyseen > (d->skill*50)+3000)
  989.                 {
  990.                     d->ai->enemy = -1;
  991.                     d->ai->enemyseen = d->ai->enemymillis = 0;
  992.                 }
  993.                 enemyok = false;
  994.                 result = 0;
  995.             }
  996.         }
  997.         else
  998.         {
  999.             if(!enemyok)
  1000.             {
  1001.                 d->ai->enemy = -1;
  1002.                 d->ai->enemyseen = d->ai->enemymillis = 0;
  1003.             }
  1004.             enemyok = false;
  1005.             result = 0;
  1006.         }
  1007.  
  1008.         fixrange(d->ai->targyaw, d->ai->targpitch);
  1009.         if(!result) scaleyawpitch(d->yaw, d->pitch, d->ai->targyaw, d->ai->targpitch, frame*0.25f, 1.f);
  1010.  
  1011.         if(d->ai->becareful && d->physstate == PHYS_FALL)
  1012.         {
  1013.             float offyaw, offpitch;
  1014.             vec v = vec(d->vel).normalize();
  1015.             vectoyawpitch(v, offyaw, offpitch);
  1016.             offyaw -= d->yaw; offpitch -= d->pitch;
  1017.             if(fabs(offyaw)+fabs(offpitch) >= 135) d->ai->becareful = false;
  1018.             else if(d->ai->becareful) d->ai->dontmove = true;
  1019.         }
  1020.         else d->ai->becareful = false;
  1021.  
  1022.         if(d->ai->dontmove) d->move = d->strafe = 0;
  1023.         else
  1024.         { // our guys move one way.. but turn another?! :)
  1025.             const struct aimdir { int move, strafe, offset; } aimdirs[8] =
  1026.             {
  1027.                 {  1,  0,   0 },
  1028.                 {  1,  -1,  45 },
  1029.                 {  0,  -1,  90 },
  1030.                 { -1,  -1, 135 },
  1031.                 { -1,  0, 180 },
  1032.                 { -1, 1, 225 },
  1033.                 {  0, 1, 270 },
  1034.                 {  1, 1, 315 }
  1035.             };
  1036.             float yaw = d->ai->targyaw-d->yaw;
  1037.             while(yaw < 0.0f) yaw += 360.0f;
  1038.             while(yaw >= 360.0f) yaw -= 360.0f;
  1039.             int r = clamp(((int)floor((yaw+22.5f)/45.0f))&7, 0, 7);
  1040.             const aimdir &ad = aimdirs[r];
  1041.             d->move = ad.move;
  1042.             d->strafe = ad.strafe;
  1043.         }
  1044.         findorientation(dp, d->yaw, d->pitch, d->ai->target);
  1045.         return result;
  1046.     }
  1047.  
  1048.     bool hasrange(fpsent *d, fpsent *e, int weap)
  1049.     {
  1050.         if(!e) return true;
  1051.         if(targetable(d, e))
  1052.         {
  1053.             vec ep = getaimpos(d, e);
  1054.             float dist = ep.squaredist(d->headpos());
  1055.             if(weaprange(d, weap, dist)) return true;
  1056.         }
  1057.         return false;
  1058.     }
  1059.  
  1060.     bool request(fpsent *d, aistate &b)
  1061.     {
  1062.         fpsent *e = getclient(d->ai->enemy);
  1063.         if(!d->hasammo(d->gunselect) || !hasrange(d, e, d->gunselect) || (d->gunselect != d->ai->weappref && (!isgoodammo(d->gunselect) || d->hasammo(d->ai->weappref))))
  1064.         {
  1065.             static const int gunprefs[] = { GUN_CG, GUN_RL, GUN_SG, GUN_RIFLE, GUN_GL, GUN_PISTOL, GUN_FIST };
  1066.             int gun = -1;
  1067.             if(d->hasammo(d->ai->weappref) && hasrange(d, e, d->ai->weappref)) gun = d->ai->weappref;
  1068.             else
  1069.             {
  1070.                 loopi(sizeof(gunprefs)/sizeof(gunprefs[0])) if(d->hasammo(gunprefs[i]) && hasrange(d, e, gunprefs[i]))
  1071.                 {
  1072.                     gun = gunprefs[i];
  1073.                     break;
  1074.                 }
  1075.             }
  1076.             if(gun >= 0 && gun != d->gunselect) gunselect(gun, d);
  1077.         }
  1078.         return process(d, b) >= 2;
  1079.     }
  1080.  
  1081.     void timeouts(fpsent *d, aistate &b)
  1082.     {
  1083.         if(d->blocked)
  1084.         {
  1085.             d->ai->blocktime += lastmillis-d->ai->lastrun;
  1086.             if(d->ai->blocktime > (d->ai->blockseq+1)*500)
  1087.             {
  1088.                 switch(d->ai->blockseq)
  1089.                 {
  1090.                     case 1: case 2: case 3:
  1091.                         if(entities::ents.inrange(d->ai->targnode)) d->ai->addprevnode(d->ai->targnode);
  1092.                         d->ai->clear(false);
  1093.                         break;
  1094.                     case 4: d->ai->reset(true); break;
  1095.                     case 5: d->ai->reset(false); break;
  1096.                     case 6: suicide(d); return; break; // this is our last resort..
  1097.                     case 0: default: break;
  1098.                 }
  1099.                 d->ai->blockseq++;
  1100.             }
  1101.         }
  1102.         else d->ai->blocktime = d->ai->blockseq = 0;
  1103.  
  1104.         if(d->ai->targnode == d->ai->targlast)
  1105.         {
  1106.             d->ai->targtime += lastmillis-d->ai->lastrun;
  1107.             if(d->ai->targtime > (d->ai->targseq+1)*1000)
  1108.             {
  1109.                 switch(d->ai->targseq)
  1110.                 {
  1111.                     case 1: case 2: case 3:
  1112.                         if(entities::ents.inrange(d->ai->targnode)) d->ai->addprevnode(d->ai->targnode);
  1113.                         d->ai->clear(false);
  1114.                         break;
  1115.                     case 4: d->ai->reset(true); break;
  1116.                     case 5: d->ai->reset(false); break;
  1117.                     case 6: suicide(d); return; break; // this is our last resort..
  1118.                     case 0: default: break;
  1119.                 }
  1120.                 d->ai->targseq++;
  1121.             }
  1122.         }
  1123.         else
  1124.         {
  1125.             d->ai->targtime = d->ai->targseq = 0;
  1126.             d->ai->targlast = d->ai->targnode;
  1127.         }
  1128.  
  1129.         if(d->ai->lasthunt)
  1130.         {
  1131.             int millis = lastmillis-d->ai->lasthunt;
  1132.             if(millis <= 3000) { d->ai->tryreset = false; d->ai->huntseq = 0; }
  1133.             else if(millis > (d->ai->huntseq+1)*3000)
  1134.             {
  1135.                 switch(d->ai->huntseq)
  1136.                 {
  1137.                     case 0: d->ai->reset(true); break;
  1138.                     case 1: d->ai->reset(false); break;
  1139.                     case 2: suicide(d); return; break; // this is our last resort..
  1140.                     default: break;
  1141.                 }
  1142.                 d->ai->huntseq++;
  1143.             }
  1144.         }
  1145.     }
  1146.  
  1147.     void logic(fpsent *d, aistate &b, bool run)
  1148.     {
  1149.         vec dp = d->headpos();
  1150.         bool allowmove = canmove(d) && b.type != AI_S_WAIT;
  1151.         if(d->state != CS_ALIVE || !allowmove) d->stopmoving();
  1152.         if(d->state == CS_ALIVE)
  1153.         {
  1154.             if(allowmove)
  1155.             {
  1156.                 if(!request(d, b)) target(d, b, d->gunselect == GUN_FIST, b.idle ? true : false);
  1157.                 shoot(d, d->ai->target);
  1158.             }
  1159.             if(!intermission)
  1160.             {
  1161.                 if(d->ragdoll) cleanragdoll(d);
  1162.                 moveplayer(d, 10, true);
  1163.                 if(allowmove && !b.idle) timeouts(d, b);
  1164.                 entities::checkitems(d);
  1165.                 if(cmode) cmode->checkitems(d);
  1166.             }
  1167.         }
  1168.         else if(d->state == CS_DEAD)
  1169.         {
  1170.             if(d->ragdoll) moveragdoll(d);
  1171.             else if(lastmillis-d->lastpain<2000)
  1172.             {
  1173.                 d->move = d->strafe = 0;
  1174.                 moveplayer(d, 10, false);
  1175.             }
  1176.         }
  1177.         d->attacking = d->jumping = false;
  1178.     }
  1179.  
  1180.     void avoid()
  1181.     {
  1182.         // guess as to the radius of ai and other critters relying on the avoid set for now
  1183.         float guessradius = player1->radius;
  1184.         obstacles.clear();
  1185.         loopv(players)
  1186.         {
  1187.             dynent *d = players[i];
  1188.             if(d->state != CS_ALIVE) continue;
  1189.             obstacles.avoidnear(d, d->o.z + d->aboveeye + 1, d->feetpos(), guessradius + d->radius);
  1190.         }
  1191.         extern avoidset wpavoid;
  1192.         obstacles.add(wpavoid);
  1193.         avoidweapons(obstacles, guessradius);
  1194.     }
  1195.  
  1196.     void think(fpsent *d, bool run)
  1197.     {
  1198.         // the state stack works like a chain of commands, certain commands simply replace each other
  1199.         // others spawn new commands to the stack the ai reads the top command from the stack and executes
  1200.         // it or pops the stack and goes back along the history until it finds a suitable command to execute
  1201.         if(d->ai->state.empty()) setup(d, false);
  1202.         bool cleannext = false, parse = run && waypoints.inrange(d->lastnode);
  1203.         loopvrev(d->ai->state)
  1204.         {
  1205.             aistate &c = d->ai->state[i];
  1206.             if(cleannext)
  1207.             {
  1208.                 c.millis = lastmillis;
  1209.                 c.override = false;
  1210.                 cleannext = false;
  1211.             }
  1212.             if(d->state == CS_DEAD && d->respawned!=d->lifesequence && (!cmode || cmode->respawnwait(d) <= 0) && lastmillis - d->lastpain >= 500)
  1213.             {
  1214.                 addmsg(N_TRYSPAWN, "rc", d);
  1215.                 d->respawned = d->lifesequence;
  1216.             }
  1217.             else if(d->state == CS_ALIVE && parse)
  1218.             {
  1219.                 int result = 0;
  1220.                 c.idle = 0;
  1221.                 switch(c.type)
  1222.                 {
  1223.                     case AI_S_WAIT: result = dowait(d, c); break;
  1224.                     case AI_S_DEFEND: result = dodefend(d, c); break;
  1225.                     case AI_S_PURSUE: result = dopursue(d, c); break;
  1226.                     case AI_S_INTEREST: result = dointerest(d, c); break;
  1227.                     default: result = 0; break;
  1228.                 }
  1229.                 if(result <= 0)
  1230.                 {
  1231.                     d->ai->clear(true);
  1232.                     if(c.type != AI_S_WAIT)
  1233.                     {
  1234.                         switch(result)
  1235.                         {
  1236.                             case 0: default: d->ai->removestate(i); cleannext = true; break;
  1237.                             case -1: i = d->ai->state.length()-1; break;
  1238.                         }
  1239.                         continue; // shouldn't interfere
  1240.                     }
  1241.                 }
  1242.             }
  1243.             logic(d, c, parse);
  1244.             break;
  1245.         }
  1246.         if(d->ai->trywipe) d->ai->wipe();
  1247.         d->ai->lastrun = lastmillis;
  1248.     }
  1249.  
  1250.     void drawstate(fpsent *d, aistate &b, bool top, int above)
  1251.     {
  1252.         const char *bnames[AI_S_MAX] = {
  1253.             "wait", "defend", "pursue", "interest"
  1254.         }, *btypes[AI_T_MAX+1] = {
  1255.             "none", "node", "player", "affinity", "entity"
  1256.         };
  1257.         string s;
  1258.         if(top)
  1259.         {
  1260.             formatstring(s)("\f0%s (%d) %s:%d (%d[%d])",
  1261.                 bnames[b.type],
  1262.                 lastmillis-b.millis,
  1263.                 btypes[clamp(b.targtype+1, 0, AI_T_MAX+1)], b.target,
  1264.                 !d->ai->route.empty() ? d->ai->route[0] : -1,
  1265.                 d->ai->route.length()
  1266.             );
  1267.         }
  1268.         else
  1269.         {
  1270.             formatstring(s)("\f2%s (%d) %s:%d",
  1271.                 bnames[b.type],
  1272.                 lastmillis-b.millis,
  1273.                 btypes[clamp(b.targtype+1, 0, AI_T_MAX+1)], b.target
  1274.             );
  1275.         }
  1276.         particle_textcopy(vec(d->abovehead()).add(vec(0, 0, above)), s, PART_TEXT, 1);
  1277.         if(b.targtype == AI_T_ENTITY && entities::ents.inrange(b.target))
  1278.         {
  1279.             formatstring(s)("GOAL: %s", colorname(d));
  1280.             particle_textcopy(entities::ents[b.target]->o, s, PART_TEXT, 1);
  1281.         }
  1282.     }
  1283.  
  1284.     void drawroute(fpsent *d, aistate &b, float amt = 1.f)
  1285.     {
  1286.         int colour = 0xFFFFFF, last = -1;
  1287.  
  1288.         loopvrev(d->ai->route)
  1289.         {
  1290.             if(d->ai->route.inrange(last))
  1291.             {
  1292.                 int index = d->ai->route[i], prev = d->ai->route[last];
  1293.                 if(waypoints.inrange(index) && waypoints.inrange(prev))
  1294.                 {
  1295.                     waypoint &e = waypoints[index],
  1296.                         &f = waypoints[prev];
  1297.                     vec fr(vec(f.o).add(vec(0, 0, 4.f*amt))),
  1298.                         dr(vec(e.o).add(vec(0, 0, 4.f*amt)));
  1299.                     particle_flare(fr, dr, 1, PART_STREAK, colour);
  1300.                 }
  1301.             }
  1302.             last = i;
  1303.         }
  1304.         if(aidebug > 4)
  1305.         {
  1306.             vec pos = d->feetpos();
  1307.             if(d->ai->spot != vec(0, 0, 0)) particle_flare(pos, d->ai->spot, 1, PART_LIGHTNING, 0xFFFFFF);
  1308.             if(waypoints.inrange(d->lastnode))
  1309.                 particle_flare(pos, waypoints[d->lastnode].o, 1, PART_LIGHTNING, 0x00FFFF);
  1310.             if(waypoints.inrange(d->ai->prevnodes[1]))
  1311.                 particle_flare(pos, waypoints[d->ai->prevnodes[1]].o, 1, PART_LIGHTNING, 0xFF00FF);
  1312.         }
  1313.     }
  1314.  
  1315.     VAR(showwaypoints, 0, 0, 1);
  1316.     VAR(showwaypointsradius, 0, 200, 10000);
  1317.  
  1318.     void render()
  1319.     {
  1320.         if(aidebug > 1)
  1321.         {
  1322.             int total = 0, alive = 0;
  1323.             loopv(players) if(players[i]->ai) total++;
  1324.             loopv(players) if(players[i]->state == CS_ALIVE && players[i]->ai)
  1325.             {
  1326.                 fpsent *d = players[i];
  1327.                 bool top = true;
  1328.                 int above = 0;
  1329.                 alive++;
  1330.                 loopvrev(d->ai->state)
  1331.                 {
  1332.                     aistate &b = d->ai->state[i];
  1333.                     drawstate(d, b, top, above += 2);
  1334.                     if(aidebug > 3 && top && b.type != AI_S_WAIT)
  1335.                         drawroute(d, b, float(alive)/float(total));
  1336.                     if(top)
  1337.                     {
  1338.                         if(aidebug > 2) top = false;
  1339.                         else break;
  1340.                     }
  1341.                 }
  1342.                 if(aidebug > 2)
  1343.                 {
  1344.                     if(d->ai->weappref >= 0 && d->ai->weappref < NUMGUNS)
  1345.                         particle_textcopy(vec(d->abovehead()).add(vec(0, 0, above += 2)), guns[d->ai->weappref].name, PART_TEXT, 1);
  1346.                     fpsent *e = getclient(d->ai->enemy);
  1347.                     if(e) particle_textcopy(vec(d->abovehead()).add(vec(0, 0, above += 2)), colorname(e), PART_TEXT, 1);
  1348.                 }
  1349.             }
  1350.         }
  1351.         if(showwaypoints || aidebug > 5)
  1352.         {
  1353.             vector<int> close;
  1354.             int len = waypoints.length();
  1355.             if(showwaypointsradius)
  1356.             {
  1357.                 findwaypointswithin(camera1->o, 0, showwaypointsradius, close);
  1358.                 len = close.length();
  1359.             }
  1360.             loopi(len)
  1361.             {
  1362.                 waypoint &w = waypoints[showwaypointsradius ? close[i] : i];
  1363.                 loopj(MAXWAYPOINTLINKS)
  1364.                 {
  1365.                      int link = w.links[j];
  1366.                      if(!link) break;
  1367.                      particle_flare(w.o, waypoints[link].o, 1, PART_STREAK, 0x0000FF);
  1368.                 }
  1369.             }
  1370.  
  1371.         }
  1372.     }
  1373. }
  1374.  
  1375. /*
  1376.  
  1377. KI.h
  1378.  
  1379. by Kaan Holat
  1380.  
  1381. */
  1382.  
  1383.  
  1384. struct fpsent;
  1385.  
  1386. #define MAXBOTS 32
  1387.  
  1388. enum { AI_NONE = 0, AI_BOT, AI_MAX };
  1389. #define isaitype(a) (a >= 0 && a <= AI_MAX-1)
  1390.  
  1391. namespace ai
  1392. {
  1393.     const int MAXWAYPOINTS = USHRT_MAX - 2;
  1394.     const int MAXWAYPOINTLINKS = 6;
  1395.     const int WAYPOINTRADIUS = 16;
  1396.  
  1397.     const float CLOSEDIST       = 16.f;    // is close
  1398.     const float JUMPMIN         = 4.f;     // decides to jump
  1399.     const float JUMPMAX         = 32.f;    // max jump
  1400.     const float SIGHTMIN        = 64.f;    // minimum line of sight
  1401.     const float SIGHTMAX        = 1024.f;  // maximum line of sight
  1402.     const float VIEWMIN         = 90.f;    // minimum field of view
  1403.     const float VIEWMAX         = 180.f;   // maximum field of view
  1404.  
  1405.     struct waypoint
  1406.     {
  1407.         vec o;
  1408.         float curscore, estscore;
  1409.         int weight;
  1410.         ushort route, prev;
  1411.         ushort links[MAXWAYPOINTLINKS];
  1412.  
  1413.         waypoint() {}
  1414.         waypoint(const vec &o, int weight = 0) : o(o), weight(weight), route(0) { memset(links, 0, sizeof(links)); }
  1415.  
  1416.         int score() const { return int(curscore) + int(estscore); }
  1417.  
  1418.         int find(int wp)
  1419.         {
  1420.             loopi(MAXWAYPOINTLINKS) if(links[i] == wp) return i;
  1421.             return -1;
  1422.         }
  1423.     };
  1424.     extern vector<waypoint> waypoints;
  1425.  
  1426.     extern int closestwaypoint(const vec &pos, float mindist, bool links, fpsent *d = NULL);
  1427.     extern void findwaypointswithin(const vec &pos, float mindist, float maxdist, vector<int> &results);
  1428.     extern void inferwaypoints(fpsent *d, const vec &o, const vec &v, float mindist = ai::CLOSEDIST);
  1429.  
  1430.     struct avoidset
  1431.     {
  1432.         struct obstacle
  1433.         {
  1434.             void *owner;
  1435.             int numwaypoints;
  1436.             float above;
  1437.  
  1438.             obstacle(void *owner, float above = -1) : owner(owner), numwaypoints(0), above(above) {}
  1439.         };
  1440.  
  1441.         vector<obstacle> obstacles;
  1442.         vector<int> waypoints;
  1443.  
  1444.         void clear()
  1445.         {
  1446.             obstacles.setsize(0);
  1447.             waypoints.setsize(0);
  1448.         }
  1449.  
  1450.         void add(void *owner, float above)
  1451.         {
  1452.             obstacles.add(obstacle(owner, above));
  1453.         }
  1454.  
  1455.         void add(void *owner, float above, int wp)
  1456.         {
  1457.             if(obstacles.empty() || owner != &obstacles.last().owner) add(owner, above);
  1458.             obstacles.last().numwaypoints++;
  1459.             waypoints.add(wp);
  1460.         }
  1461.  
  1462.         void add(avoidset &avoid)
  1463.         {
  1464.             waypoints.put(avoid.waypoints.getbuf(), avoid.waypoints.length());
  1465.             loopv(avoid.obstacles)
  1466.             {
  1467.                 obstacle &o = avoid.obstacles[i];
  1468.                 if(obstacles.empty() || o.owner != &obstacles.last().owner) add(o.owner, o.above);
  1469.                 obstacles.last().numwaypoints += o.numwaypoints;
  1470.             }
  1471.         }
  1472.  
  1473.         void avoidnear(void *owner, float above, const vec &pos, float limit);
  1474.  
  1475.         #define loopavoid(v, d, body) \
  1476.             if(!(v).obstacles.empty()) \
  1477.             { \
  1478.                 int cur = 0; \
  1479.                 loopv((v).obstacles) \
  1480.                 { \
  1481.                     const ai::avoidset::obstacle &ob = (v).obstacles[i]; \
  1482.                     int next = cur + ob.numwaypoints; \
  1483.                     if(ob.owner != d) \
  1484.                     { \
  1485.                         for(; cur < next; cur++) \
  1486.                         { \
  1487.                             int wp = (v).waypoints[cur]; \
  1488.                             body; \
  1489.                         } \
  1490.                     } \
  1491.                     cur = next; \
  1492.                 } \
  1493.             }
  1494.  
  1495.         bool find(int n, fpsent *d) const
  1496.         {
  1497.             loopavoid(*this, d, { if(wp == n) return true; });
  1498.             return false;
  1499.         }
  1500.  
  1501.         int remap(fpsent *d, int n, vec &pos, bool retry = false);
  1502.     };
  1503.  
  1504.     extern bool route(fpsent *d, int node, int goal, vector<int> &route, const avoidset &obstacles, bool retry = false);
  1505.     extern void navigate();
  1506.     extern void clearwaypoints(bool full = false);
  1507.     extern void seedwaypoints();
  1508.     extern void loadwaypoints(bool force = false, const char *mname = NULL);
  1509.     extern void savewaypoints(bool force = false, const char *mname = NULL);
  1510.  
  1511.     // ai state information for the owner client
  1512.     enum
  1513.     {
  1514.         AI_S_WAIT = 0,      // waiting for next command
  1515.         AI_S_DEFEND,        // defend goal target
  1516.         AI_S_PURSUE,        // pursue goal target
  1517.         AI_S_INTEREST,      // interest in goal entity
  1518.         AI_S_MAX
  1519.     };
  1520.  
  1521.     enum
  1522.     {
  1523.         AI_T_NODE,
  1524.         AI_T_PLAYER,
  1525.         AI_T_AFFINITY,
  1526.         AI_T_ENTITY,
  1527.         AI_T_MAX
  1528.     };
  1529.  
  1530.     struct interest
  1531.     {
  1532.         int state, node, target, targtype;
  1533.         float score;
  1534.         interest() : state(-1), node(-1), target(-1), targtype(-1), score(0.f) {}
  1535.         ~interest() {}
  1536.     };
  1537.  
  1538.     struct aistate
  1539.     {
  1540.         int type, millis, targtype, target, idle;
  1541.         bool override;
  1542.  
  1543.         aistate(int m, int t, int r = -1, int v = -1) : type(t), millis(m), targtype(r), target(v)
  1544.         {
  1545.             reset();
  1546.         }
  1547.         ~aistate() {}
  1548.  
  1549.         void reset()
  1550.         {
  1551.             idle = 0;
  1552.             override = false;
  1553.         }
  1554.     };
  1555.  
  1556.     const int NUMPREVNODES = 6;
  1557.  
  1558.     struct aiinfo
  1559.     {
  1560.         vector<aistate> state;
  1561.         vector<int> route;
  1562.         vec target, spot;
  1563.         int enemy, enemyseen, enemymillis, weappref, prevnodes[NUMPREVNODES], targnode, targlast, targtime, targseq,
  1564.             lastrun, lasthunt, lastaction, jumpseed, jumprand, blocktime, huntseq, blockseq, lastaimrnd;
  1565.         float targyaw, targpitch, views[3], aimrnd[3];
  1566.         bool dontmove, becareful, tryreset, trywipe;
  1567.  
  1568.         aiinfo()
  1569.         {
  1570.             clearsetup();
  1571.             reset();
  1572.             loopk(3) views[k] = 0.f;
  1573.         }
  1574.         ~aiinfo() {}
  1575.  
  1576.         void clearsetup()
  1577.         {
  1578.             weappref = GUN_PISTOL;
  1579.             spot = target = vec(0, 0, 0);
  1580.             lastaction = lasthunt = enemyseen = enemymillis = blocktime = huntseq = blockseq = targtime = targseq = lastaimrnd = 0;
  1581.             lastrun = jumpseed = lastmillis;
  1582.             jumprand = lastmillis+5000;
  1583.             targnode = targlast = enemy = -1;
  1584.         }
  1585.  
  1586.         void clear(bool prev = true)
  1587.         {
  1588.             if(prev) memset(prevnodes, -1, sizeof(prevnodes));
  1589.             route.setsize(0);
  1590.         }
  1591.  
  1592.         void wipe()
  1593.         {
  1594.             clear(true);
  1595.             state.setsize(0);
  1596.             addstate(AI_S_WAIT);
  1597.             trywipe = false;
  1598.         }
  1599.  
  1600.         void clean(bool tryit = false)
  1601.         {
  1602.             if(!tryit) becareful = dontmove = false;
  1603.             targyaw = rnd(360);
  1604.             targpitch = 0.f;
  1605.             tryreset = tryit;
  1606.         }
  1607.  
  1608.         void reset(bool tryit = false) { wipe(); clean(tryit); }
  1609.  
  1610.         bool hasprevnode(int n) const
  1611.         {
  1612.             loopi(NUMPREVNODES) if(prevnodes[i] == n) return true;
  1613.             return false;
  1614.         }
  1615.  
  1616.         void addprevnode(int n)
  1617.         {
  1618.             if(prevnodes[0] != n)
  1619.             {
  1620.                 memmove(&prevnodes[1], prevnodes, sizeof(prevnodes) - sizeof(prevnodes[0]));
  1621.                 prevnodes[0] = n;
  1622.             }
  1623.         }
  1624.  
  1625.         aistate &addstate(int t, int r = -1, int v = -1)
  1626.         {
  1627.             return state.add(aistate(lastmillis, t, r, v));
  1628.         }
  1629.  
  1630.         void removestate(int index = -1)
  1631.         {
  1632.             if(index < 0) state.pop();
  1633.             else if(state.inrange(index)) state.remove(index);
  1634.             if(!state.length()) addstate(AI_S_WAIT);
  1635.         }
  1636.  
  1637.         aistate &getstate(int idx = -1)
  1638.         {
  1639.             if(state.inrange(idx)) return state[idx];
  1640.             return state.last();
  1641.         }
  1642.  
  1643.         aistate &switchstate(aistate &b, int t, int r = -1, int v = -1)
  1644.         {
  1645.             if(b.type == t && b.targtype == r)
  1646.             {
  1647.                 b.millis = lastmillis;
  1648.                 b.target = v;
  1649.                 b.reset();
  1650.                 return b;
  1651.             }
  1652.             return addstate(t, r, v);
  1653.         }
  1654.     };
  1655.  
  1656.     extern avoidset obstacles;
  1657.     extern vec aitarget;
  1658.  
  1659.     extern float viewdist(int x = 101);
  1660.     extern float viewfieldx(int x = 101);
  1661.     extern float viewfieldy(int x = 101);
  1662.     extern bool targetable(fpsent *d, fpsent *e);
  1663.     extern bool cansee(fpsent *d, vec &x, vec &y, vec &targ = aitarget);
  1664.  
  1665.     extern void init(fpsent *d, int at, int on, int sk, int bn, int pm, const char *name, const char *team);
  1666.     extern void update();
  1667.     extern void avoid();
  1668.     extern void think(fpsent *d, bool run);
  1669.  
  1670.     extern bool badhealth(fpsent *d);
  1671.     extern bool checkothers(vector<int> &targets, fpsent *d = NULL, int state = -1, int targtype = -1, int target = -1, bool teams = false);
  1672.     extern bool makeroute(fpsent *d, aistate &b, int node, bool changed = true, bool retry = false);
  1673.     extern bool makeroute(fpsent *d, aistate &b, const vec &pos, bool changed = true, bool retry = false);
  1674.     extern bool randomnode(fpsent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, float wander = SIGHTMAX);
  1675.     extern bool randomnode(fpsent *d, aistate &b, float guard = SIGHTMIN, float wander = SIGHTMAX);
  1676.     extern bool violence(fpsent *d, aistate &b, fpsent *e, bool pursue = false);
  1677.     extern bool patrol(fpsent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, float wander = SIGHTMAX, int walk = 1, bool retry = false);
  1678.     extern bool defend(fpsent *d, aistate &b, const vec &pos, float guard = SIGHTMIN, float wander = SIGHTMAX, int walk = 1);
  1679.     extern void assist(fpsent *d, aistate &b, vector<interest> &interests, bool all = false, bool force = false);
  1680.     extern bool parseinterests(fpsent *d, aistate &b, vector<interest> &interests, bool override = false, bool ignore = false);
  1681.  
  1682.     extern void spawned(fpsent *d);
  1683.     extern void damaged(fpsent *d, fpsent *e);
  1684.     extern void killed(fpsent *d, fpsent *e);
  1685.     extern void itemspawned(int ent);
  1686.  
  1687.     extern void render();
  1688. }
Add Comment
Please, Sign In to add comment