home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2007 September / maximum-cd-2007-09.iso / Assets / data / AssaultCube_v0.93.exe / source / src / weapon.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-06-03  |  21.6 KB  |  769 lines

  1. // weapon.cpp: all shooting and effects code
  2.  
  3. #include "cube.h"
  4. #include "bot/bot.h"
  5.  
  6. struct guninfo { short sound, reload, reloadtime, attackdelay, damage, projspeed, part, spread, recoil, magsize, mdl_kick_rot, mdl_kick_back; bool isauto; };
  7.  
  8. const int SGRAYS = 21;  //down from 21, must be 32 or less (default)
  9. const float SGSPREAD = 2;
  10. vec sg[SGRAYS];
  11.  
  12. char *gunnames[NUMGUNS] = { "knife", "pistol", "shotgun", "subgun", "sniper", "assault", "grenade" };
  13.  
  14. guninfo guns[NUMGUNS] =
  15. {    
  16.     { S_KNIFE,        S_NULL,     0,      500,    50,     0,   0,  1,    1,   1,    0,  0,  false },
  17.     { S_PISTOL,        S_RPISTOL,  1400,   170,    19,     0,   0, 80,   10,   8,    6,  5,  false },  // *SGRAYS
  18.     { S_SHOTGUN,    S_RSHOTGUN, 2400,   1000,   5,      0,   0,  1,   35,   7,    9,  9,  false },  //reload time is for 1 shell from 7 too powerful to 6
  19.     { S_SUBGUN,        S_RSUBGUN,  1650,   80,        16,        0,   0, 70,   15,   30,   1,  2,  true  },
  20.     { S_SNIPER,        S_RSNIPER,  1950,   1500,   85,     0,   0, 60,   50,   5,    4,  4,  false },
  21.     { S_ASSAULT,    S_RASSAULT, 2000,   130,    24,     0,   0, 20,   40,   15,   0,  2,  true  },  //recoil was 44
  22.     { S_NULL,        S_NULL,     1000,   1200,   150,    20,  6,  1,    1,   1,    3,  1,  false },
  23. };
  24.  
  25. int nadetimer = 2000; // detonate after $ms
  26. bool gun_changed = false;
  27.  
  28. void checkweaponswitch()
  29. {
  30.     if(!player1->weaponchanging) return;
  31.     int timeprogress = lastmillis-player1->lastaction;
  32.     if(timeprogress>WEAPONCHANGE_TIME) 
  33.     {
  34.         gun_changed = true;
  35.         player1->weaponchanging = false; 
  36.         player1->lastaction = 0;
  37.     }
  38.     else if(timeprogress>WEAPONCHANGE_TIME/2)
  39.     {
  40.         if((!player1->akimbo && player1->akimbomillis && player1->nextweapon==GUN_PISTOL) || 
  41.             (player1->akimbo && !player1->akimbomillis && player1->gunselect==GUN_PISTOL)) 
  42.                 player1->akimbo = !player1->akimbo;
  43.         player1->gunselect = player1->nextweapon;
  44.     }
  45. }
  46.  
  47. void weaponswitch(int gun)
  48. {
  49.     player1->weaponchanging = true;
  50.     player1->thrownademillis = 0;
  51.     player1->lastaction = player1->akimbolastaction[0] = player1->akimbolastaction[1] = lastmillis;
  52.     player1->nextweapon = gun;
  53.     playsound(S_GUNCHANGE);
  54. }
  55.  
  56. void weapon(int gun)
  57. {
  58.     if(gun == player1->gunselect) return;
  59.     if(player1->state!=CS_ALIVE || player1->weaponchanging || NADE_IN_HAND || player1->reloading) return;
  60.     if(gun != GUN_KNIFE && gun != GUN_GRENADE && gun != GUN_PISTOL && gun != player1->primary) return;
  61.  
  62.     if(m_noguns && gun != GUN_KNIFE && gun != GUN_GRENADE) return;
  63.     if(m_noprimary && gun != GUN_KNIFE && gun != GUN_GRENADE && gun != GUN_PISTOL) return;
  64.     if(m_nopistol && gun == GUN_PISTOL) return;
  65.     if(gun == GUN_GRENADE && !player1->mag[GUN_GRENADE]) return;
  66.  
  67.     setscope(false);
  68.     weaponswitch(gun);
  69. }
  70.  
  71. void shiftweapon(int s)
  72. {
  73.     for(int i = 0; i < NUMGUNS && !player1->weaponchanging; i++) 
  74.     {
  75.         int trygun = player1->gunselect + s + (s < 0 ? -i : i);
  76.         if((trygun %= NUMGUNS) < 0) trygun += NUMGUNS;
  77.         weapon(trygun);
  78.     }
  79. }
  80.  
  81. int currentprimary() { return player1->primary; }
  82. int curweapon() { return player1->gunselect; }
  83. int magcontent(int gun) { if(gun > 0 && gun < NUMGUNS) return player1->mag[gun]; else return -1;}
  84.  
  85. COMMAND(weapon, ARG_1INT);
  86. COMMAND(shiftweapon, ARG_1INT);
  87. COMMAND(currentprimary, ARG_1EST);
  88. COMMAND(curweapon, ARG_1EXP);
  89. COMMAND(magcontent, ARG_1EXP);
  90.  
  91. VAR(scopefov, 5, 50, 50);
  92. bool scoped = false;
  93. int oldfov = 100;
  94.  
  95. void setscope(bool activate)
  96. {
  97.     if(player1->gunselect != GUN_SNIPER || player1->state == CS_DEAD) return;
  98.     if(activate == scoped) return;
  99.     if(activate)
  100.     {
  101.         oldfov = getvar("fov");
  102.         setvar("fov", scopefov);
  103.     }
  104.     else
  105.     {
  106.         setvar("fov", oldfov);
  107.     }
  108.     scoped = activate;
  109. }
  110.  
  111. COMMAND(setscope, ARG_1INT);
  112.  
  113. void reload(playerent *d)
  114. {
  115.     if(demoplayback) { setvar("gamespeed", getvar("gamespeed") != 100 ? 100 : 35); return; }
  116.     if(!d || d->state!=CS_ALIVE || d->reloading || d->weaponchanging) return;
  117.     if(!reloadable_gun(d->gunselect) || d->ammo[d->gunselect]<=0) return;
  118.     if(d->mag[d->gunselect] >= (has_akimbo(d) ? 2 : 1)*guns[d->gunselect].magsize) return;
  119.     if(d == player1) setscope(false);
  120.     
  121.     d->reloading = true;
  122.     d->lastaction = lastmillis;
  123.     d->akimbolastaction[0] = d->akimbolastaction[1] = lastmillis;
  124.     d->gunwait = guns[d->gunselect].reloadtime;
  125.     
  126.     int numbullets = (has_akimbo(d) ? 2 : 1)*guns[d->gunselect].magsize - d->mag[d->gunselect];
  127.     
  128.     if(has_akimbo(d)) numbullets = guns[d->gunselect].magsize * 2 - d->mag[d->gunselect];
  129.     
  130.     if(numbullets > d->ammo[d->gunselect]) numbullets = d->ammo[d->gunselect];
  131.     d->mag[d->gunselect] += numbullets;
  132.     d->ammo[d->gunselect] -= numbullets;
  133.  
  134.     if(has_akimbo(d)) playsoundc(S_RAKIMBO);
  135.     else if(d->type==ENT_BOT) playsound(guns[d->gunselect].reload, &d->o);
  136.     else playsoundc(guns[d->gunselect].reload);
  137. }
  138.  
  139. void selfreload() { reload(player1); }
  140. COMMANDN(reload, selfreload, ARG_NONE);
  141.  
  142. int reloadtime(int gun) { return guns[gun].reloadtime; }
  143. int attackdelay(int gun) { return guns[gun].attackdelay; }
  144. int magsize(int gun) { return guns[gun].magsize; }
  145. int kick_rot(int gun) { return guns[gun].mdl_kick_rot; }
  146. int kick_back(int gun) { return guns[gun].mdl_kick_back; }
  147.  
  148. void createrays(vec &from, vec &to)             // create random spread of rays for the shotgun
  149. {
  150.     float f = to.dist(from)*SGSPREAD/1000;
  151.     loopi(SGRAYS)
  152.     {
  153.         #define RNDD (rnd(101)-50)*f
  154.         vec r(RNDD, RNDD, RNDD);
  155.         sg[i] = to;
  156.         sg[i].add(r);
  157.         #undef RNDD
  158.     }
  159. }
  160.  
  161. bool intersect(dynent *d, vec &from, vec &to, vec *end)   // if lineseg hits entity bounding box
  162. {
  163.     vec v = to, w = d->o, *p; 
  164.     v.sub(from);
  165.     w.sub(from);
  166.     float c1 = w.dot(v);
  167.  
  168.     if(c1<=0) p = &from;
  169.     else
  170.     {
  171.         float c2 = v.squaredlen();
  172.         if(c2<=c1) p = &to;
  173.         else
  174.         {
  175.             float f = c1/c2;
  176.             v.mul(f).add(from);
  177.             p = &v;
  178.         }
  179.     }
  180.  
  181.     return p->x <= d->o.x+d->radius
  182.         && p->x >= d->o.x-d->radius
  183.         && p->y <= d->o.y+d->radius
  184.         && p->y >= d->o.y-d->radius
  185.         && p->z <= d->o.z+d->aboveeye
  186.         && p->z >= d->o.z-d->eyeheight;
  187. }
  188.  
  189. playerent *intersectclosest(vec &from, vec &to, int &n, playerent *at)
  190. {
  191.     playerent *best = NULL;
  192.     float bestdist = 1e16f;
  193.     if(at!=player1 && player1->state==CS_ALIVE && intersect(player1, from, to))
  194.     {
  195.         best = player1;
  196.         bestdist = at->o.dist(player1->o);
  197.         n = getclientnum();
  198.     }
  199.     loopv(players)
  200.     {
  201.         playerent *o = players[i];
  202.         if(!o || o==at || o->state!=CS_ALIVE) continue;
  203.         if(!intersect(o, from, to)) continue;
  204.         float dist = at->o.dist(o->o);
  205.         if(dist<bestdist)
  206.         {
  207.             best = o;
  208.             bestdist = dist;
  209.             n = i; 
  210.         }
  211.     }
  212.     return best;
  213. }
  214.  
  215. playerent *playerincrosshair()
  216. {
  217.     if(demoplayback) return NULL;
  218.     int n;
  219.     return intersectclosest(player1->o, worldpos, n, player1);
  220. }
  221.  
  222. const int MAXPROJ = 100;
  223. struct projectile { vec o, to; float speed; playerent *owner; int gun; bool inuse, local; };
  224. projectile projs[MAXPROJ];
  225.  
  226. void projreset() { loopi(MAXPROJ) projs[i].inuse = false; }
  227.  
  228. void newprojectile(vec &from, vec &to, float speed, bool local, playerent *owner, int gun)
  229. {
  230.     loopi(MAXPROJ)
  231.     {
  232.         projectile *p = &projs[i];
  233.         if(p->inuse) continue;
  234.         p->inuse = true;
  235.         p->o = from;
  236.         p->to = to;
  237.         p->speed = speed;
  238.         p->local = local;
  239.         p->owner = owner;
  240.         p->gun = gun;
  241.         return;
  242.     }
  243. }
  244.  
  245. void hit(int target, int damage, playerent *d, playerent *at, bool gib=false)
  246. {
  247.     if(d==player1 || d->type==ENT_BOT) dodamage(damage, -1, at, gib, d);
  248.     else
  249.     {
  250.          addmsg(gib ? SV_GIBDAMAGE : SV_DAMAGE, "ri3", target, damage, d->lifesequence);
  251.          playsound(S_PAIN1+rnd(5), &d->o);
  252.     }
  253.     particle_splash(3, damage, 1000, d->o);
  254.     demodamage(damage, d->o);
  255. }
  256.  
  257. const float RL_DAMRAD = 10;
  258.  
  259. void radialeffect(playerent *o, vec &v, int cn, int qdam, playerent *at)
  260. {
  261.     if(o->state!=CS_ALIVE) return;
  262.     vec temp;
  263.     float dist = o->o.dist(v, temp);
  264.     dist -= 2; // account for eye distance imprecision
  265.     if(dist<RL_DAMRAD) 
  266.     {
  267.         if(dist<0) dist = 0;
  268.         int damage = (int)(qdam*(1-(dist/RL_DAMRAD)));
  269.         hit(cn, damage, o, at, true);
  270.         o->vel.add(temp.mul((RL_DAMRAD-dist)*damage/800));
  271.     }
  272. }
  273.  
  274. void splash(projectile *p, vec &v, vec &vold, int notthisplayer, int qdam)
  275. {
  276.     particle_splash(0, 50, 300, v);
  277.     p->inuse = false;
  278.     if(p->gun!=GUN_GRENADE)
  279.     {
  280.         playsound(S_FEXPLODE, &v);
  281.         // no push?
  282.     }
  283.     else
  284.     {
  285.         //playsound(S_RLHIT, &v);
  286.         particle_fireball(5, v); 
  287.         dodynlight(vold, v, 0, 0, p->owner);
  288.         if(!p->local) return;
  289.         radialeffect(player1, v, -1, qdam, p->owner);
  290.         loopv(players)
  291.         {
  292.             if(i==notthisplayer) continue;
  293.             playerent *o = players[i];
  294.             if(!o) continue; 
  295.             radialeffect(o, v, i, qdam, p->owner);
  296.         }
  297.     }
  298. }
  299.  
  300. inline void projdamage(playerent *o, projectile *p, vec &v, int i, int qdam)
  301. {
  302.     if(o->state!=CS_ALIVE) return;
  303.     if(intersect(o, p->o, v))
  304.     {
  305.         splash(p, v, p->o, i, qdam);
  306.         hit(i, qdam, o, p->owner, true); //fixme
  307.     } 
  308. }
  309.  
  310. void moveprojectiles(float time)
  311. {
  312.     loopi(MAXPROJ)
  313.     {
  314.         projectile *p = &projs[i];
  315.         if(!p->inuse) continue;
  316.         //int qdam = guns[p->gun].damage*(p->owner->quadmillis ? 4 : 1);
  317.         int qdam = guns[p->gun].damage;
  318.         vec v;
  319.         float dist = p->to.dist(p->o, v);
  320.         float dtime = dist*1000/p->speed;
  321.         if(time>dtime) dtime = time;
  322.         v.mul(time/dtime).add(p->o);
  323.         if(p->local)
  324.         {
  325.             loopvj(players)
  326.             {
  327.                 playerent *o = players[j];
  328.                 if(!o) continue; 
  329.                 projdamage(o, p, v, j, qdam);
  330.             }
  331.             if(p->owner!=player1) projdamage(player1, p, v, -1, qdam);
  332.         }
  333.         if(p->inuse)
  334.         {
  335.             if(time==dtime) splash(p, v, p->o, -1, qdam);
  336.             else
  337.             {
  338.                 if(p->gun==GUN_GRENADE) { dodynlight(p->o, v, 0, 255, p->owner); particle_splash(5, 2, 200, v); }
  339.                 else { particle_splash(1, 1, 200, v); particle_splash(guns[p->gun].part, 1, 1, v); }
  340.                 // Added by Rick
  341.                 traceresult_s tr;
  342.                 TraceLine(p->o, v, p->owner, true, &tr);
  343.                 if (tr.collided) splash(p, v, p->o, -1, qdam);
  344.                 // End add                
  345.             }       
  346.         }
  347.         p->o = v;
  348.     }
  349. }
  350.  
  351. vector<bounceent *> bounceents;
  352.  
  353. bounceent *newbounceent()
  354. {
  355.     bounceent *p = new bounceent;
  356.     bounceents.add(p);
  357.     return p;
  358. }
  359.  
  360. void explode_nade(bounceent *i);
  361.  
  362. void movebounceents()
  363. {
  364.     loopv(bounceents) if(bounceents[i])
  365.     {
  366.         bounceent *p = bounceents[i];
  367.         if(p->bouncestate == NADE_THROWED || p->bouncestate == GIB) moveplayer(p, 2, false);
  368.         
  369.         if(lastmillis - p->millis >= p->timetolife)
  370.         {
  371.             if(p->bouncestate==NADE_ACTIVATED || p->bouncestate==NADE_THROWED) explode_nade(bounceents[i]);
  372.             delete p;
  373.             bounceents.remove(i);
  374.             i--;
  375.         }
  376.     }
  377. }
  378.  
  379. void clearbounceents()
  380. {   
  381.     if(gamespeed==100);
  382.     else if(multiplayer(false)) bounceents.add((bounceent *)player1);
  383.     loopv(bounceents) if(bounceents[i]) { delete bounceents[i]; bounceents.remove(i--); }
  384. }
  385.  
  386. void renderbounceents()
  387. {
  388.     loopv(bounceents)
  389.     {
  390.         bounceent *p = bounceents[i];
  391.         if(!p) continue;
  392.         string model;
  393.         float z = p->o.z;
  394.  
  395.         switch(p->bouncestate)
  396.         {
  397.             case NADE_THROWED:
  398.                 s_strcpy(model, "weapons/grenade/static");
  399.                 break;
  400.             case GIB:
  401.             default:
  402.             {    
  403.                 uint n = (((4*(uint)(size_t)p)+(uint)p->timetolife)%3)+1;
  404.                 s_sprintf(model)("misc/gib0%u", n);
  405.                 int t = lastmillis-p->millis;
  406.                 if(t>p->timetolife-2000)
  407.                 {
  408.                     t -= p->timetolife-2000;
  409.                     z -= t*t/4000000000.0f*t;
  410.                 }
  411.                 break;
  412.             }
  413.         }
  414.         path(model);
  415.         rendermodel(model, ANIM_MAPMODEL|ANIM_LOOP, 0, 1.1f, p->o.x, p->o.y, z, p->yaw+90, p->pitch);
  416.     }
  417. }
  418.  
  419. VARP(gibnum, 0, 6, 1000); 
  420. VARP(gibttl, 0, 5000, 15000);
  421. VARP(gibspeed, 1, 30, 100);
  422.  
  423. void addgib(playerent *d)
  424. {   
  425.     if(!d) return;
  426.     playsound(S_GIB, &d->o);
  427.  
  428.     loopi(gibnum)
  429.     {
  430.         bounceent *p = newbounceent();
  431.         p->owner = d;
  432.         p->millis = lastmillis;
  433.         p->timetolife = gibttl+rnd(10)*100;
  434.         p->bouncestate = GIB;
  435.  
  436.         p->o = d->o;
  437.         p->o.z -= d->aboveeye;
  438.     
  439.         p->yaw = (float)rnd(360);
  440.         p->pitch = (float)rnd(360);
  441.  
  442.         p->maxspeed = 30.0f;
  443.         p->rotspeed = 3.0f;
  444.  
  445.         const float angle = (float)rnd(360);
  446.         const float speed = (float)gibspeed;
  447.  
  448.         p->vel.x = sinf(RAD*angle)*rnd(1000)/1000.0f;
  449.         p->vel.y = cosf(RAD*angle)*rnd(1000)/1000.0f;
  450.         p->vel.z = rnd(1000)/1000.0f;
  451.         p->vel.mul(speed/100.0f);
  452.     }
  453. }
  454.  
  455. void thrownade(playerent *d, const vec &vel, bounceent *p)
  456. {
  457.     if(!d || !p) return;
  458.     vec dir(vel);
  459.     dir.mul(1.1f*d->radius);
  460.     p->vel = vel;
  461.     p->o.add(dir);
  462.     p->o.add(d->o);
  463.     loopi(10) moveplayer(p, 10, true, 10);
  464.  
  465.     p->bouncestate = NADE_THROWED;
  466.     d->thrownademillis = lastmillis;
  467.     d->inhandnade = NULL;
  468.     
  469.     if(d==player1)
  470.     {
  471.         player1->lastaction = lastmillis;
  472.         addmsg(SV_SHOT, "ri8", d->gunselect, (int)(d->o.x*DMF), (int)(d->o.y*DMF), (int)(d->o.z*DMF), (int)(vel.x*DMF), (int)(vel.y*DMF), (int)(vel.z*DMF), lastmillis-p->millis);
  473.     }
  474. }
  475.  
  476. void thrownade(playerent *d, bounceent *p)
  477. {
  478.     if(!d || !p) return;
  479.     const float speed = cosf(RAD*d->pitch);
  480.     vec vel(sinf(RAD*d->yaw)*speed, -cosf(RAD*d->yaw)*speed, sinf(RAD*d->pitch));
  481.     vel.mul(1.5f);
  482.     thrownade(d, vel, p);
  483. }
  484.  
  485. bounceent *new_nade(playerent *d, int millis = 0)
  486. {
  487.     bounceent *p = newbounceent();
  488.     p->owner = d;
  489.     p->millis = lastmillis;
  490.     p->timetolife = 2000-millis;
  491.     p->bouncestate = NADE_ACTIVATED;
  492.     p->maxspeed = 27.0f;
  493.     p->rotspeed = 6.0f;
  494.     
  495.     d->inhandnade = p;
  496.     d->thrownademillis = 0;  
  497.     if(d==player1 && !demoplayback) playsoundc(S_GRENADEPULL);
  498.     return p;
  499. }
  500.  
  501. void explode_nade(bounceent *i)
  502.     if(!i) return;
  503.     if(i->bouncestate != NADE_THROWED) thrownade(i->owner, vec(0,0,0), i);
  504.     playsound(S_FEXPLODE, &i->o);
  505.     newprojectile(i->o, i->o, 1, i->owner==player1, i->owner, GUN_GRENADE);
  506. }
  507.  
  508. void shootv(int gun, vec &from, vec &to, playerent *d, bool local, int nademillis)     // create visual effect from a shot
  509. {
  510.     if(guns[gun].sound) playsound(guns[gun].sound, d==player1 ? NULL : &d->o);
  511.     switch(gun)
  512.     {
  513.         case GUN_KNIFE:
  514.             break;
  515.  
  516.         case GUN_SHOTGUN:
  517.         {
  518.             loopi(SGRAYS) particle_splash(0, 5, 200, sg[i]);
  519.             if(addbullethole(from, to))
  520.             {
  521.                 int holes = 3+rnd(5);
  522.                 loopi(holes) addbullethole(from, sg[i], 0);
  523.             }
  524.             break;
  525.         }
  526.  
  527.         case GUN_PISTOL:
  528.         case GUN_SUBGUN:
  529.         case GUN_ASSAULT:
  530.         {
  531.             addbullethole(from, to);
  532.             addshotline(d, from, to);
  533.             particle_splash(0, 5, 250, to);
  534.             if(!local && !CMPB(guns, 0xc7d70531)) player1->clientnum += 250*(int)to.dist(from);
  535.             break;
  536.         }
  537.  
  538.         case GUN_GRENADE:
  539.         {
  540.             if(d!=player1 || demoplayback)
  541.             {
  542.                 bounceent *p = new_nade(d, nademillis);
  543.                 thrownade(d, to, p);
  544.             }
  545.             break;
  546.         }
  547.             
  548.         case GUN_SNIPER: 
  549.         {
  550.             addbullethole(from, to);
  551.             addshotline(d, from, to);
  552.             particle_splash(0, 50, 200, to);
  553.             particle_trail(1, 500, from, to);
  554.             break;
  555.         }
  556.     }
  557. }
  558.  
  559. void hitpush(int target, int damage, playerent *d, playerent *at, vec &from, vec &to, bool gib = false)
  560. {
  561.     hit(target, damage, d, at, gib);
  562.     vec v;
  563.     float dist = to.dist(from, v);
  564.     v.mul(damage/dist/50);
  565.     d->vel.add(v);
  566. }
  567.  
  568. void shorten(vec &from, vec &to, vec &target)
  569. {
  570.     target.sub(from).normalize().mul(from.dist(to)).add(from);
  571. }
  572.  
  573. const float HEADSIZE = 0.8f;
  574. vec hitpos;
  575.  
  576. void raydamage(vec &from, vec &to, playerent *d)
  577. {
  578.     int i = -1, gdam = guns[d->gunselect].damage;
  579.     playerent *o = NULL;
  580.     if(d->gunselect==GUN_SHOTGUN)
  581.     {
  582.         uint done = 0;
  583.         playerent *cl = NULL;
  584.         int n = -1;
  585.         for(;;)
  586.         {
  587.             bool raysleft = false;
  588.             int damage = 0;
  589.             o = NULL;
  590.             loop(r, SGRAYS) if((done&(1<<r))==0 && (cl = intersectclosest(from, sg[r], n, d)))
  591.             {
  592.                 if(!o || o==cl)
  593.                 {
  594.                     damage += gdam;
  595.                     o = cl;
  596.                     done |= 1<<r;
  597.                     i = n;
  598.                     shorten(from, o->o, sg[r]);
  599.                 }
  600.                 else raysleft = true;
  601.             }
  602.             if(damage) hitpush(i, damage, o, d, from, to);
  603.             if(!raysleft) break;
  604.         }
  605.     }
  606.     else if((o = intersectclosest(from, to, i, d)))
  607.     {
  608.         bool gib = false;
  609.         if(d->gunselect==GUN_KNIFE) gib = true;
  610.         else if(d==player1 && d->gunselect==GUN_SNIPER
  611.         && worldpos!=hitpos && hitpos.z>=o->o.z+o->aboveeye-HEADSIZE
  612.         && intersect(o, from, hitpos)) 
  613.         {
  614.             gdam *= 3;
  615.             gib = true;
  616.         }
  617.  
  618.         hitpush(i, gdam, o, d, from, to, gib);
  619.         shorten(from, o->o, to);
  620.     }
  621. }
  622.  
  623. void spreadandrecoil(vec &from, vec &to, playerent *d)
  624. {
  625.     //nothing special for a knife
  626.     if (d->gunselect==GUN_KNIFE || d->gunselect==GUN_GRENADE) return;
  627.  
  628.     //spread
  629.     vec unitv;
  630.     float dist = to.dist(from, unitv);
  631.     float f = dist/1000;
  632.     int spd = guns[d->gunselect].spread;
  633.  
  634.     //recoil
  635.     float rcl = guns[d->gunselect].recoil*-0.01f;
  636.  
  637.     if(d->gunselect==GUN_ASSAULT)
  638.     {
  639.         if(d->shots > 3)
  640.             spd = 70;
  641.         rcl += (rnd(8)*-0.01f);
  642.     }
  643.    
  644.     if((d->gunselect==GUN_SNIPER) /*&& (d->vel.x<.25f && d->vel.y<.25f)*/ && scoped)
  645.     {
  646.         spd = 1;
  647.         rcl = rcl / 3;
  648.     }
  649.  
  650.     if(d->gunselect!=GUN_SHOTGUN)  //no spread on shotgun
  651.     {   
  652.         #define RNDD (rnd(spd)-spd/2)*f
  653.         vec r(RNDD, RNDD, RNDD);
  654.         to.add(r);
  655.         #undef RNDD
  656.     }
  657.  
  658.    //increase pitch for recoil
  659.     d->vel.add(vec(unitv).mul(rcl/dist));
  660.  
  661.     if(d->pitch<80.0f) d->pitch += guns[d->gunselect].recoil*0.05f;
  662. }
  663.  
  664. bool hasammo(playerent *d)     // bot mod
  665. {
  666.     if(!d->mag[d->gunselect])
  667.     {
  668.         if (d->type==ENT_BOT) playsound(S_NOAMMO, &d->o);
  669.         else playsoundc(S_NOAMMO);
  670.         d->gunwait = 250;
  671.         d->lastaction = lastmillis;
  672.         d->lastattackgun = -1;
  673.         return false;
  674.     } else return true;
  675. }
  676.  
  677. VARP(autoreload, 0, 1, 1);
  678.  
  679. const int grenadepulltime = 650;
  680. bool akimboside = false;
  681.  
  682. void shoot(playerent *d, vec &targ)
  683. {
  684.     int attacktime = lastmillis-d->lastaction;
  685.  
  686.     vec from = d->o;
  687.     vec to = targ;
  688.  
  689.     if(d->gunselect==GUN_GRENADE)
  690.     {
  691.         d->shots = 0;
  692.  
  693.         if(d->thrownademillis && attacktime >= 13*(1000/25))
  694.         {
  695.             d->weaponchanging = true;
  696.             d->nextweapon = d->mag[GUN_GRENADE] ? GUN_GRENADE : d->primary;
  697.             d->thrownademillis = 0;
  698.             d->lastaction = lastmillis-1-WEAPONCHANGE_TIME/2;
  699.         }
  700.  
  701.         if(d->weaponchanging || attacktime<d->gunwait) return;
  702.         d->gunwait = 0;
  703.         d->reloading = false;
  704.  
  705.         if(d->attacking && !d->inhandnade) // activate
  706.         {
  707.             if(!hasammo(d)) return;
  708.             d->mag[d->gunselect]--;
  709.             new_nade(d);
  710.             d->gunwait = grenadepulltime;
  711.             d->lastaction = lastmillis;
  712.             d->lastattackgun = d->gunselect;
  713.         }
  714.         else if(!d->attacking && d->inhandnade && attacktime>grenadepulltime) thrownade(d, d->inhandnade);
  715.         return;
  716.     }
  717.     else
  718.     {
  719.         if(autoreload && d == player1 && !d->mag[d->gunselect] && lastmillis-d->lastaction>guns[d->gunselect].attackdelay) { reload(d); return; }
  720.         if(d->weaponchanging || attacktime<d->gunwait) return;
  721.         d->gunwait = 0;
  722.         d->reloading = false;
  723.  
  724.         if(!d->attacking) { d->shots = 0; return; }
  725.         d->lastaction = lastmillis;
  726.         d->lastattackgun = d->gunselect;
  727.  
  728.         if(d->gunselect!=GUN_KNIFE && !hasammo(d)) { d->shots = 0; return; }
  729.             
  730.         if(guns[d->gunselect].isauto) d->shots++;
  731.         else d->attacking = false;
  732.  
  733.         if(d->gunselect==GUN_PISTOL && d->akimbo) 
  734.         {
  735.             d->attacking = true;  // make akimbo auto
  736.             d->akimbolastaction[akimboside?1:0] = lastmillis;
  737.             akimboside = !akimboside;
  738.         }
  739.         
  740.         if(d->gunselect!=GUN_KNIFE) d->mag[d->gunselect]--;
  741.         from.z -= 0.2f;    // below eye
  742.         if(attacktime<d->gunwait) from = to;
  743.     }
  744.     
  745.     spreadandrecoil(from,to,d);
  746.     vec unitv;
  747.     float dist = to.dist(from, unitv);
  748.     unitv.div(dist);
  749.  
  750.     if(d->gunselect==GUN_KNIFE) 
  751.     {
  752.         unitv.mul(3); // punch range
  753.         to = from;
  754.         to.add(unitv);
  755.     }   
  756.     if(d->gunselect==GUN_SHOTGUN) createrays(from, to);
  757.     
  758.     if(has_akimbo(d)) d->gunwait = guns[d->gunselect].attackdelay / 2;  //make akimbo pistols shoot twice as fast as normal pistol
  759.     else d->gunwait = guns[d->gunselect].attackdelay;
  760.     
  761.     shootv(d->gunselect, from, to, d, true, 0);
  762.     if(d->type==ENT_PLAYER) addmsg(SV_SHOT, "ri8", d->gunselect, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF), 0);
  763.  
  764.     if(guns[d->gunselect].projspeed || d->gunselect==GUN_GRENADE) return;
  765.  
  766.     raydamage(from, to, d);    
  767. }
  768.