home *** CD-ROM | disk | FTP | other *** search
/ Qu-ake / Qu-ake.iso / qu_ke / waffen / 001 / WEAPONS.QC < prev   
Encoding:
Text File  |  1996-08-29  |  81.8 KB  |  3,228 lines

  1. /*
  2.  
  3. The Mark I SuperScream Grenade Machine v1.0
  4. by Klaus Breuer (sz0759@rzmail.uni-erlangen.de)
  5.  
  6. This patch adds a lot of new ammo for the grenade launcher, my favourite
  7. weapon: 12 new grenades!
  8.  
  9. See ImpulseCommands() at the end of this file for a list of
  10. all possible grenades. Also check ClientConnect() in CLIENT.QC.
  11.  
  12. I wasn't able to add a new variable to the player object (anybody who
  13. can tell me how to do that?), so I used the self.button1 (which isn't
  14. used by the game) to remember which ammo type is selected.
  15.  
  16. A lot of these ammo types are way too powerful - you might want to
  17. disable them or tone them down (cause them to use more ammo, cause
  18. damage to the user, etc). I just use them for fun and messing about :)
  19.  
  20. To disable any ammo type, you'll need to look at these functions:
  21. ImpulseCommands()  // Selecting ammo type
  22. ShowGrenadeType()  // Identify currently loaded ammo
  23. W_FireGrenade()    // Select which grenade to fire
  24. ClientConnect()    // Show info on available ammo (in CLIENT.QC)
  25.  
  26.  
  27. Notes:
  28.  
  29. * I purposely made each ammo type totally self-contained, so you can
  30.   easily port them to your own code.
  31. * I tried to document a lot so you can easily make changes.
  32. * This is mostly my own code, but quite a few ideas/code bits were
  33.   from other people. Sorry, I forgot which ones (thought too late of
  34.   making a list), so if you recognize your work, drop me a mail.
  35.  
  36. */
  37. void (entity targ, entity inflictor, entity attacker, float damage) T_Damage;
  38. void () player_run;
  39. void(entity bomb, entity attacker, float rad, entity ignore) T_RadiusDamage;
  40. void(vector org, vector vel, float damage) SpawnBlood;
  41. void() SuperDamageSound;
  42.  
  43.  
  44. // called by worldspawn
  45. void() W_Precache =
  46. {
  47.     precache_sound ("weapons/r_exp3.wav");    // new rocket explosion
  48.     precache_sound ("weapons/rocket1i.wav");    // spike gun
  49.     precache_sound ("weapons/sgun1.wav");
  50.     precache_sound ("weapons/guncock.wav");    // player shotgun
  51.     precache_sound ("weapons/ric1.wav");    // ricochet (used in c code)
  52.     precache_sound ("weapons/ric2.wav");    // ricochet (used in c code)
  53.     precache_sound ("weapons/ric3.wav");    // ricochet (used in c code)
  54.     precache_sound ("weapons/spike2.wav");    // super spikes
  55.     precache_sound ("weapons/tink1.wav");    // spikes tink (used in c code)
  56.     precache_sound ("weapons/grenade.wav");    // grenade launcher
  57.     precache_sound ("weapons/bounce.wav");        // grenade bounce
  58.     precache_sound ("weapons/shotgn2.wav");    // super shotgun
  59.  
  60.         precache_sound ("talk/scum.wav"); // Duke: Scumsucker
  61.  
  62. };
  63.  
  64. float() crandom =
  65. {
  66.     return 2*(random() - 0.5);
  67. };
  68.  
  69. /*
  70. ================
  71. W_FireAxe
  72. ================
  73. */
  74. void() W_FireAxe =
  75. {
  76.     local    vector    source;
  77.     local    vector    org;
  78.  
  79.     source = self.origin + '0 0 16';
  80.     traceline (source, source + v_forward*64, FALSE, self);
  81.     if (trace_fraction == 1.0)
  82.         return;
  83.     
  84.     org = trace_endpos - v_forward*4;
  85.  
  86.     if (trace_ent.takedamage)
  87.     {
  88.         trace_ent.axhitme = 1;
  89.         SpawnBlood (org, '0 0 0', 20);
  90.         T_Damage (trace_ent, self, self, 20);
  91.     }
  92.     else
  93.     {    // hit wall
  94.         sound (self, CHAN_WEAPON, "player/axhit2.wav", 1, ATTN_NORM);
  95.         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  96.         WriteByte (MSG_BROADCAST, TE_GUNSHOT);
  97.         WriteCoord (MSG_BROADCAST, org_x);
  98.         WriteCoord (MSG_BROADCAST, org_y);
  99.         WriteCoord (MSG_BROADCAST, org_z);
  100.     }
  101. };
  102.  
  103.  
  104. //============================================================================
  105.  
  106.  
  107. vector() wall_velocity =
  108. {
  109.     local vector    vel;
  110.     
  111.     vel = normalize (self.velocity);
  112.     vel = normalize(vel + v_up*(random()- 0.5) + v_right*(random()- 0.5));
  113.     vel = vel + 2*trace_plane_normal;
  114.     vel = vel * 200;
  115.     
  116.     return vel;
  117. };
  118.  
  119.  
  120. /*
  121. ================
  122. SpawnMeatSpray
  123. ================
  124. */
  125. void(vector org, vector vel) SpawnMeatSpray =
  126. {
  127.     local    entity missile, mpuff;
  128.     local    vector    org;
  129.  
  130.     missile = spawn ();
  131.     missile.owner = self;
  132.     missile.movetype = MOVETYPE_BOUNCE;
  133.     missile.solid = SOLID_NOT;
  134.  
  135.     makevectors (self.angles);
  136.  
  137.     missile.velocity = vel;
  138.     missile.velocity_z = missile.velocity_z + 250 + 50*random();
  139.  
  140.     missile.avelocity = '3000 1000 2000';
  141.  
  142. // set missile duration
  143.     missile.nextthink = time + 1;
  144.     missile.think = SUB_Remove;
  145.  
  146.     setmodel (missile, "progs/zom_gib.mdl");
  147.     setsize (missile, '0 0 0', '0 0 0');
  148.     setorigin (missile, org);
  149. };
  150.  
  151. /*
  152. ================
  153. SpawnBlood
  154. ================
  155. */
  156. void(vector org, vector vel, float damage) SpawnBlood =
  157. {
  158.     particle (org, vel*0.1, 73, damage*2);
  159. };
  160.  
  161. /*
  162. ================
  163. spawn_touchblood
  164. ================
  165. */
  166. void(float damage) spawn_touchblood =
  167. {
  168.     local vector    vel;
  169.  
  170.     vel = wall_velocity () * 0.2;
  171.     SpawnBlood (self.origin + vel*0.01, vel, damage);
  172. };
  173.  
  174.  
  175. /*
  176. ================
  177. SpawnChunk
  178. ================
  179. */
  180. void(vector org, vector vel) SpawnChunk =
  181. {
  182.     particle (org, vel*0.02, 0, 10);
  183. };
  184.  
  185. /*
  186. ==============================================================================
  187.  
  188. MULTI-DAMAGE
  189.  
  190. Collects multiple small damages into a single damage
  191.  
  192. ==============================================================================
  193. */
  194.  
  195. entity    multi_ent;
  196. float    multi_damage;
  197.  
  198. void() ClearMultiDamage =
  199. {
  200.     multi_ent = world;
  201.     multi_damage = 0;
  202. };
  203.  
  204. void() ApplyMultiDamage =
  205. {
  206.     if (!multi_ent)
  207.         return;
  208.     T_Damage (multi_ent, self, self, multi_damage);
  209. };
  210.  
  211. void(entity hit, float damage) AddMultiDamage =
  212. {
  213.     if (!hit)
  214.         return;
  215.     
  216.     if (hit != multi_ent)
  217.     {
  218.         ApplyMultiDamage ();
  219.         multi_damage = damage;
  220.         multi_ent = hit;
  221.     }
  222.     else
  223.         multi_damage = multi_damage + damage;
  224. };
  225.  
  226. /*
  227. ==============================================================================
  228.  
  229. BULLETS
  230.  
  231. ==============================================================================
  232. */
  233.  
  234. /*
  235. ================
  236. TraceAttack
  237. ================
  238. */
  239. void(float damage, vector dir) TraceAttack =
  240. {
  241.     local    vector    vel, org;
  242.     
  243.     vel = normalize(dir + v_up*crandom() + v_right*crandom());
  244.     vel = vel + 2*trace_plane_normal;
  245.     vel = vel * 200;
  246.  
  247.     org = trace_endpos - dir*4;
  248.  
  249.     if (trace_ent.takedamage)
  250.     {
  251.         SpawnBlood (org, vel*0.2, damage);
  252.         AddMultiDamage (trace_ent, damage);
  253.     }
  254.     else
  255.     {
  256.         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  257.         WriteByte (MSG_BROADCAST, TE_GUNSHOT);
  258.         WriteCoord (MSG_BROADCAST, org_x);
  259.         WriteCoord (MSG_BROADCAST, org_y);
  260.         WriteCoord (MSG_BROADCAST, org_z);
  261.     }
  262. };
  263.  
  264. /*
  265. ================
  266. FireBullets
  267.  
  268. Used by shotgun, super shotgun, and enemy soldier firing
  269. Go to the trouble of combining multiple pellets into a single damage call.
  270. ================
  271. */
  272. void(float shotcount, vector dir, vector spread) FireBullets =
  273. {
  274.     local    vector direction;
  275.     local    vector    src;
  276.     
  277.     makevectors(self.v_angle);
  278.  
  279.     src = self.origin + v_forward*10;
  280.     src_z = self.absmin_z + self.size_z * 0.7;
  281.  
  282.     ClearMultiDamage ();
  283.     while (shotcount > 0)
  284.     {
  285.         direction = dir + crandom()*spread_x*v_right + crandom()*spread_y*v_up;
  286.  
  287.         traceline (src, src + direction*2048, FALSE, self);
  288.         if (trace_fraction != 1.0)
  289.             TraceAttack (4, direction);
  290.  
  291.         shotcount = shotcount - 1;
  292.     }
  293.     ApplyMultiDamage ();
  294. };
  295.  
  296. /*
  297. ================
  298. W_FireShotgun
  299. ================
  300. */
  301. void() W_FireShotgun =
  302. {
  303.     local vector dir;
  304.  
  305.     sound (self, CHAN_WEAPON, "weapons/guncock.wav", 1, ATTN_NORM);    
  306.  
  307.     self.punchangle_x = -2;
  308.     
  309.     self.currentammo = self.ammo_shells = self.ammo_shells - 1;
  310.     dir = aim (self, 100000);
  311.     FireBullets (6, dir, '0.04 0.04 0');
  312. };
  313.  
  314.  
  315. /*
  316. ================
  317. W_FireSuperShotgun
  318. ================
  319. */
  320. void() W_FireSuperShotgun =
  321. {
  322.     local vector dir;
  323.  
  324.     if (self.currentammo == 1)
  325.     {
  326.         W_FireShotgun ();
  327.         return;
  328.     }
  329.         
  330.     sound (self ,CHAN_WEAPON, "weapons/shotgn2.wav", 1, ATTN_NORM);    
  331.  
  332.     self.punchangle_x = -4;
  333.     
  334.     self.currentammo = self.ammo_shells = self.ammo_shells - 2;
  335.     dir = aim (self, 100000);
  336.     FireBullets (14, dir, '0.14 0.08 0');
  337. };
  338.  
  339.  
  340. /*
  341. ==============================================================================
  342.  
  343. ROCKETS
  344.  
  345. ==============================================================================
  346. */
  347.  
  348. void()    s_explode1    =    [0,        s_explode2] {};
  349. void()    s_explode2    =    [1,        s_explode3] {};
  350. void()    s_explode3    =    [2,        s_explode4] {};
  351. void()    s_explode4    =    [3,        s_explode5] {};
  352. void()    s_explode5    =    [4,        s_explode6] {};
  353. void()    s_explode6    =    [5,        SUB_Remove] {};
  354.  
  355. void() BecomeExplosion =
  356. {
  357.     self.movetype = MOVETYPE_NONE;
  358.     self.velocity = '0 0 0';
  359.     self.touch = SUB_Null;
  360.     setmodel (self, "progs/s_explod.spr");
  361.     self.solid = SOLID_NOT;
  362.     s_explode1 ();
  363. };
  364.  
  365. void() T_MissileTouch =
  366. {
  367.     local float    damg;
  368.  
  369.     if (other == self.owner)
  370.         return;        // don't explode on owner
  371.  
  372.     if (pointcontents(self.origin) == CONTENT_SKY)
  373.     {
  374.         remove(self);
  375.         return;
  376.     }
  377.  
  378.     damg = 100 + random()*20;
  379.     
  380.     if (other.health)
  381.     {
  382.         if (other.classname == "monster_shambler")
  383.             damg = damg * 0.5;    // mostly immune
  384.         T_Damage (other, self, self.owner, damg );
  385.     }
  386.  
  387.     // don't do radius damage to the other, because all the damage
  388.     // was done in the impact
  389.     T_RadiusDamage (self, self.owner, 120, other);
  390.  
  391. //    sound (self, CHAN_WEAPON, "weapons/r_exp3.wav", 1, ATTN_NORM);
  392.     self.origin = self.origin - 8*normalize(self.velocity);
  393.  
  394.     WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  395.     WriteByte (MSG_BROADCAST, TE_EXPLOSION);
  396.     WriteCoord (MSG_BROADCAST, self.origin_x);
  397.     WriteCoord (MSG_BROADCAST, self.origin_y);
  398.     WriteCoord (MSG_BROADCAST, self.origin_z);
  399.  
  400.     BecomeExplosion ();
  401. };
  402.  
  403.  
  404.  
  405. /*
  406. ================
  407. W_FireRocket
  408. ================
  409. */
  410. void() W_FireRocket =
  411. {
  412.     local    entity missile, mpuff;
  413.     
  414.     self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  415.     
  416.     sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
  417.  
  418.     self.punchangle_x = -2;
  419.  
  420.     missile = spawn ();
  421.     missile.owner = self;
  422.     missile.movetype = MOVETYPE_FLYMISSILE;
  423.     missile.solid = SOLID_BBOX;
  424.  
  425. // set missile speed
  426.  
  427.     makevectors (self.v_angle);
  428.     missile.velocity = aim(self, 1000);
  429.     missile.velocity = missile.velocity * 1000;
  430.     missile.angles = vectoangles(missile.velocity);
  431.  
  432.     missile.touch = T_MissileTouch;
  433.  
  434. // set missile duration
  435.     missile.nextthink = time + 5;
  436.     missile.think = SUB_Remove;
  437.  
  438.     setmodel (missile, "progs/missile.mdl");
  439.     setsize (missile, '0 0 0', '0 0 0');
  440.     setorigin (missile, self.origin + v_forward*8 + '0 0 16');
  441. };
  442.  
  443. /*
  444. ===============================================================================
  445.  
  446. LIGHTNING
  447.  
  448. ===============================================================================
  449. */
  450.  
  451. /*
  452. =================
  453. LightningDamage
  454. =================
  455. */
  456. void(vector p1, vector p2, entity from, float damage) LightningDamage =
  457. {
  458.     local entity        e1, e2;
  459.     local vector        f;
  460.     
  461.     f = p2 - p1;
  462.     normalize (f);
  463.     f_x = 0 - f_y;
  464.     f_y = f_x;
  465.     f_z = 0;
  466.     f = f*16;
  467.  
  468.     e1 = e2 = world;
  469.  
  470.     traceline (p1, p2, FALSE, self);
  471.     if (trace_ent.takedamage)
  472.     {
  473.         particle (trace_endpos, '0 0 100', 225, damage*4);
  474.         T_Damage (trace_ent, from, from, damage);
  475.         if (self.classname == "player")
  476.         {
  477.             if (other.classname == "player")
  478.                 trace_ent.velocity_z = trace_ent.velocity_z + 400;
  479.         }
  480.     }
  481.     e1 = trace_ent;
  482.  
  483.     traceline (p1 + f, p2 + f, FALSE, self);
  484.     if (trace_ent != e1 && trace_ent.takedamage)
  485.     {
  486.         particle (trace_endpos, '0 0 100', 225, damage*4);
  487.         T_Damage (trace_ent, from, from, damage);
  488.     }
  489.     e2 = trace_ent;
  490.  
  491.     traceline (p1 - f, p2 - f, FALSE, self);
  492.     if (trace_ent != e1 && trace_ent != e2 && trace_ent.takedamage)
  493.     {
  494.         particle (trace_endpos, '0 0 100', 225, damage*4);
  495.         T_Damage (trace_ent, from, from, damage);
  496.     }
  497. };
  498.  
  499.  
  500. void() W_FireLightning =
  501. {
  502.     local    vector        org;
  503.  
  504.     if (self.ammo_cells < 1)
  505.     {
  506.         self.weapon = W_BestWeapon ();
  507.         W_SetCurrentAmmo ();
  508.         return;
  509.     }
  510.  
  511. // explode if under water
  512.     if (self.waterlevel > 1)
  513.     {
  514.         T_RadiusDamage (self, self, 35*self.ammo_cells, world);
  515.         self.ammo_cells = 0;
  516.         W_SetCurrentAmmo ();
  517.         return;
  518.     }
  519.  
  520.     if (self.t_width < time)
  521.     {
  522.         sound (self, CHAN_WEAPON, "weapons/lhit.wav", 1, ATTN_NORM);
  523.         self.t_width = time + 0.6;
  524.     }
  525.     self.punchangle_x = -2;
  526.  
  527.     self.currentammo = self.ammo_cells = self.ammo_cells - 1;
  528.  
  529.     org = self.origin + '0 0 16';
  530.     
  531.     traceline (org, org + v_forward*600, TRUE, self);
  532.  
  533.     WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  534.     WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
  535.     WriteEntity (MSG_BROADCAST, self);
  536.     WriteCoord (MSG_BROADCAST, org_x);
  537.     WriteCoord (MSG_BROADCAST, org_y);
  538.     WriteCoord (MSG_BROADCAST, org_z);
  539.     WriteCoord (MSG_BROADCAST, trace_endpos_x);
  540.     WriteCoord (MSG_BROADCAST, trace_endpos_y);
  541.     WriteCoord (MSG_BROADCAST, trace_endpos_z);
  542.  
  543.     LightningDamage (self.origin, trace_endpos + v_forward*4, self, 30);
  544. };
  545.  
  546.  
  547.  
  548. //=============================================================================
  549.  
  550. void() spike_touch;
  551. void() superspike_touch;
  552.  
  553.  
  554. /*
  555. ===============
  556. launch_spike
  557.  
  558. Used for both the player and the ogre
  559. ===============
  560. */
  561. void(vector org, vector dir) launch_spike =
  562. {
  563.     newmis = spawn ();
  564.     newmis.owner = self;
  565.     newmis.movetype = MOVETYPE_FLYMISSILE;
  566.     newmis.solid = SOLID_BBOX;
  567.  
  568.     newmis.angles = vectoangles(dir);
  569.     
  570.     newmis.touch = spike_touch;
  571.     newmis.classname = "spike";
  572.     newmis.think = SUB_Remove;
  573.     newmis.nextthink = time + 6;
  574.     setmodel (newmis, "progs/spike.mdl");
  575.     setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);        
  576.     setorigin (newmis, org);
  577.  
  578.     newmis.velocity = dir * 1000;
  579. };
  580.  
  581. void() W_FireSuperSpikes =
  582. {
  583.     local vector    dir;
  584.     local entity    old;
  585.     
  586.     sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
  587.     self.attack_finished = time + 0.2;
  588.     self.currentammo = self.ammo_nails = self.ammo_nails - 2;
  589.     dir = aim (self, 1000);
  590.     launch_spike (self.origin + '0 0 16', dir);
  591.     newmis.touch = superspike_touch;
  592.     setmodel (newmis, "progs/s_spike.mdl");
  593.     setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);        
  594.     self.punchangle_x = -2;
  595. };
  596.  
  597. void(float ox) W_FireSpikes =
  598. {
  599.     local vector    dir;
  600.     local entity    old;
  601.     
  602.     makevectors (self.v_angle);
  603.     
  604.     if (self.ammo_nails >= 2 && self.weapon == IT_SUPER_NAILGUN)
  605.     {
  606.         W_FireSuperSpikes ();
  607.         return;
  608.     }
  609.  
  610.     if (self.ammo_nails < 1)
  611.     {
  612.         self.weapon = W_BestWeapon ();
  613.         W_SetCurrentAmmo ();
  614.         return;
  615.     }
  616.  
  617.     sound (self, CHAN_WEAPON, "weapons/rocket1i.wav", 1, ATTN_NORM);
  618.     self.attack_finished = time + 0.2;
  619.     self.currentammo = self.ammo_nails = self.ammo_nails - 1;
  620.     dir = aim (self, 1000);
  621.     launch_spike (self.origin + '0 0 16' + v_right*ox, dir);
  622.  
  623.     self.punchangle_x = -2;
  624. };
  625.  
  626.  
  627.  
  628. .float hit_z;
  629. void() spike_touch =
  630. {
  631. local float rand;
  632.     if (other == self.owner)
  633.         return;
  634.  
  635.     if (other.solid == SOLID_TRIGGER)
  636.         return;    // trigger field, do nothing
  637.  
  638.     if (pointcontents(self.origin) == CONTENT_SKY)
  639.     {
  640.         remove(self);
  641.         return;
  642.     }
  643.  
  644. // hit something that bleeds
  645.     if (other.takedamage)
  646.     {
  647.         spawn_touchblood (9);
  648.         T_Damage (other, self, self.owner, 9);
  649.     }
  650.     else
  651.     {
  652.         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  653.         
  654.         if (self.classname == "wizspike")
  655.             WriteByte (MSG_BROADCAST, TE_WIZSPIKE);
  656.         else if (self.classname == "knightspike")
  657.             WriteByte (MSG_BROADCAST, TE_KNIGHTSPIKE);
  658.         else
  659.             WriteByte (MSG_BROADCAST, TE_SPIKE);
  660.         WriteCoord (MSG_BROADCAST, self.origin_x);
  661.         WriteCoord (MSG_BROADCAST, self.origin_y);
  662.         WriteCoord (MSG_BROADCAST, self.origin_z);
  663.     }
  664.  
  665.     remove(self);
  666.  
  667. };
  668.  
  669. void() superspike_touch =
  670. {
  671. local float rand;
  672.     if (other == self.owner)
  673.         return;
  674.  
  675.     if (other.solid == SOLID_TRIGGER)
  676.         return;    // trigger field, do nothing
  677.  
  678.     if (pointcontents(self.origin) == CONTENT_SKY)
  679.     {
  680.         remove(self);
  681.         return;
  682.     }
  683.  
  684. // hit something that bleeds
  685.     if (other.takedamage)
  686.     {
  687.         spawn_touchblood (18);
  688.         T_Damage (other, self, self.owner, 18);
  689.     }
  690.     else
  691.     {
  692.         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  693.         WriteByte (MSG_BROADCAST, TE_SUPERSPIKE);
  694.         WriteCoord (MSG_BROADCAST, self.origin_x);
  695.         WriteCoord (MSG_BROADCAST, self.origin_y);
  696.         WriteCoord (MSG_BROADCAST, self.origin_z);
  697.     }
  698.  
  699.     remove(self);
  700.  
  701. };
  702.  
  703. //=============================================================================
  704.  
  705. /*
  706. ============
  707. The standard bouncing grenade
  708. Well-known and -loved.
  709. ============
  710. */
  711. void() GrenadeExplode =
  712. {
  713.     T_RadiusDamage (self, self.owner, 120, world);
  714.  
  715.     WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  716.     WriteByte (MSG_BROADCAST, TE_EXPLOSION);
  717.     WriteCoord (MSG_BROADCAST, self.origin_x);
  718.     WriteCoord (MSG_BROADCAST, self.origin_y);
  719.     WriteCoord (MSG_BROADCAST, self.origin_z);
  720.  
  721.     BecomeExplosion ();
  722. };
  723.  
  724. void() GrenadeTouch =
  725. {
  726.     if (other == self.owner)
  727.         return;        // don't explode on owner
  728.     if (other.takedamage == DAMAGE_AIM)
  729.     {
  730.         GrenadeExplode();
  731.         return;
  732.     }
  733.     sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);    // bounce sound
  734.     if (self.velocity == '0 0 0')
  735.         self.avelocity = '0 0 0';
  736. };
  737.  
  738. /*
  739. ================
  740. W_FireNormalGrenade
  741. ================
  742. */
  743. void() W_FireNormalGrenade =
  744. {
  745.     local    entity missile, mpuff;
  746.  
  747.     self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  748.  
  749.     sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  750.  
  751.     self.punchangle_x = -2;
  752.  
  753.     missile = spawn ();
  754.     missile.owner = self;
  755.     missile.movetype = MOVETYPE_BOUNCE;
  756.     missile.solid = SOLID_BBOX;
  757.     missile.classname = "grenade";
  758.  
  759. // set missile speed
  760.  
  761.     makevectors (self.v_angle);
  762.  
  763.     if (self.v_angle_x)
  764.         missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
  765.     else
  766.     {
  767.         missile.velocity = aim(self, 10000);
  768.         missile.velocity = missile.velocity * 600;
  769.         missile.velocity_z = 200;
  770.     }
  771.  
  772.     missile.avelocity = '300 300 300';
  773.  
  774.     missile.angles = vectoangles(missile.velocity);
  775.     
  776.     missile.touch = GrenadeTouch;
  777.     
  778. // set missile duration
  779.     missile.nextthink = time + 2.5;
  780.     missile.think = GrenadeExplode;
  781.  
  782.     setmodel (missile, "progs/grenade.mdl");
  783.     setsize (missile, '0 0 0', '0 0 0');
  784.     setorigin (missile, self.origin);
  785. };
  786.  
  787.  
  788. /*
  789. =====================
  790. Nail Bomb Grenade
  791.  
  792. As seen in 'Eraser', it bounces to a stop, waits a few moments,
  793. pops up and explodes in a downward shower of nails.
  794. =====================
  795. */
  796.  
  797. void(vector org) launch_burst_spike =
  798. {
  799.     newmis = spawn ();
  800.     newmis.owner = self;
  801.     newmis.movetype = MOVETYPE_FLYMISSILE;
  802.     newmis.solid = SOLID_BBOX;
  803.  
  804.         newmis.velocity_x=(random()*2 - 1)*2000; //Highspeed, random direction
  805.         newmis.velocity_y=(random()*2 - 1)*2000;
  806.         newmis.velocity_z=(random() - 1)*2000;
  807.  
  808.     newmis.touch = superspike_touch;
  809.     newmis.classname = "spike";
  810.     newmis.think = SUB_Remove;
  811.     newmis.nextthink = time + 6;
  812.     setmodel (newmis, "progs/spike.mdl");
  813.     setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
  814.     setorigin (newmis, org);
  815.  
  816. };
  817.  
  818. void() NailBombBurst = // Explode in a shower of nails
  819. {
  820.  local float nnum = 0;
  821.  
  822.  // Bompf...
  823.  sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  824.  
  825.  // Turn into a fireball...
  826.  
  827.  WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);   // Light
  828.  WriteByte (MSG_BROADCAST, TE_EXPLOSION);     // Pixel-explosion
  829.  WriteCoord (MSG_BROADCAST, self.origin_x);
  830.  WriteCoord (MSG_BROADCAST, self.origin_y);
  831.  WriteCoord (MSG_BROADCAST, self.origin_z);
  832.  
  833.  self.movetype = MOVETYPE_NONE;
  834.  self.velocity = '0 0 0';
  835.  self.touch = SUB_Null;
  836.  setmodel (self, "progs/s_explod.spr");
  837.  self.solid = SOLID_NOT;
  838.  
  839.  // Throw lots of spikes in random directions
  840.  while (nnum < 50)
  841.  {
  842.   launch_burst_spike (self.origin);
  843.   nnum = nnum + 1;
  844.  }
  845.  
  846.  // Disappear
  847.  remove(self);
  848.  
  849. };
  850.  
  851. void() NailBombThink =
  852. // If enough time has passed, hop up and explode
  853. {
  854.         self.nextthink = time + 0.5;
  855.         self.think = NailBombThink;
  856.  
  857.         if (self.wait < time)
  858.         {
  859.          self.velocity_x=0;
  860.          self.velocity_y=0;
  861.          self.velocity_z=750;
  862.          if (self.flags & FL_ONGROUND) self.flags = self.flags - FL_ONGROUND;
  863.          self.nextthink = time + 0.2;
  864.          self.think = NailBombBurst;
  865.         }
  866. };
  867.  
  868. void() NailBombTouch =
  869. {
  870.     if (other == self.owner)
  871.         return;        // don't explode on owner
  872.     if (other.takedamage == DAMAGE_AIM)
  873.         return;
  874.     sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);    // bounce sound
  875.     if (self.velocity == '0 0 0')
  876.         self.avelocity = '0 0 0';
  877. };
  878.  
  879. void() W_FireNailBomb =
  880. {
  881.     local    entity missile, mpuff;
  882.  
  883.     sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  884.  
  885.     self.punchangle_x = -2;
  886.  
  887.     missile = spawn ();
  888.     missile.owner = self;
  889.     missile.movetype = MOVETYPE_BOUNCE;
  890.     missile.solid = SOLID_BBOX;
  891.     missile.classname = "grenade";
  892.         missile.wait = time + 2 + 5*random(); //blow up after a few seconds
  893.  
  894. // set missile speed
  895.  
  896.     makevectors (self.v_angle);
  897.  
  898.     if (self.v_angle_x)
  899.         missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
  900.     else
  901.     {
  902.         missile.velocity = aim(self, 10000);
  903.         missile.velocity = missile.velocity * 600;
  904.         missile.velocity_z = 200;
  905.     }
  906.  
  907.     missile.avelocity = '300 300 300';
  908.  
  909.     missile.angles = vectoangles(missile.velocity);
  910.  
  911.     missile.touch = NailBombTouch;
  912.  
  913.         self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  914.         missile.nextthink = time + 1; // Gets live real quick
  915.         missile.think = NailBombThink;
  916.  
  917.     setmodel (missile, "progs/grenade.mdl");
  918.     setsize (missile, '0 0 0', '0 0 0');
  919.     setorigin (missile, self.origin);
  920. };
  921.  
  922. /*
  923. ======================
  924. Bouncing cluster bomb
  925.  
  926. The grenade bursts into 5 sub-munitions, which hop about.
  927. If they reach near a target, they jump towards it and explode.
  928. They'll explode on their own sooner or later.
  929. PS: This lovely is what got me started - it's slightly modified (grenades
  930. hopped for ever) from the CBP Grenade, designed by:
  931.                 John Spickes jspickes@eng.umd.edu {JDS}
  932.                 Josh Spickes spickesj@wam.umd.edu
  933.                 Allen Seger
  934. Thanks, guys.
  935. =======================
  936. */
  937.  
  938. void() BCGrenadeThink =
  939. // Hop about, looking for a victim
  940. {
  941.         local entity head;
  942.         local vector dir;
  943.         if(self.flags & FL_ONGROUND) {
  944.         // Don't detect targets if I'm not on the ground
  945.                 head = findradius(self.origin, 300);
  946.                 while(head) {
  947.                         if (head.takedamage) {
  948.                                 if (self.avelocity == '0 0 0')
  949.                                         self.avelocity = '300 300 300';
  950.                                 self.origin_z = self.origin_z + 1;
  951.                                 dir = normalize(head.origin - self.origin);
  952.                                 self.velocity = dir*200 + '0 0 500';
  953.                                 if (self.flags & FL_ONGROUND)
  954.                                         self.flags = self.flags - FL_ONGROUND;
  955.  
  956.                                 self.nextthink = time + random()*1.5;
  957.                                 self.think = GrenadeExplode;
  958.                                 return;
  959.                         }
  960.                         head = head.chain;
  961.                 }
  962.         }
  963.         self.nextthink = time + 0.5;
  964.         self.think = BCGrenadeThink;
  965.         if(self.wait < time) // Time to jump
  966.         {
  967.          if (random() < 0.3) self.think = GrenadeExplode;
  968.          self.wait = time + 5 + 5*random();
  969.          if (self.avelocity == '0 0 0')
  970.                 self.avelocity = '300 300 300';
  971.      self.origin_z = self.origin_z + 1;
  972.          self.velocity_x=(random()*400 - 200);
  973.          self.velocity_y=(random()*400 - 200);
  974.          self.velocity_z = 500;
  975.          if (self.flags & FL_ONGROUND)
  976.                 self.flags = self.flags - FL_ONGROUND;
  977.         }
  978. };
  979.  
  980. void() BCGrenadeTouch =
  981. {
  982.     if (other == self.owner)
  983.         return;        // don't explode on owner
  984.     if (other.takedamage == DAMAGE_AIM)
  985.         return; // Don't explode on target impact
  986.     sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);    // bounce sound
  987.     if (self.velocity == '0 0 0')
  988.         self.avelocity = '0 0 0';
  989. };
  990.  
  991. void() BCGrenadeDeploy =
  992. // Spray out 5 submunitions
  993. {
  994.         local float gnum = 0;
  995.         local entity missile;
  996.  
  997.         while(gnum < 5) {
  998.                 missile = spawn ();
  999.                 missile.owner = self.owner;
  1000.                 missile.movetype = MOVETYPE_BOUNCE;
  1001.                 missile.solid = SOLID_BBOX;
  1002.                 missile.classname = "grenade";
  1003.                 missile.wait = time + 5 + 5*random();
  1004.                 missile.think = BCGrenadeThink;
  1005.                 missile.nextthink = time + 0.5;
  1006.                 missile.velocity_z = 500;
  1007.                 missile.velocity_x = 400*random() - 200;
  1008.                 missile.velocity_y = 400*random() - 200;
  1009.                 gnum = gnum + 1;
  1010.                 missile.avelocity = '300 300 300';
  1011.                 missile.angles = vectoangles(missile.velocity);
  1012.                 missile.touch = BCGrenadeTouch;
  1013.                 setmodel (missile, "progs/grenade.mdl");
  1014.                 setsize (missile, '0 0 0', '0 0 0');
  1015.                 setorigin (missile, self.origin);
  1016.         }
  1017.         self.think = GrenadeExplode;
  1018.         self.nextthink = time + 0.1;
  1019. };
  1020.  
  1021. void() W_FireBCGrenade =
  1022. {
  1023.     local    entity missile, mpuff;
  1024.  
  1025.     
  1026.     sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  1027.  
  1028.     self.punchangle_x = -2;
  1029.  
  1030.     missile = spawn ();
  1031.     missile.owner = self;
  1032.     missile.movetype = MOVETYPE_BOUNCE;
  1033.     missile.solid = SOLID_BBOX;
  1034.     missile.classname = "grenade";
  1035.         missile.wait = time + 5 + 5*random();
  1036.  
  1037. // set missile speed
  1038.  
  1039.     makevectors (self.v_angle);
  1040.  
  1041.     if (self.v_angle_x)
  1042.         missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
  1043.     else
  1044.     {
  1045.         missile.velocity = aim(self, 10000);
  1046.         missile.velocity = missile.velocity * 600;
  1047.         missile.velocity_z = 200;
  1048.     }
  1049.  
  1050.     missile.avelocity = '300 300 300';
  1051.  
  1052.     missile.angles = vectoangles(missile.velocity);
  1053.  
  1054.     missile.touch = BCGrenadeTouch;
  1055.  
  1056.         // If we have enough for a cluster, fire a cluster, otherwise
  1057.         // fire a bouncer.
  1058.         if (self.ammo_rockets > 4) {
  1059.                 self.currentammo = self.ammo_rockets = self.ammo_rockets - 5;
  1060.                 missile.nextthink = time + 2+(random()*3);
  1061.                 missile.think = BCGrenadeDeploy;
  1062.         }
  1063.         else {
  1064.                 self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  1065.                 missile.think = BCGrenadeThink;
  1066.                 missile.nextthink = time + 2+(random()*2);
  1067.         }
  1068.  
  1069.     setmodel (missile, "progs/grenade.mdl");
  1070.     setsize (missile, '0 0 0', '0 0 0');
  1071.     setorigin (missile, self.origin);
  1072. };
  1073.  
  1074. /*
  1075. =====================
  1076. Ricochet grenade
  1077.  
  1078. Flies at high speed and bounces for a fair while before exploding.
  1079. Unless it hits somebody first >:)
  1080. ======================
  1081. */
  1082. void() RicochetTouch =
  1083. {
  1084.     if (other == self.owner)
  1085.         return;        // don't explode on owner
  1086.     if (other.takedamage == DAMAGE_AIM)
  1087.     {
  1088.         GrenadeExplode();
  1089.         return;
  1090.     }
  1091.     sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);    // bounce sound
  1092.  
  1093.         self.velocity = self.velocity * 1.2; // Keep your speed
  1094.  
  1095.     if (self.velocity == '0 0 0')
  1096.         self.avelocity = '0 0 0';
  1097. };
  1098.  
  1099.  
  1100. void() W_FireRicochet =
  1101. {
  1102.     local    entity missile, mpuff;
  1103.  
  1104.     self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  1105.  
  1106.     sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  1107.  
  1108.     self.punchangle_x = -2;
  1109.  
  1110.     missile = spawn ();
  1111.     missile.owner = self;
  1112.         missile.movetype = MOVETYPE_BOUNCE;
  1113.     missile.solid = SOLID_BBOX;
  1114.  
  1115. // set missile speed
  1116.  
  1117.     makevectors (self.v_angle);
  1118.     missile.velocity = aim(self, 1000);
  1119.     missile.velocity = missile.velocity * 1000;
  1120.         missile.velocity_z = 200;
  1121.     missile.angles = vectoangles(missile.velocity);
  1122.  
  1123.     missile.nextthink = time + 5 + random()*5;
  1124.     missile.think = GrenadeExplode;
  1125.         missile.touch = RicochetTouch;
  1126.  
  1127.     setmodel (missile, "progs/grenade.mdl");
  1128.     setsize (missile, '0 0 0', '0 0 0');
  1129.     setorigin (missile, self.origin);
  1130. };
  1131.  
  1132. /*
  1133. =====================
  1134. Glue bomb
  1135.  
  1136. Flies straight like a missile and sticks to the target, possibly floating
  1137. in the air.
  1138. Then it blows.
  1139. ======================
  1140. */
  1141.  
  1142. void() GlueStick =
  1143. {
  1144.  if (random() < 0.02) // Check if it blows
  1145.  {
  1146.   self.think = GrenadeExplode;  // Boom
  1147.   self.touch = GrenadeExplode;
  1148.  }
  1149.  else
  1150.  {
  1151.   self.think = GlueStick;       // Keep sticking
  1152.   self.touch = GlueStick;
  1153.  }
  1154.  
  1155.   if ((self.enemy != world) && (self.enemy.health > 1))
  1156.   {
  1157.    self.origin=self.enemy.origin; // stick with the target
  1158.    if (self.velocity == '0 0 0') self.avelocity = '0 0 0';
  1159.   }
  1160.   self.nextthink = time + 0.1;
  1161.  
  1162. };
  1163.  
  1164. void() GlueTouch =
  1165. {
  1166.     if (other == self.owner)
  1167.         return;        // don't stick to owner
  1168.  
  1169.     sound (self, CHAN_WEAPON, "misc/outwater.wav", 1, ATTN_NORM);
  1170.         self.think = GlueStick;
  1171.         self.touch = GlueStick;
  1172.         self.nextthink = time + 0.1;
  1173.         self.velocity = self.velocity * 0; // Stop right there (floats)
  1174.     self.avelocity = '0 0 0';
  1175.         self.enemy = other;
  1176. };
  1177.  
  1178. void() W_FireGlue =
  1179. {
  1180.     local    entity missile, mpuff;
  1181.  
  1182.     self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  1183.  
  1184.     sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  1185.  
  1186.     self.punchangle_x = -2;
  1187.  
  1188.     missile = spawn ();
  1189.     missile.owner = self;
  1190.         missile.movetype = MOVETYPE_FLYMISSILE;
  1191.     missile.solid = SOLID_BBOX;
  1192.  
  1193. // set missile speed
  1194.  
  1195.     makevectors (self.v_angle);
  1196.     missile.velocity = aim(self, 1000);
  1197.     missile.velocity = missile.velocity * 1000;
  1198.     missile.angles = vectoangles(missile.velocity);
  1199.  
  1200.     missile.nextthink = time + 5; // Blow after 5s uninterrupted flight
  1201.     missile.think = GrenadeExplode;
  1202.         missile.touch = GlueTouch;
  1203.  
  1204.     setmodel (missile, "progs/grenade.mdl");
  1205.     setsize (missile, '0 0 0', '0 0 0');
  1206.     setorigin (missile, self.origin);
  1207. };
  1208.  
  1209. /*
  1210. =====================
  1211. MG Point Defense
  1212.  
  1213. Gets live after a few seconds, turns into a nailgun,
  1214. shooting at everything in sight. Explodes after a while.
  1215. =====================
  1216. */
  1217.  
  1218. void(vector org, vector d) MGShoot =
  1219. // Shoot once in direction d
  1220. {
  1221.   sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
  1222.   launch_spike (org + '0 0 16', d);
  1223.   newmis.touch = superspike_touch;
  1224.   setmodel (newmis, "progs/spike.mdl");
  1225.   setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
  1226. };
  1227.  
  1228. void() MGThink =
  1229. // Scan for anything alive
  1230. {
  1231.         local entity head;
  1232.         local vector dir;
  1233.  
  1234.         self.nextthink = time + 1; // 1s pause between target searches
  1235.         self.think = MGThink;
  1236.  
  1237.         head = findradius(self.origin, 1500); // Look for opponents
  1238.         while(head)  // found something
  1239.         {
  1240.          if (head.takedamage)  // can be damaged
  1241.          {
  1242.           if ((self.owner != head) && (head.classname != "monster_zombie")) // Not owner or zombie
  1243.           {
  1244.            if ((coop && (head.classname == "player")) != 1) // Not player in coop
  1245.            {
  1246.             if (visible(head) == 1) // Can see it!
  1247.             {
  1248.              dir = normalize(head.origin - self.origin); // Get direction
  1249.              self.ideal_yaw = vectoyaw(head.origin - self.origin);
  1250.              self.angles_y = self.ideal_yaw;
  1251.            self.angles_x = 0;
  1252.            self.angles_z = 0;
  1253.  
  1254.              MGShoot (self.origin,dir); // Shoot at it
  1255.             }
  1256.            }
  1257.           }
  1258.          }
  1259.          head = head.chain; // next
  1260.         }
  1261.  
  1262.         if (self.wait < time) // self-destruct
  1263.         {
  1264.          self.nextthink = time + 0.2;
  1265.          self.think = GrenadeExplode;
  1266.         }
  1267. };
  1268.  
  1269. void() MGDeploy =
  1270. // Turn into an MG.
  1271. // Since I can't build an MG shape yet, I'm using a standard player head
  1272. {
  1273.  setmodel (self, "progs/h_player.mdl");
  1274.  self.angles = '0 0 0';
  1275.  sound (self, CHAN_WEAPON, "misc/outwater.wav", 1, ATTN_NORM);
  1276.  
  1277.  self.think = MGThink;
  1278.  self.nextthink = time + random()*2;
  1279.  self.wait = time + 20 + random()*20; // Active lifespan
  1280. };
  1281.  
  1282. void() MGTouch =
  1283. {
  1284.     if (other == self.owner)
  1285.         return;        // don't explode on owner
  1286.     if (other.takedamage == DAMAGE_AIM)
  1287.         return;
  1288.     sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);    // bounce sound
  1289.     if (self.velocity == '0 0 0')
  1290.         self.avelocity = '0 0 0';
  1291. };
  1292.  
  1293. void() W_FireMG =
  1294. {
  1295.     local    entity missile, mpuff;
  1296.  
  1297.     sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  1298.  
  1299.     self.punchangle_x = -2;
  1300.  
  1301.     missile = spawn ();
  1302.     missile.owner = self;
  1303.     missile.movetype = MOVETYPE_BOUNCE;
  1304.     missile.solid = SOLID_BBOX;
  1305.     missile.classname = "grenade";
  1306.         missile.wait = time + 2 + 5*random(); //blow up after a few seconds
  1307.  
  1308. // set missile speed
  1309.  
  1310.     makevectors (self.v_angle);
  1311.  
  1312.     if (self.v_angle_x)
  1313.         missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
  1314.     else
  1315.     {
  1316.         missile.velocity = aim(self, 10000);
  1317.         missile.velocity = missile.velocity * 600;
  1318.         missile.velocity_z = 200;
  1319.     }
  1320.  
  1321.     missile.avelocity = '300 300 300';
  1322.  
  1323.     missile.angles = vectoangles(missile.velocity);
  1324.  
  1325.     missile.touch = MGTouch;
  1326.  
  1327.         self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  1328.         missile.nextthink = time + 2 + random()*5; // Takes a few seconds to deploy
  1329.         missile.think = MGDeploy;
  1330.  
  1331.     setmodel (missile, "progs/grenade.mdl");
  1332.     setsize (missile, '0 0 0', '0 0 0');
  1333.     setorigin (missile, self.origin);
  1334. };
  1335.  
  1336. /*
  1337. ===============
  1338. Vampire Bolt
  1339.  
  1340. Gives YOU the HP the opponent lost by being hit with this
  1341. ================
  1342. */
  1343. void() VampTouch =
  1344. {
  1345.     local float    damg;
  1346.         local float     oldhealth,healthgained;
  1347.         local string    s;
  1348.  
  1349.     if (other == self.owner)
  1350.         return;        // don't explode on owner
  1351.  
  1352.     if (pointcontents(self.origin) == CONTENT_SKY)
  1353.     {
  1354.         remove(self);
  1355.         return;
  1356.     }
  1357.  
  1358.     damg = 5 + random()*15;
  1359.  
  1360.     if ((other.health) && (other.classname == "player"))
  1361.     {
  1362.          oldhealth = other.health;
  1363.          T_Damage (other, self, self.owner, damg );
  1364.          spawn_touchblood(damg);
  1365.          if (other.health <= 0) { healthgained = oldhealth; }
  1366.                            else { healthgained = oldhealth-other.health; }
  1367.          T_Heal(self.owner,healthgained,0);
  1368.          sprint(self.owner, "You drain ");
  1369.      s = ftos(healthgained);
  1370.      sprint(self.owner, s);
  1371.      sprint(self.owner, " health\n");
  1372.          // Tell victim about it
  1373.          sprint(other,self.owner.netname);
  1374.          sprint(other," drains ");
  1375.          sprint(other, s);
  1376.      sprint(other, " health from you\n");
  1377.     }
  1378.  
  1379.         remove(self);
  1380. };
  1381.  
  1382.  
  1383. void() W_FireVamp =
  1384. {
  1385.     local    entity missile, mpuff;
  1386.  
  1387.     self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  1388.  
  1389.     sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
  1390.  
  1391.     self.punchangle_x = -2;
  1392.  
  1393.     missile = spawn ();
  1394.     missile.owner = self;
  1395.     missile.movetype = MOVETYPE_FLYMISSILE;
  1396.     missile.solid = SOLID_BBOX;
  1397.  
  1398. // set missile speed
  1399.  
  1400.     makevectors (self.v_angle);
  1401.     missile.velocity = aim(self, 1000);
  1402.     missile.velocity = missile.velocity * 1000;
  1403.     missile.angles = vectoangles(missile.velocity);
  1404.  
  1405.     missile.touch = VampTouch;
  1406.  
  1407. // set missile duration
  1408.     missile.nextthink = time + 5;
  1409.     missile.think = SUB_Remove;
  1410.  
  1411.     setmodel (missile, "progs/grenade.mdl");
  1412.     setsize (missile, '0 0 0', '0 0 0');
  1413.     setorigin (missile, self.origin + v_forward*8 + '0 0 16');
  1414. };
  1415.  
  1416. /*
  1417. ==========
  1418. Medic Bolt
  1419.  
  1420. Heals the target by a small amount
  1421. ==========
  1422. */
  1423. void() MedicTouch =
  1424. {
  1425.         local string    s;
  1426.         local float     heal_p,oldhealth;
  1427.  
  1428.     if (other == self.owner)
  1429.         return;        // don't explode on owner
  1430.  
  1431.     if (pointcontents(self.origin) == CONTENT_SKY)
  1432.     {
  1433.         remove(self);
  1434.         return;
  1435.     }
  1436.         if (other.classname != "player") { remove(self); return; }
  1437.  
  1438.     heal_p = 5 + random()*15;
  1439.  
  1440.     if (other.health)
  1441.     {
  1442.          oldhealth = other.health;
  1443.          T_Heal(other,heal_p,0);
  1444.          heal_p = other.health - oldhealth;
  1445.          sprint(self.owner, "You heal ");
  1446.      s = ftos(heal_p);
  1447.      sprint(self.owner, s);
  1448.      sprint(self.owner, " health\n");
  1449.          sprint(other,self.owner.netname);
  1450.          sprint(other," heals ");
  1451.          sprint(other, s);
  1452.          sprint(other, " health for you\n");
  1453.     }
  1454.  
  1455.         remove(self);
  1456. };
  1457.  
  1458. void() W_FireMedic =
  1459. {
  1460.     local    entity missile, mpuff;
  1461.  
  1462.     self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  1463.  
  1464.     sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
  1465.  
  1466.     self.punchangle_x = -2;
  1467.  
  1468.     missile = spawn ();
  1469.     missile.owner = self;
  1470.     missile.movetype = MOVETYPE_FLYMISSILE;
  1471.     missile.solid = SOLID_BBOX;
  1472.  
  1473. // set missile speed
  1474.  
  1475.     makevectors (self.v_angle);
  1476.     missile.velocity = aim(self, 1000);
  1477.     missile.velocity = missile.velocity * 1000;
  1478.     missile.angles = vectoangles(missile.velocity);
  1479.  
  1480.     missile.touch = MedicTouch;
  1481.  
  1482. // set missile duration
  1483.     missile.nextthink = time + 5;
  1484.     missile.think = SUB_Remove;
  1485.  
  1486.     setmodel (missile, "progs/grenade.mdl");
  1487.     setsize (missile, '0 0 0', '0 0 0');
  1488.     setorigin (missile, self.origin + v_forward*8 + '0 0 16');
  1489. };
  1490.  
  1491. /*
  1492. ==========
  1493. Armor Piercing
  1494.  
  1495. Goes straight through the targets until it hits a wall
  1496. ==========
  1497. */
  1498.  
  1499. void() APRemove =
  1500. {
  1501.  WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);  // Piiing
  1502.  WriteByte (MSG_BROADCAST, TE_SUPERSPIKE);
  1503.  WriteCoord (MSG_BROADCAST, self.origin_x);
  1504.  WriteCoord (MSG_BROADCAST, self.origin_y);
  1505.  WriteCoord (MSG_BROADCAST, self.origin_z);
  1506.  remove(self);
  1507. };
  1508.  
  1509. void() APTouch =
  1510. {
  1511.  local float dam;
  1512.  
  1513.     if (other == self.owner)
  1514.         return;        // don't explode on owner
  1515.  
  1516.     if (pointcontents(self.origin) == CONTENT_SKY)
  1517.     {
  1518.         remove(self);
  1519.         return;
  1520.     }
  1521.  
  1522.         if (other.takedamage) // damage it
  1523.         {
  1524.          dam = 50 + random()*20;
  1525.          spawn_touchblood(dam);
  1526.          T_Damage (other, self, self.owner, dam );
  1527.         }
  1528.         else
  1529.         {
  1530.          APRemove; // Disappear against the wall
  1531.         }
  1532.  
  1533. };
  1534.  
  1535. void() W_FireAP =
  1536. {
  1537.     local    entity missile, mpuff;
  1538.  
  1539.     self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  1540.  
  1541.     sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
  1542.  
  1543.     self.punchangle_x = -2;
  1544.  
  1545.     missile = spawn ();
  1546.     missile.owner = self;
  1547.     missile.movetype = MOVETYPE_FLY;
  1548.     missile.solid = SOLID_TRIGGER;
  1549.  
  1550. // set missile speed
  1551.  
  1552.     makevectors (self.v_angle);
  1553.     missile.velocity = aim(self, 1000);
  1554.     missile.velocity = missile.velocity * 1000;
  1555.  
  1556.     missile.angles = vectoangles(missile.velocity);
  1557.  
  1558.     missile.touch = APTouch;
  1559.  
  1560. // set missile duration
  1561.     missile.nextthink = time + 5;   // Disappear after 5s
  1562.     missile.think = SUB_Remove;
  1563.  
  1564.     setmodel (missile, "progs/spike.mdl");
  1565.     setsize (missile, '0 0 0', '0 0 0');
  1566.     setorigin (missile, self.origin + v_forward*8 + '0 0 16');
  1567. };
  1568.  
  1569. /*
  1570. ==============
  1571. Radio Tag Bomb
  1572.  
  1573. Is detonated by command only and can be disarmed by command as well.
  1574. If it hits a valid target, it'll stick to it and can be blown at
  1575. any time. Otherwise, it acts like the pipe bomb from Duke3D.
  1576. ==============
  1577. */
  1578.  
  1579. void() DetonateRTB =
  1580. // Detonate all owned RTBs in range (10000)
  1581. {
  1582.  local entity head;
  1583.  
  1584.  head = findradius (self.origin, 10000); // Search
  1585.  while(head) // Found something
  1586.  {
  1587.   if((head.classname == "RTB") && (head.owner == self)) // It's a bomb, belonging to us
  1588.   {
  1589.    head.think = GrenadeExplode; // Tell it to blow up...
  1590.    head.nextthink = time; // ...right away
  1591.   }
  1592.   head = head.chain; // Check next object
  1593.  }
  1594. };
  1595.  
  1596. void() DisarmRTB =
  1597. // Disarm all owned RTBs in range (10000)
  1598. {
  1599.  local entity head;
  1600.  
  1601.  head = findradius (self.origin, 10000); // Start looking
  1602.  while(head) // Found an object
  1603.  {
  1604.   if((head.classname == "RTB") && (head.owner == self)) // It's our RTB
  1605.   {
  1606.    sprint (self,"You untagged "); // Notify owner
  1607.    if (head.enemy.classname == "player")
  1608.    {
  1609.     sprint (self,head.enemy.netname);
  1610.     sprint (self,"\n");
  1611.     sprint (head.enemy,self.netname); // Tell target
  1612.     sprint (head.enemy," untagged his RTB\n");
  1613.    }
  1614.    else // Not a human player; return name of monster
  1615.    {
  1616.     sprint (self, "a ");
  1617.     sprint (self, head.enemy.classname);
  1618.     sprint (self,"\n");
  1619.    }
  1620.    head.movetype = MOVETYPE_NONE; // Remove yourself immediately
  1621.    head.velocity = '0 0 0';
  1622.    head.touch = SUB_Null;
  1623.    head.think = SUB_Remove;
  1624.    head.nextthink = time;
  1625.    }
  1626.   head = head.chain; // Check next
  1627.   }
  1628.  return;
  1629. };
  1630.  
  1631. void() TaggedRTBTouch =
  1632. {
  1633.  self.origin = self.enemy.origin; // stick with enemy
  1634.  if (self.velocity == '0 0 0') self.avelocity = '0 0 0';
  1635. };
  1636.  
  1637. void() RTBTouch =
  1638. {
  1639.  if (other == self.owner) return; // don't explode on owner
  1640.  
  1641.  if (other.takedamage == DAMAGE_AIM)
  1642.  {
  1643.   if (!self.enemy)
  1644.   {
  1645.    sound(self, CHAN_WEAPON, "weapons/tink1.wav", 1, ATTN_NORM);
  1646.    self.think = GrenadeExplode;
  1647.   }
  1648.   self.enemy = other;
  1649.   self.origin = self.enemy.origin; // Stick to target
  1650.   if (self.effects != EF_DIMLIGHT) // We're not glowing yet
  1651.   {
  1652.    sprint (self.owner, "You tagged "); // Tell owner about it
  1653.    if (self.enemy.classname == "player") // Tagged an opponent
  1654.    {
  1655.     sprint (self.owner, self.enemy.netname);
  1656.     sprint (self.owner, "\n");
  1657.     sprint (self.enemy, "You have been tagged by "); // tell opponent
  1658.     sprint (self.enemy, self.owner.netname);
  1659.     sprint (self.enemy, "\n");
  1660.    }
  1661.    else // Not human player; get name of monster
  1662.    {
  1663.     sprint (self.owner, "a ");
  1664.     sprint (self.owner, self.enemy.classname);
  1665.     sprint (self.owner, "\n");
  1666.    }
  1667.    self.effects = EF_DIMLIGHT; // Start glowing
  1668.    self.think = TaggedRTBTouch;
  1669.    self.touch = TaggedRTBTouch;
  1670.    self.nextthink = time;
  1671.   }
  1672.  }
  1673.  sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);  // bounce sound
  1674.  if (self.velocity == '0 0 0') self.avelocity = '0 0 0';
  1675. };
  1676.  
  1677. void() W_FireRTB =
  1678. {
  1679.     local    entity missile, mpuff;
  1680.  
  1681.  
  1682.     sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  1683.  
  1684.     self.punchangle_x = -2;
  1685.  
  1686.     missile = spawn ();
  1687.     missile.owner = self;
  1688.     missile.movetype = MOVETYPE_BOUNCE;
  1689.     missile.solid = SOLID_BBOX;
  1690.     missile.classname = "RTB"; // Radio Tag Bomb
  1691.         missile.wait = time + 5 + 5*random();
  1692.  
  1693.     makevectors (self.v_angle);
  1694.  
  1695.     if (self.v_angle_x)
  1696.         missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
  1697.     else
  1698.     {
  1699.         missile.velocity = aim(self, 10000);
  1700.         missile.velocity = missile.velocity * 600;
  1701.         missile.velocity_z = 200;
  1702.     }
  1703.  
  1704.     missile.avelocity = '300 300 300';
  1705.  
  1706.     missile.angles = vectoangles(missile.velocity);
  1707.  
  1708.     missile.touch = RTBTouch;
  1709.  
  1710.         self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  1711.         missile.think = GrenadeExplode;
  1712.         missile.nextthink = time + 10; // Explode if didn't touch anything after 10s
  1713.  
  1714.     setmodel (missile, "progs/grenade.mdl");
  1715.     setsize (missile, '0 0 0', '0 0 0');
  1716.     setorigin (missile, self.origin);
  1717. };
  1718.  
  1719. /*
  1720. ==========
  1721. Ammo Cache
  1722.  
  1723. Grenade turns into backpack which contains 20 of each ammo, if the
  1724. user has as much. Useful for feeding your buddies or building caches.
  1725. ==========
  1726. */
  1727.  
  1728. void() AmmoCacheDeploy =
  1729. // Turn into backpack
  1730. {
  1731.  self.flags = FL_ITEM;
  1732.  self.solid = SOLID_TRIGGER;
  1733.  self.movetype = MOVETYPE_TOSS;
  1734.  
  1735.  self.origin_z = self.origin_z + 20; // Pop up a bit
  1736.  self.angles = self.angles * 0; // Stand upright
  1737.  setmodel (self, "progs/backpack.mdl");
  1738.  setsize(self, '-16 -16 0', '16 16 56');
  1739.  sound (self, CHAN_WEAPON, "misc/outwater.wav", 1, ATTN_NORM); // Deploy sound
  1740.  self.touch = BackpackTouch;
  1741.  self.nextthink = time + 1200;    // remove after 20 minutes
  1742.  self.think = SUB_Remove;
  1743. };
  1744.  
  1745. void() AmmoCacheTouch =
  1746. {
  1747.     if (other == self.owner) return; // don't bounce off owner
  1748.     sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);    // bounce sound
  1749.     if (self.velocity == '0 0 0') self.avelocity = '0 0 0';
  1750. };
  1751.  
  1752. void() W_FireAmmoCache =
  1753. {
  1754.     local    entity missile, mpuff;
  1755.  
  1756.     if ( (self.ammo_shells + self.ammo_nails + self.ammo_rockets + self.ammo_cells) <= 1)
  1757.         return; // Not enough ammo
  1758.  
  1759.     sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  1760.  
  1761.     self.punchangle_x = -2;
  1762.  
  1763.     missile = spawn ();
  1764.     missile.owner = self;
  1765.     missile.movetype = MOVETYPE_BOUNCE;
  1766.     missile.solid = SOLID_BBOX;
  1767.     missile.classname = "grenade";
  1768.         missile.wait = time + 5 + 5*random();
  1769.  
  1770.         // Load with ammo
  1771.         if (self.ammo_shells >= 20)
  1772.     {
  1773.      missile.ammo_shells = 20;
  1774.      self.ammo_shells = self.ammo_shells - 20;
  1775.     }
  1776.     else
  1777.     {
  1778.      missile.ammo_shells = self.ammo_shells;
  1779.      self.ammo_shells = 0;
  1780.     }
  1781.  
  1782.     if (self.ammo_nails >= 20)
  1783.     {
  1784.      missile.ammo_nails = 20;
  1785.      self.ammo_nails = self.ammo_nails - 20;
  1786.     }
  1787.     else
  1788.     {
  1789.      missile.ammo_nails = self.ammo_nails;
  1790.      self.ammo_nails = 0;
  1791.     }
  1792.     if (self.ammo_rockets >= 10)
  1793.     {
  1794.      missile.ammo_rockets = 10;
  1795.      self.ammo_rockets = self.ammo_rockets - 10;
  1796.     }
  1797.     else
  1798.     {
  1799.      missile.ammo_rockets = self.ammo_rockets;
  1800.      self.ammo_rockets = 0;
  1801.     }
  1802.  
  1803.     if (self.ammo_cells >= 20)
  1804.     {
  1805.      missile.ammo_cells = 20;
  1806.      self.ammo_cells = self.ammo_cells - 20;
  1807.     }
  1808.     else
  1809.     {
  1810.      missile.ammo_cells = self.ammo_cells;
  1811.      self.ammo_cells = 0;
  1812.     }
  1813.  
  1814.     makevectors (self.v_angle);
  1815.  
  1816.     if (self.v_angle_x)
  1817.         missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
  1818.     else
  1819.     {
  1820.         missile.velocity = aim(self, 10000);
  1821.         missile.velocity = missile.velocity * 600;
  1822.         missile.velocity_z = 200;
  1823.     }
  1824.  
  1825.     missile.avelocity = '300 300 300';
  1826.  
  1827.     missile.angles = vectoangles(missile.velocity);
  1828.  
  1829.     missile.touch = AmmoCacheTouch;
  1830.  
  1831.         self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  1832.         missile.think = AmmoCacheDeploy;
  1833.         missile.nextthink = time + 2 + random()*5; // Deploy after a few seconds
  1834.  
  1835.     setmodel (missile, "progs/grenade.mdl");
  1836.     setsize (missile, '0 0 0', '0 0 0');
  1837.     setorigin (missile, self.origin);
  1838. };
  1839.  
  1840. /*
  1841. ===============
  1842. Homing Missiles
  1843.  
  1844. These are quite smart, flying through the area looking for a target (not you).
  1845. Code is from Iikka Paavolainen (ipaavola@cc.hut.fi), who wrote the
  1846. GeniusMissile patch. This is a VERY impressive piece of code, and I only
  1847. changed little of it (missile search radius much smaller for speed and
  1848. interestng search pattern).
  1849.  
  1850. As soon as I have a bit of time, I'll work out exactly what this thing
  1851. does and comment it.
  1852. ===============
  1853. */
  1854.  
  1855. void()   HomeThink;
  1856. entity() HomeFindTarget;
  1857. float(entity targ) visible;
  1858. float(entity targ) infront;
  1859.  
  1860. void() W_FireHomingMissile =
  1861. {
  1862.     local    entity missile, mpuff;
  1863.  
  1864.         self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  1865.  
  1866.     sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
  1867.  
  1868.     self.punchangle_x = -2;
  1869.  
  1870.     missile = spawn ();
  1871.     missile.owner = self;
  1872.     missile.movetype = MOVETYPE_FLYMISSILE;
  1873.     missile.solid = SOLID_BBOX;
  1874.  
  1875. // set missile speed
  1876.  
  1877.     makevectors (self.v_angle);
  1878.     missile.velocity = aim(self, 1000);
  1879.     missile.velocity = missile.velocity * 300;
  1880.     missile.angles = vectoangles(missile.velocity);
  1881.  
  1882.     missile.touch = T_MissileTouch;
  1883.  
  1884. // set missile duration
  1885. //    missile.nextthink = time + 30;
  1886. //    missile.think = SUB_Remove;
  1887.  
  1888.         missile.nextthink = time + 0.2;
  1889.     missile.think = HomeThink;
  1890.     missile.enemy = world;
  1891.         missile.ltime=time;
  1892.  
  1893.     setmodel (missile, "progs/missile.mdl");
  1894.     setsize (missile, '0 0 0', '0 0 0');
  1895.     setorigin (missile, self.origin + v_forward*8 + '0 0 16');
  1896. };
  1897.  
  1898. entity() HomeFindTarget =
  1899. {
  1900.     local entity head, selected;
  1901.     local float dist;
  1902.     dist = 100000;
  1903.     selected = world;
  1904.     head = findradius(self.origin, 1500);
  1905.     while(head)
  1906.     {
  1907.                 if( (head.health > 1) && (head != self) && (head != self.owner) && head.classname != "door")
  1908.         {
  1909.                  if ((coop && (head.classname == "player")) != 1) // Not player in coop
  1910.                  {
  1911.             traceline(self.origin,head.origin,TRUE,self);
  1912.             if ( (trace_fraction >= 1) && (vlen(head.origin - self.origin) < dist) )
  1913.             {
  1914.                 selected = head;
  1915.                 dist = vlen(head.origin - self.origin);
  1916.             }
  1917.                  }
  1918.         }
  1919.         head = head.chain;
  1920.     }
  1921.         if (selected != world)
  1922.     {
  1923.         sprint (self.owner,"Homing->");
  1924.         if (selected.classname == "player")
  1925.         {
  1926.             sprint (self.owner,selected.netname);
  1927.             sprint (selected,self.owner.netname);
  1928.             sprint (selected," has a bogey on you!\n");
  1929.         }
  1930.         else
  1931.             sprint (self.owner,selected.classname);
  1932.         sprint (self.owner,"\n");
  1933.     }
  1934.     return selected;
  1935. };
  1936.  
  1937. void() HomeThink =
  1938. {
  1939.         local vector dir, vtemp, tm;
  1940.         local float r,dist;
  1941.         dist=100;
  1942.         if(time-self.ltime>=20)
  1943.         {
  1944.                 sprint(self.owner,"Missile self-destructing.\n");
  1945.                 self.nextthink=time+0.1;
  1946.                 self.think=SUB_Remove;
  1947.                 return;
  1948.         }
  1949.         traceline(self.origin,self.enemy.origin,TRUE,self);
  1950.         if ( trace_fraction<1.0 || !(self.enemy) || (self.enemy == world) || (self.enemy.health < 1) )
  1951.         self.enemy = HomeFindTarget();
  1952.  
  1953.         if (self.enemy != world)
  1954.     {
  1955.         vtemp = self.enemy.origin + '0 0 10';
  1956.         dir = normalize(vtemp - self.origin);
  1957.                 self.velocity = dir * 300;
  1958.         self.angles = vectoangles(self.velocity);
  1959.     }
  1960.         else
  1961.         {
  1962.                 makevectors(self.angles);
  1963.                 traceline(self.origin,self.origin+v_forward*dist,TRUE,self);
  1964.                 if(trace_fraction<1.0)
  1965.                 {
  1966.                         traceline(self.origin,self.origin+v_right*dist,TRUE,self);
  1967.                         if(trace_fraction==1.0)
  1968.                                 self.velocity=v_right*300;
  1969.                         else
  1970.                         {
  1971.                                 traceline(self.origin,self.origin-v_right*dist,TRUE,self);
  1972.                                 if(trace_fraction==1.0)
  1973.                                         self.velocity='0 0 0'-v_right*300;
  1974.                                 else
  1975.                                 {
  1976.                                         traceline(self.origin,self.origin+v_up*dist,TRUE,self);
  1977.                                         if(trace_fraction==1.0)
  1978.                                                 self.velocity=v_up*300;
  1979.                                         else
  1980.                                         {
  1981.                                                 traceline(self.origin,self.origin-v_up*dist,TRUE,self);
  1982.                                                 if(trace_fraction==1.0)
  1983.                                                         self.velocity='0 0 0'-v_up*300;
  1984.                                                 else
  1985.                                                 {
  1986.                                                         traceline(self.origin,self.origin-v_forward*dist,TRUE,self);
  1987.                                                         if(trace_fraction==1.0)
  1988.                                                                 self.velocity='0 0 0'-v_forward*300;
  1989.                                                         else
  1990.                                                         {
  1991.                                                                 sprint(self.owner,"Missile in trouble!\n");
  1992.                                                                 r=random();
  1993.                                                                 if(r>0.75)
  1994.                                                                         self.velocity=v_right*300;
  1995.                                                                 else
  1996.                                                                 if(r>0.50)
  1997.                                                                         self.velocity='0 0 0'-v_right*300;
  1998.                                                                 else
  1999.                                                                 if(r>0.25)
  2000.                                                                         self.velocity=v_up*300;
  2001.                                                                 else
  2002.                                                                         self.velocity='0 0 0'-v_up*300;
  2003.                                                         }
  2004.                                                 }
  2005.                                         }
  2006.                                 }
  2007.                         }
  2008.                         self.angles=vectoangles(self.velocity);
  2009.                 }
  2010.         }
  2011.     self.nextthink = time + 0.2;
  2012.     self.think=HomeThink;
  2013. };
  2014.  
  2015. /*
  2016. =====================
  2017. Nail Launcher Grenade
  2018.  
  2019. Pops up, rotates and fires nails before exploding.
  2020. Code is from TeamFortress 1.0 by Robin Walker, John Cook and Ian Caughley.
  2021. =====================
  2022. */
  2023. void() NailLauncherTouch =
  2024. {
  2025.     if (other == self.owner)
  2026.         return;        // don't explode on owner
  2027.  
  2028.     // If the Nail Grenade hits a player, it just bounces off
  2029.  
  2030.     sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);
  2031.     if (self.velocity == '0 0 0')
  2032.         self.avelocity = '0 0 0';
  2033. };
  2034.  
  2035. void() NailLauncherLaunchNail =
  2036. // Fire three nails in random directions, parallel to the ground
  2037. {
  2038.     local float i,j;
  2039.  
  2040.     i = 0;
  2041.     while (i < 3)
  2042.     {
  2043.         j = (random() + 2) * 2;
  2044.         current_yaw = anglemod( self.angles_y );
  2045.         current_yaw = anglemod(current_yaw + j);
  2046.         self.angles_y = current_yaw;
  2047.         self.angles_x = 0;
  2048.         self.angles_z = 0;
  2049.         makevectors(self.angles);
  2050.  
  2051.         launch_spike(self.origin, v_forward);
  2052.  
  2053.         i = i + 1;
  2054.     }
  2055.  
  2056.     self.nextthink = time + 0.1;
  2057.  
  2058.     // Explode?
  2059.         if (random() < 0.01)
  2060.         self.think = GrenadeExplode;
  2061. };
  2062.  
  2063. void() NailLauncherNailEm =
  2064. // Ready to launch nails
  2065. {
  2066.     // Rotate and spew Nails
  2067.     self.velocity = '0 0 0';
  2068.  
  2069.     self.nextthink = time + 0.1;
  2070.     self.think = NailLauncherLaunchNail;
  2071. };
  2072.  
  2073. void() NailLauncherExplode =
  2074. // Pop up and launch nails
  2075. {
  2076.     // Raise into the air
  2077.     self.movetype = MOVETYPE_NOCLIP;
  2078.     self.velocity = '0 0 100';
  2079.     self.avelocity = '0 500 0';
  2080.     self.nextthink = time + 0.5;
  2081.     self.think = NailLauncherNailEm;
  2082. };
  2083.  
  2084. void() W_FireNailLauncher =
  2085. {
  2086.     local    entity missile, mpuff;
  2087.  
  2088.     sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  2089.  
  2090.     self.punchangle_x = -2;
  2091.  
  2092.     missile = spawn ();
  2093.     missile.owner = self;
  2094.     missile.movetype = MOVETYPE_BOUNCE;
  2095.     missile.solid = SOLID_BBOX;
  2096.     missile.classname = "grenade";
  2097.         missile.nextthink = time + 2 + 5*random();
  2098.  
  2099.     makevectors (self.v_angle);
  2100.  
  2101.     if (self.v_angle_x)
  2102.         missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
  2103.     else
  2104.     {
  2105.         missile.velocity = aim(self, 10000);
  2106.         missile.velocity = missile.velocity * 600;
  2107.         missile.velocity_z = 200;
  2108.     }
  2109.  
  2110.     missile.avelocity = '300 300 300';
  2111.  
  2112.     missile.angles = vectoangles(missile.velocity);
  2113.  
  2114.     missile.touch = NailLauncherTouch;
  2115.         missile.think = NailLauncherExplode;
  2116.  
  2117.         self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  2118.  
  2119.     setmodel (missile, "progs/grenade.mdl");
  2120.     setsize (missile, '0 0 0', '0 0 0');
  2121.     setorigin (missile, self.origin);
  2122. };
  2123.  
  2124. /*
  2125. =====
  2126. Flare
  2127.  
  2128. Fires a flare which sticks to walls (but not doors and plats).
  2129. Flies until it hits something (does 1pt damage), then flares up.
  2130. After a while it'll die down again.
  2131.  
  2132. Cool code is from Steve Bond (wedge@nuc.net).
  2133. =====
  2134. */
  2135.  
  2136. void() FlareDim =
  2137. // Make flare go dim again
  2138. {
  2139. // Make the flare get dim by setting .effects bit
  2140.         self.effects=EF_DIMLIGHT;
  2141. // Give it 15 seconds before burnout
  2142.     self.nextthink = time + 15;
  2143. // After 15 seconds remove the flare from the game
  2144.     self.think = SUB_Remove;
  2145. };
  2146.  
  2147. void() FlareBright =
  2148. // Flare brightens up
  2149. {
  2150.     local vector    vel;
  2151. // Make the flare get BRIGHT by setting .effects bit
  2152.         self.effects=EF_BRIGHTLIGHT;
  2153. // Give it 10 seconds at maximum intensity
  2154.     self.nextthink = time + 10;
  2155. // After 10 seconds, call flare_dim
  2156.     self.think = FlareDim;
  2157. // Play a sound for flare ignition
  2158.     sound (self, CHAN_WEAPON, "misc/power.wav", 1, ATTN_NORM);
  2159.  
  2160. // Set up some red and yellow sparks
  2161.     vel = '0 0 150';
  2162.     particle (self.origin+vel*0.01,vel,111,150);
  2163.         vel = '0 0 120';
  2164.     particle (self.origin+vel*0.01,vel,73,200);
  2165. };
  2166.  
  2167. void() FlareTouch =
  2168. {
  2169. local float rand;
  2170.     if (other == self.owner)
  2171.         return;
  2172.  
  2173.         if (other.classname == "door" || other.classname == "plat")
  2174.                 {
  2175.                 remove(self);
  2176.                 return;
  2177.                 }
  2178.  
  2179.     if (other.solid == SOLID_TRIGGER)
  2180.         return; // trigger field, do nothing
  2181.  
  2182.     if (pointcontents(self.origin) == CONTENT_SKY)
  2183.     {
  2184.         remove(self);
  2185.         return;
  2186.     }
  2187.  
  2188. // has the flare hit something that bleeds?
  2189.     if (other.takedamage)
  2190.     {
  2191.                 // If so, spawn some blood
  2192.         spawn_touchblood (9);
  2193.                 // also do one point of damage
  2194.         T_Damage (other, self, self.owner, 1);
  2195.  
  2196. /*
  2197. Don't remove the flare unless it is still flying. Otherwise, monsters or
  2198. other players can come up and 'pull' your flare out of the wall
  2199. */
  2200.           if (self.velocity != '0 0 0')
  2201.             remove(self);
  2202.     }
  2203.     else
  2204.     {
  2205.         // Write information to the network
  2206.         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
  2207.         WriteByte (MSG_BROADCAST, TE_SPIKE);
  2208.         WriteCoord (MSG_BROADCAST, self.origin_x);
  2209.         WriteCoord (MSG_BROADCAST, self.origin_y);
  2210.         WriteCoord (MSG_BROADCAST, self.origin_z);
  2211.     }
  2212.  
  2213. //Stop the flare, but do not remove it.
  2214.     self.movetype = MOVETYPE_NONE;
  2215. // Fix it so that flare only partially embeds itself (thanks, Luke)
  2216.         self.origin = self.origin - self.velocity * 0.005;
  2217.          self.velocity = '0 0 0';
  2218.     self.touch = SUB_Null;
  2219. };
  2220.  
  2221. void() W_FireFlare =
  2222. {
  2223.     local    entity missile, mpuff;
  2224.  
  2225.     self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  2226.  
  2227.     sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
  2228.  
  2229.     self.punchangle_x = -2;
  2230.  
  2231.     missile = spawn ();
  2232.     missile.owner = self;
  2233.     missile.movetype = MOVETYPE_FLY;
  2234.     missile.solid = SOLID_BBOX;
  2235.  
  2236. // set missile speed
  2237.  
  2238.     makevectors (self.v_angle);
  2239.     missile.velocity = aim(self, 1000);
  2240.     missile.velocity = missile.velocity * 1000;
  2241.     missile.angles = vectoangles(missile.velocity);
  2242.  
  2243.     missile.touch = FlareTouch;
  2244.  
  2245. // set missile duration
  2246.     missile.nextthink = time + 3; // Flare brightly after 3s
  2247.     missile.think = FlareBright;
  2248.  
  2249.     setmodel (missile, "progs/missile.mdl");
  2250.     setsize (missile, '0 0 0', '0 0 0');
  2251.     setorigin (missile, self.origin + v_forward*8 + '0 0 16');
  2252. };
  2253.  
  2254. /*
  2255. ======
  2256. Camera
  2257.  
  2258. Launches a camera, which pops up and can be rotated via the cursor keys.
  2259. Has to be explicitly removed, but the player can launch several eyes,
  2260. always looking through the latest one.
  2261.  
  2262. ======
  2263. */
  2264. void() CameraTouch =
  2265. {
  2266.     if (other == self.owner)
  2267.         return;        // don't react to owner
  2268.  
  2269.     sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);
  2270.     if (self.velocity == '0 0 0')
  2271.         self.avelocity = '0 0 0';
  2272. };
  2273.  
  2274. void() RemoveCameras =
  2275. {
  2276.  local entity head,camtokill;
  2277.  
  2278.  // First, set the viewport back to the player
  2279.   msg_entity = self; // target of message: the player
  2280.   WriteByte(MSG_ONE, 5);
  2281.   WriteEntity(MSG_ONE, self); // back to player
  2282.   WriteByte(MSG_ONE, 10);
  2283.   WriteAngle(MSG_ONE, self.angles_x);
  2284.   WriteAngle(MSG_ONE, self.angles_y);
  2285.   WriteAngle(MSG_ONE, self.angles_z);
  2286.   // Now, remove all cameras
  2287.   head = findradius(self.origin, 10000); // Look in a big radius
  2288.   while(head)
  2289.   {
  2290.    if ((head.owner == self) && (head.classname == "camera")) // It's a camera of ours
  2291.    {
  2292.     camtokill = head;
  2293.     head = head.chain;
  2294.     sound (camtokill, CHAN_WEAPON, "misc/power.wav", 0.3, ATTN_NORM); // Weird sound as it disappears
  2295.     remove(camtokill);
  2296.    }
  2297.    else
  2298.     { head = head.chain; } // Check next
  2299.   }
  2300. };
  2301.  
  2302. void() CameraFilm =
  2303. {
  2304.  // Shift the viewpoint to the camera
  2305.  msg_entity = self.owner;
  2306.  WriteByte(MSG_ONE, 5);
  2307.  WriteEntity(MSG_ONE, self);
  2308.  WriteByte(MSG_ONE, 10);
  2309.  WriteAngle(MSG_ONE, self.angles_x);
  2310.  WriteAngle(MSG_ONE, self.angles_y);
  2311.  WriteAngle(MSG_ONE, self.angles_z);
  2312. };
  2313.  
  2314. void() CameraActivate =
  2315. // Stop the upwards movement and start filming
  2316. {
  2317.  self.velocity = '0 0 0'; // Stop rising
  2318.  current_yaw = current_yaw * 0; // Place yourself horizontally
  2319.  self.angles_y = 0;
  2320.  self.angles_x = 0;
  2321.  self.angles_z = 0;
  2322.  makevectors(self.angles);
  2323.  
  2324.  self.think = CameraFilm;
  2325.  self.nextthink = time + 0.1;
  2326. };
  2327.  
  2328. void() CameraDeploy =
  2329. // Grenade releases a pop-up camera
  2330. {
  2331.  // Rise into the air
  2332.  self.movetype = MOVETYPE_NOCLIP;
  2333.  self.velocity = '0 0 100';
  2334.  self.think = CameraActivate;
  2335.  self.nextthink = time + 0.5;
  2336.  // Change into glowing eyes
  2337.  setmodel (self, "progs/eyes.mdl");
  2338.  self.effects = self.effects | EF_DIMLIGHT; // Weird glow
  2339. };
  2340.  
  2341. void() W_FireCamera =
  2342. {
  2343.     local    entity missile, mpuff;
  2344.  
  2345.     self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  2346.  
  2347.     sound (self, CHAN_WEAPON, "weapons/grenade.wav", 1, ATTN_NORM);
  2348.  
  2349.     self.punchangle_x = -2;
  2350.  
  2351.     missile = spawn ();
  2352.     missile.owner = self;
  2353.     missile.movetype = MOVETYPE_BOUNCE;
  2354.     missile.solid = SOLID_BBOX;
  2355.     missile.classname = "camera";
  2356.  
  2357. // set missile speed
  2358.  
  2359.     makevectors (self.v_angle);
  2360.  
  2361.     if (self.v_angle_x)
  2362.         missile.velocity = v_forward*600 + v_up * 200 + crandom()*v_right*10 + crandom()*v_up*10;
  2363.     else
  2364.     {
  2365.         missile.velocity = aim(self, 10000);
  2366.         missile.velocity = missile.velocity * 600;
  2367.         missile.velocity_z = 200;
  2368.     }
  2369.  
  2370.     missile.avelocity = '300 300 300';
  2371.  
  2372.     missile.angles = vectoangles(missile.velocity);
  2373.  
  2374.     missile.touch = CameraTouch;
  2375.  
  2376. // set missile duration
  2377.     missile.nextthink = time + 2.5;
  2378.     missile.think = CameraDeploy;
  2379.  
  2380.     setmodel (missile, "progs/grenade.mdl");
  2381.     setsize (missile, '0 0 0', '0 0 0');
  2382.     setorigin (missile, self.origin);
  2383. };
  2384.  
  2385. /*
  2386. ===========
  2387. Nail Probe
  2388.  
  2389. Similar to the homing missile, this slow-moving probe fires
  2390. nails at the enemy.
  2391. The probe self-destructs after 20s or when it runs out of ammo.
  2392.  
  2393. This code is a modified Homing Missile, which, in turn, is
  2394. a modified Genius Missile - see comment there for original
  2395. author.
  2396.  
  2397. PS: I used to have the life span set to 60s with no ammo limit -
  2398. those things cleared out whole levels without help from me >:)
  2399. ===========
  2400. */
  2401. void()   NailHomeThink;
  2402. entity() NailHomeFindTarget;
  2403. float(entity targ) visible;
  2404. float(entity targ) infront;
  2405.  
  2406. void() NailProbeTouch =
  2407. {
  2408.     if (other == self.owner)
  2409.         return;        // don't react to owner
  2410.  
  2411.     sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);
  2412.     if (self.velocity == '0 0 0') // bounce off
  2413.         self.avelocity = '0 0 0';
  2414. };
  2415.  
  2416. void(vector org, vector d) NailProbeShoot =
  2417. // Shoot once in direction d
  2418. {
  2419.   sound (self, CHAN_WEAPON, "weapons/spike2.wav", 1, ATTN_NORM);
  2420.   launch_spike (org + '0 0 16', d);
  2421.   self.ammo_nails = self.ammo_nails - 1; // One more nail gone...
  2422.   newmis.touch = spike_touch;
  2423.   setmodel (newmis, "progs/spike.mdl");
  2424.   setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
  2425.   self.nextthink = time + 1; // shoot & think slower for a moment
  2426. };
  2427.  
  2428. void() W_FireNailProbe =
  2429. {
  2430.     local    entity missile, mpuff;
  2431.  
  2432.         self.currentammo = self.ammo_rockets = self.ammo_rockets - 1;
  2433.  
  2434.     sound (self, CHAN_WEAPON, "weapons/sgun1.wav", 1, ATTN_NORM);
  2435.  
  2436.     self.punchangle_x = -2;
  2437.  
  2438.     missile = spawn ();
  2439.     missile.owner = self;
  2440.     missile.movetype = MOVETYPE_FLY;
  2441.     missile.solid = SOLID_BBOX;
  2442.  
  2443. // set missile speed
  2444.  
  2445.     makevectors (self.v_angle);
  2446.     missile.velocity = aim(self, 1000);
  2447.     missile.velocity = missile.velocity * 100;
  2448.     missile.angles = vectoangles(missile.velocity);
  2449.  
  2450.     missile.touch = NailProbeTouch;
  2451.     missile.enemy = world;
  2452.         missile.ltime = time;
  2453.         missile.ammo_nails = 20; // Missile is armed with 20 nails
  2454.  
  2455.     setmodel (missile, "progs/grenade.mdl");
  2456.     setsize (missile, '0 0 0', '0 0 0');
  2457.     setorigin (missile, self.origin + v_forward*8 + '0 0 16');
  2458.         missile.effects = self.effects | EF_DIMLIGHT; // Glow a bit
  2459.         missile.nextthink = time + 1; // 1s pause
  2460.     missile.think = NailHomeThink;
  2461. };
  2462.  
  2463. entity() NailHomeFindTarget =
  2464. {
  2465.     local entity head, selected;
  2466.     local float dist;
  2467.     dist = 100000;
  2468.     selected = world;
  2469.     head = findradius(self.origin, 1500);
  2470.     while(head)
  2471.     {
  2472.                 if( (head.health > 1) && (head != self) && (head != self.owner) && head.classname != "door")
  2473.         {
  2474.                  if ((coop && (head.classname == "player")) != 1) // Not player in coop
  2475.                  {
  2476.                   if (head.classname != "monster_zombie")  // No use shooting zombies...
  2477.                   {
  2478.            traceline(self.origin,head.origin,TRUE,self);
  2479.            if ( (trace_fraction >= 1) && (vlen(head.origin - self.origin) < dist) )
  2480.            {
  2481.             selected = head;
  2482.             dist = vlen(head.origin - self.origin);
  2483.            }
  2484.                   }
  2485.                  }
  2486.         }
  2487.         head = head.chain;
  2488.     }
  2489.         if (selected != world)
  2490.     {
  2491.         sprint (self.owner,"Target->");
  2492.         if (selected.classname == "player")
  2493.         {
  2494.             sprint (self.owner,selected.netname);
  2495.             sprint (selected,self.owner.netname);
  2496.             sprint (selected," has an eye on you!\n");
  2497.         }
  2498.         else
  2499.             sprint (self.owner,selected.classname);
  2500.         sprint (self.owner,"\n");
  2501.     }
  2502.     return selected;
  2503. };
  2504.  
  2505. void() NailHomeThink =
  2506. {
  2507.         local vector dir, vtemp, tm, enemydir;
  2508.         local float r,dist;
  2509.         dist=100;
  2510.         if ((time-self.ltime >= 20) && // Life-span is 20s. Feel free to increment
  2511.             (self.ammo_nails <= 0)) // No ammo left?
  2512.         {
  2513.          sprint(self.owner,"Probe self-destructing.\n");
  2514.          self.nextthink=time+0.1;
  2515.          self.think=GrenadeExplode; // Go out with a bang
  2516.          return;
  2517.         }
  2518.         traceline(self.origin,self.enemy.origin,TRUE,self);
  2519.         if ( trace_fraction<1.0 || !(self.enemy) || (self.enemy == world) || (self.enemy.health < 1) )
  2520.         self.enemy = HomeFindTarget();
  2521.  
  2522.         if (self.enemy != world) // Valid target
  2523.     {
  2524.          enemydir = normalize(self.enemy.origin - self.origin); // Get direction
  2525.          NailProbeShoot(self.origin,enemydir);
  2526.     }
  2527.         else
  2528.         {
  2529.                 makevectors(self.angles);
  2530.                 traceline(self.origin,self.origin+v_forward*dist,TRUE,self);
  2531.                 if(trace_fraction<1.0)
  2532.                 {
  2533.                         traceline(self.origin,self.origin+v_right*dist,TRUE,self);
  2534.                         if(trace_fraction==1.0)
  2535.                                 self.velocity=v_right*300;
  2536.                         else
  2537.                         {
  2538.                                 traceline(self.origin,self.origin-v_right*dist,TRUE,self);
  2539.                                 if(trace_fraction==1.0)
  2540.                                         self.velocity='0 0 0'-v_right*300;
  2541.                                 else
  2542.                                 {
  2543.                                         traceline(self.origin,self.origin+v_up*dist,TRUE,self);
  2544.                                         if(trace_fraction==1.0)
  2545.                                                 self.velocity=v_up*300;
  2546.                                         else
  2547.                                         {
  2548.                                                 traceline(self.origin,self.origin-v_up*dist,TRUE,self);
  2549.                                                 if(trace_fraction==1.0)
  2550.                                                         self.velocity='0 0 0'-v_up*300;
  2551.                                                 else
  2552.                                                 {
  2553.                                                         traceline(self.origin,self.origin-v_forward*dist,TRUE,self);
  2554.                                                         if(trace_fraction==1.0)
  2555.                                                                 self.velocity='0 0 0'-v_forward*300;
  2556.                                                         else
  2557.                                                         {
  2558.                                                                 sprint(self.owner,"Probe in trouble!\n");
  2559.                                                                 r=random();
  2560.                                                                 if(r>0.75)
  2561.                                                                         self.velocity=v_right*300;
  2562.                                                                 else
  2563.                                                                 if(r>0.50)
  2564.                                                                         self.velocity='0 0 0'-v_right*300;
  2565.                                                                 else
  2566.                                                                 if(r>0.25)
  2567.                                                                         self.velocity=v_up*300;
  2568.                                                                 else
  2569.                                                                         self.velocity='0 0 0'-v_up*300;
  2570.                                                         }
  2571.                                                 }
  2572.                                         }
  2573.                                 }
  2574.                         }
  2575.                         self.angles=vectoangles(self.velocity);
  2576.                 }
  2577.         }
  2578.     self.nextthink = time + 0.2;
  2579.     self.think=NailHomeThink;
  2580. };
  2581.  
  2582.  
  2583. void() W_FireGrenade =
  2584. // Choose grenade type to fire according to player.button1
  2585. // The USE button is not used, so we use it as a local variable for each player
  2586. {
  2587.  if (self.button1 ==  1) { W_FireNailBomb (); return; }
  2588.  if (self.button1 ==  2) { W_FireNailLauncher (); return; }
  2589.  if (self.button1 ==  3) { W_FireBCGrenade (); return; }
  2590.  if (self.button1 ==  4) { W_FireRocket (); return; }
  2591.  if (self.button1 ==  5) { W_FireRicochet (); return; }
  2592.  if (self.button1 ==  6) { W_FireGlue (); return; }
  2593.  if (self.button1 ==  7) { W_FireMG (); return; }
  2594.  if (self.button1 ==  8) { W_FireVamp (); return; }
  2595.  if (self.button1 ==  9) { W_FireMedic (); return; }
  2596.  if (self.button1 == 10) { W_FireAP (); return; }
  2597.  if (self.button1 == 11) { W_FireRTB (); return; }
  2598.  if (self.button1 == 12) { W_FireAmmoCache (); return; }
  2599.  if (self.button1 == 13) { W_FireHomingMissile (); return; }
  2600.  if (self.button1 == 14) { W_FireFlare (); return; }
  2601.  if (self.button1 == 15) { W_FireCamera (); return; }
  2602.  if (self.button1 == 16) { W_FireNailProbe (); return; }
  2603.  W_FireNormalGrenade (); // Firewall
  2604. };
  2605.  
  2606. /*
  2607. ===============================================================================
  2608.  
  2609. PLAYER WEAPON USE
  2610.  
  2611. ===============================================================================
  2612. */
  2613.  
  2614. void() W_SetCurrentAmmo =
  2615. {
  2616.     player_run ();        // get out of any weapon firing states
  2617.  
  2618.     self.items = self.items - ( self.items & (IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS) );
  2619.  
  2620.     if (self.weapon == IT_AXE)
  2621.     {
  2622.         self.currentammo = 0;
  2623.         self.weaponmodel = "progs/v_axe.mdl";
  2624.         self.weaponframe = 0;
  2625.     }
  2626.     else if (self.weapon == IT_SHOTGUN)
  2627.     {
  2628.         self.currentammo = self.ammo_shells;
  2629.         self.weaponmodel = "progs/v_shot.mdl";
  2630.         self.weaponframe = 0;
  2631.         self.items = self.items | IT_SHELLS;
  2632.     }
  2633.     else if (self.weapon == IT_SUPER_SHOTGUN)
  2634.     {
  2635.         self.currentammo = self.ammo_shells;
  2636.         self.weaponmodel = "progs/v_shot2.mdl";
  2637.         self.weaponframe = 0;
  2638.         self.items = self.items | IT_SHELLS;
  2639.     }
  2640.     else if (self.weapon == IT_NAILGUN)
  2641.     {
  2642.         self.currentammo = self.ammo_nails;
  2643.         self.weaponmodel = "progs/v_nail.mdl";
  2644.         self.weaponframe = 0;
  2645.         self.items = self.items | IT_NAILS;
  2646.     }
  2647.     else if (self.weapon == IT_SUPER_NAILGUN)
  2648.     {
  2649.         self.currentammo = self.ammo_nails;
  2650.         self.weaponmodel = "progs/v_nail2.mdl";
  2651.         self.weaponframe = 0;
  2652.         self.items = self.items | IT_NAILS;
  2653.     }
  2654.     else if (self.weapon == IT_GRENADE_LAUNCHER)
  2655.     {
  2656.         self.currentammo = self.ammo_rockets;
  2657.         self.weaponmodel = "progs/v_rock.mdl";
  2658.         self.weaponframe = 0;
  2659.         self.items = self.items | IT_ROCKETS;
  2660.     }
  2661.     else if (self.weapon == IT_ROCKET_LAUNCHER)
  2662.     {
  2663.         self.currentammo = self.ammo_rockets;
  2664.         self.weaponmodel = "progs/v_rock2.mdl";
  2665.         self.weaponframe = 0;
  2666.         self.items = self.items | IT_ROCKETS;
  2667.     }
  2668.     else if (self.weapon == IT_LIGHTNING)
  2669.     {
  2670.         self.currentammo = self.ammo_cells;
  2671.         self.weaponmodel = "progs/v_light.mdl";
  2672.         self.weaponframe = 0;
  2673.         self.items = self.items | IT_CELLS;
  2674.     }
  2675.     else
  2676.     {
  2677.         self.currentammo = 0;
  2678.         self.weaponmodel = "";
  2679.         self.weaponframe = 0;
  2680.     }
  2681. };
  2682.  
  2683. float() W_BestWeapon =
  2684. {
  2685.     local    float    it;
  2686.     
  2687.     it = self.items;
  2688.  
  2689.     if(self.ammo_cells >= 1 && (it & IT_LIGHTNING) )
  2690.         return IT_LIGHTNING;
  2691.     else if(self.ammo_nails >= 2 && (it & IT_SUPER_NAILGUN) )
  2692.         return IT_SUPER_NAILGUN;
  2693.     else if(self.ammo_shells >= 2 && (it & IT_SUPER_SHOTGUN) )
  2694.         return IT_SUPER_SHOTGUN;
  2695.     else if(self.ammo_nails >= 1 && (it & IT_NAILGUN) )
  2696.         return IT_NAILGUN;
  2697.     else if(self.ammo_shells >= 1 && (it & IT_SHOTGUN) )
  2698.         return IT_SHOTGUN;
  2699.  
  2700. /*
  2701.     if(self.ammo_rockets >= 1 && (it & IT_ROCKET_LAUNCHER) )
  2702.         return IT_ROCKET_LAUNCHER;
  2703.     else if(self.ammo_rockets >= 1 && (it & IT_GRENADE_LAUNCHER) )
  2704.         return IT_GRENADE_LAUNCHER;
  2705.  
  2706. */
  2707.  
  2708.     return IT_AXE;
  2709. };
  2710.  
  2711. float() W_CheckNoAmmo =
  2712. {
  2713.     if (self.currentammo > 0)
  2714.         return TRUE;
  2715.  
  2716.     if (self.weapon == IT_AXE)
  2717.         return TRUE;
  2718.     
  2719.     self.weapon = W_BestWeapon ();
  2720.  
  2721.     W_SetCurrentAmmo ();
  2722.  
  2723. // drop the weapon down
  2724.     return FALSE;
  2725. };
  2726.  
  2727. /*
  2728. ============
  2729. W_Attack
  2730.  
  2731. An attack impulse can be triggered now
  2732. ============
  2733. */
  2734. void()    player_axe1;
  2735. void()    player_axeb1;
  2736. void()    player_axec1;
  2737. void()    player_axed1;
  2738. void()    player_shot1;
  2739. void()    player_nail1;
  2740. void()    player_light1;
  2741. void()    player_rocket1;
  2742.  
  2743. void() W_Attack =
  2744. {
  2745.     local    float    r;
  2746.  
  2747.     if (!W_CheckNoAmmo ())
  2748.         return;
  2749.  
  2750.     makevectors    (self.v_angle);            // calculate forward angle for velocity
  2751.     self.show_hostile = time + 1;    // wake monsters up
  2752.  
  2753.     if (self.weapon == IT_AXE)
  2754.     {
  2755.         sound (self, CHAN_WEAPON, "weapons/ax1.wav", 1, ATTN_NORM);
  2756.         r = random();
  2757.         if (r < 0.25)
  2758.             player_axe1 ();
  2759.         else if (r<0.5)
  2760.             player_axeb1 ();
  2761.         else if (r<0.75)
  2762.             player_axec1 ();
  2763.         else
  2764.             player_axed1 ();
  2765.         self.attack_finished = time + 0.5;
  2766.     }
  2767.     else if (self.weapon == IT_SHOTGUN)
  2768.     {
  2769.         player_shot1 ();
  2770.         W_FireShotgun ();
  2771.         self.attack_finished = time + 0.5;
  2772.     }
  2773.     else if (self.weapon == IT_SUPER_SHOTGUN)
  2774.     {
  2775.         player_shot1 ();
  2776.         W_FireSuperShotgun ();
  2777.         self.attack_finished = time + 0.7;
  2778.     }
  2779.     else if (self.weapon == IT_NAILGUN)
  2780.     {
  2781.         player_nail1 ();
  2782.     }
  2783.     else if (self.weapon == IT_SUPER_NAILGUN)
  2784.     {
  2785.         player_nail1 ();
  2786.     }
  2787.     else if (self.weapon == IT_GRENADE_LAUNCHER)
  2788.     {
  2789.         player_rocket1();
  2790.         W_FireGrenade();
  2791.         self.attack_finished = time + 0.6;
  2792.     }
  2793.     else if (self.weapon == IT_ROCKET_LAUNCHER)
  2794.     {
  2795.         player_rocket1();
  2796.         W_FireRocket();
  2797.         self.attack_finished = time + 0.8;
  2798.     }
  2799.     else if (self.weapon == IT_LIGHTNING)
  2800.     {
  2801.         player_light1();
  2802.         self.attack_finished = time + 0.1;
  2803.         sound (self, CHAN_AUTO, "weapons/lstart.wav", 1, ATTN_NORM);
  2804.     }
  2805. };
  2806.  
  2807. /*
  2808. ============
  2809. W_ChangeWeapon
  2810.  
  2811. ============
  2812. */
  2813. void() W_ChangeWeapon =
  2814. {
  2815.     local    float    it, am, fl;
  2816.     
  2817.     it = self.items;
  2818.     am = 0;
  2819.  
  2820.     if (self.impulse == 1)
  2821.     {
  2822.         fl = IT_AXE;
  2823.     }
  2824.     else if (self.impulse == 2)
  2825.     {
  2826.         fl = IT_SHOTGUN;
  2827.         if (self.ammo_shells < 1)
  2828.             am = 1;
  2829.     }
  2830.     else if (self.impulse == 3)
  2831.     {
  2832.         fl = IT_SUPER_SHOTGUN;
  2833.         if (self.ammo_shells < 2)
  2834.             am = 1;
  2835.     }        
  2836.     else if (self.impulse == 4)
  2837.     {
  2838.         fl = IT_NAILGUN;
  2839.         if (self.ammo_nails < 1)
  2840.             am = 1;
  2841.     }
  2842.     else if (self.impulse == 5)
  2843.     {
  2844.         fl = IT_SUPER_NAILGUN;
  2845.         if (self.ammo_nails < 2)
  2846.             am = 1;
  2847.     }
  2848.     else if (self.impulse == 6)
  2849.     {
  2850.         fl = IT_GRENADE_LAUNCHER;
  2851.         if (self.ammo_rockets < 1)
  2852.             am = 1;
  2853.     }
  2854.     else if (self.impulse == 7)
  2855.     {
  2856.         fl = IT_ROCKET_LAUNCHER;
  2857.         if (self.ammo_rockets < 1)
  2858.             am = 1;
  2859.     }
  2860.     else if (self.impulse == 8)
  2861.     {
  2862.         fl = IT_LIGHTNING;
  2863.         if (self.ammo_cells < 1)
  2864.             am = 1;
  2865.     }
  2866.  
  2867.     self.impulse = 0;
  2868.     
  2869.     if (!(self.items & fl))
  2870.     {    // don't have the weapon or the ammo
  2871.         sprint (self, "no weapon.\n");
  2872.         return;
  2873.     }
  2874.  
  2875.     if (am)
  2876.     {    // don't have the ammo
  2877.         sprint (self, "not enough ammo.\n");
  2878.         return;
  2879.     }
  2880.  
  2881. //
  2882. // set weapon, set ammo
  2883. //
  2884.     self.weapon = fl;
  2885.     W_SetCurrentAmmo ();
  2886. };
  2887.  
  2888. /*
  2889. ============
  2890. CheatCommand
  2891. ============
  2892. */
  2893. void() CheatCommand =
  2894. {
  2895.     if (deathmatch || coop)
  2896.         return;
  2897.  
  2898.     self.ammo_rockets = 100;
  2899.     self.ammo_nails = 200;
  2900.     self.ammo_shells = 100;
  2901.     self.items = self.items |
  2902.         IT_AXE |
  2903.         IT_SHOTGUN |
  2904.         IT_SUPER_SHOTGUN |
  2905.         IT_NAILGUN |
  2906.         IT_SUPER_NAILGUN |
  2907.         IT_GRENADE_LAUNCHER |
  2908.         IT_ROCKET_LAUNCHER |
  2909.         IT_KEY1 | IT_KEY2;
  2910.  
  2911.     self.ammo_cells = 200;
  2912.     self.items = self.items | IT_LIGHTNING;
  2913.  
  2914.     self.weapon = IT_ROCKET_LAUNCHER;
  2915.     self.impulse = 0;
  2916.     W_SetCurrentAmmo ();
  2917. };
  2918.  
  2919. /*
  2920. ============
  2921. CycleWeaponCommand
  2922.  
  2923. Go to the next weapon with ammo
  2924. ============
  2925. */
  2926. void() CycleWeaponCommand =
  2927. {
  2928.     local    float    it, am;
  2929.  
  2930.     it = self.items;
  2931.     self.impulse = 0;
  2932.  
  2933.     while (1)
  2934.     {
  2935.         am = 0;
  2936.  
  2937.         if (self.weapon == IT_LIGHTNING)
  2938.         {
  2939.             self.weapon = IT_AXE;
  2940.         }
  2941.         else if (self.weapon == IT_AXE)
  2942.         {
  2943.             self.weapon = IT_SHOTGUN;
  2944.             if (self.ammo_shells < 1)
  2945.                 am = 1;
  2946.         }
  2947.         else if (self.weapon == IT_SHOTGUN)
  2948.         {
  2949.             self.weapon = IT_SUPER_SHOTGUN;
  2950.             if (self.ammo_shells < 2)
  2951.                 am = 1;
  2952.         }
  2953.         else if (self.weapon == IT_SUPER_SHOTGUN)
  2954.         {
  2955.             self.weapon = IT_NAILGUN;
  2956.             if (self.ammo_nails < 1)
  2957.                 am = 1;
  2958.         }
  2959.         else if (self.weapon == IT_NAILGUN)
  2960.         {
  2961.             self.weapon = IT_SUPER_NAILGUN;
  2962.             if (self.ammo_nails < 2)
  2963.                 am = 1;
  2964.         }
  2965.         else if (self.weapon == IT_SUPER_NAILGUN)
  2966.         {
  2967.             self.weapon = IT_GRENADE_LAUNCHER;
  2968.             if (self.ammo_rockets < 1)
  2969.                 am = 1;
  2970.         }
  2971.         else if (self.weapon == IT_GRENADE_LAUNCHER)
  2972.         {
  2973.             self.weapon = IT_ROCKET_LAUNCHER;
  2974.             if (self.ammo_rockets < 1)
  2975.                 am = 1;
  2976.         }
  2977.         else if (self.weapon == IT_ROCKET_LAUNCHER)
  2978.         {
  2979.             self.weapon = IT_LIGHTNING;
  2980.             if (self.ammo_cells < 1)
  2981.                 am = 1;
  2982.         }
  2983.  
  2984.         if ( (self.items & self.weapon) && am == 0)
  2985.         {
  2986.             W_SetCurrentAmmo ();
  2987.             return;
  2988.         }
  2989.     }
  2990.  
  2991. };
  2992.  
  2993. /*
  2994. ============
  2995. ServerflagsCommand
  2996.  
  2997. Just for development
  2998. ============
  2999. */
  3000. void() ServerflagsCommand =
  3001. {
  3002.     serverflags = serverflags * 2 + 1;
  3003. };
  3004.  
  3005. void() QuadCheat =
  3006. {
  3007.     if (deathmatch || coop)
  3008.         return;
  3009.     self.super_time = 1;
  3010.     self.super_damage_finished = time + 30;
  3011.     self.items = self.items | IT_QUAD;
  3012.     dprint ("quad cheat\n");
  3013. };
  3014.  
  3015. void(entity p) ShowGrenadeType =
  3016. // Show which grenade player p got loaded
  3017. {
  3018.  if (p.button1 ==  0) { sprint (self,"Standard grenade\n"); return; }
  3019.  if (p.button1 ==  1) { sprint (self,"Nail bomb\n"); return; }
  3020.  if (p.button1 ==  2) { sprint (self,"Nail launcher\n"); return; }
  3021.  if (p.button1 ==  3) { sprint (self,"Bouncing cluster bomb\n"); return; }
  3022.  if (p.button1 ==  4) { sprint (self,"RPG\n"); return; }
  3023.  if (p.button1 ==  5) { sprint (self,"Ricochet grenade\n"); return; }
  3024.  if (p.button1 ==  6) { sprint (self,"Glue grenade\n"); return; }
  3025.  if (p.button1 ==  7) { sprint (self,"MG point defense\n"); return; }
  3026.  if (p.button1 ==  8) { sprint (self,"Vampire bolt\n"); return; }
  3027.  if (p.button1 ==  9) { sprint (self,"Medic bolt\n"); return; }
  3028.  if (p.button1 == 10) { sprint (self,"Armor piercing\n"); return; }
  3029.  if (p.button1 == 11) { sprint (self,"Radio tag bomb\n"); return; }
  3030.  if (p.button1 == 12) { sprint (self,"Ammo cache\n"); return; }
  3031.  if (p.button1 == 13) { sprint (self,"Homing missile\n"); return; }
  3032.  if (p.button1 == 14) { sprint (self,"Flare\n"); return; }
  3033.  if (p.button1 == 15) { sprint (self,"Camera\n"); return; }
  3034.  if (p.button1 == 16) { sprint (self,"Nail probe\n"); return; }
  3035.  sprint (self,"Unknown grenade; using standard\n");
  3036. };
  3037.  
  3038. /*
  3039. ============
  3040. ImpulseCommands
  3041.  
  3042. ============
  3043. */
  3044. void() ImpulseCommands =
  3045. {
  3046.     if (self.impulse >= 1 && self.impulse <= 8)
  3047.         W_ChangeWeapon ();
  3048.  
  3049.     if (self.impulse == 9)
  3050.         CheatCommand ();
  3051.     if (self.impulse == 10)
  3052.         CycleWeaponCommand ();
  3053.     if (self.impulse == 11)
  3054.         ServerflagsCommand ();
  3055.  
  3056. // *** Grenade selection done here
  3057.  
  3058.         if (self.impulse == 99) // Give launcher & ammo
  3059.         {
  3060.     self.ammo_rockets = 100;
  3061.     self.items = self.items | IT_GRENADE_LAUNCHER;
  3062.         }
  3063.  
  3064.         if (self.impulse == 100) // Next grenade type
  3065.         {
  3066.                 self.button1 = self.button1 + 1;
  3067.                 if (self.button1 > 20) self.button1 = 0;
  3068.                 ShowGrenadeType (self);
  3069.         }
  3070.         if (self.impulse == 101) // Previous grenade type
  3071.         {
  3072.                 self.button1 = self.button1 - 1;
  3073.                 if (self.button1 < 0) self.button1 = 20;
  3074.                 ShowGrenadeType (self);
  3075.         }
  3076.         if (self.impulse == 102) // Show current type
  3077.         {
  3078.                 ShowGrenadeType (self);
  3079.         }
  3080.         if (self.impulse == 103) // Select Nail Bomb
  3081.         {
  3082.                 self.button1 = 1;
  3083.                 ShowGrenadeType (self);
  3084.         }
  3085.         if (self.impulse == 104) // Select Bouncing Cluster Bomb
  3086.         {
  3087.                 self.button1 = 2;
  3088.                 ShowGrenadeType (self);
  3089.         }
  3090.         if (self.impulse == 105) // Select RPG
  3091.         {
  3092.                 self.button1 = 3;
  3093.                 ShowGrenadeType (self);
  3094.         }
  3095.         if (self.impulse == 106) // Select Ricochet Grenade
  3096.         {
  3097.                 self.button1 = 4;
  3098.                 ShowGrenadeType (self);
  3099.         }
  3100.         if (self.impulse == 107) // Select Glue Grenade
  3101.         {
  3102.                 self.button1 = 5;
  3103.                 ShowGrenadeType (self);
  3104.         }
  3105.         if (self.impulse == 108) // Select MG Point Defense
  3106.         {
  3107.                 self.button1 = 6;
  3108.                 ShowGrenadeType (self);
  3109.         }
  3110.         if (self.impulse == 109) // Select Vampire Bolt
  3111.         {
  3112.                 self.button1 = 7;
  3113.                 ShowGrenadeType (self);
  3114.         }
  3115.         if (self.impulse == 110) // Select Medic Bolt
  3116.         {
  3117.                 self.button1 = 8;
  3118.                 ShowGrenadeType (self);
  3119.         }
  3120.         if (self.impulse == 111) // Select Armor Piercing
  3121.         {
  3122.                 self.button1 = 9;
  3123.                 ShowGrenadeType (self);
  3124.         }
  3125.         if (self.impulse == 112) // Select Radio Tag Bomb
  3126.         {
  3127.                 self.button1 = 10;
  3128.                 ShowGrenadeType (self);
  3129.         }
  3130.         if (self.impulse == 113) // Detonate RTB
  3131.         {
  3132.          DetonateRTB ();
  3133.         }
  3134.         if (self.impulse == 114) // Disarm RTB
  3135.         {
  3136.          DisarmRTB ();
  3137.         }
  3138.         if (self.impulse == 115) // Select Ammo Cache
  3139.         {
  3140.                 self.button1 = 11;
  3141.                 ShowGrenadeType (self);
  3142.         }
  3143.         if (self.impulse == 116) // Select Homing Missile
  3144.         {
  3145.                 self.button1 = 12;
  3146.                 ShowGrenadeType (self);
  3147.         }
  3148.         if (self.impulse == 117) // Select Nail Launcher
  3149.         {
  3150.                 self.button1 = 13;
  3151.                 ShowGrenadeType (self);
  3152.         }
  3153.         if (self.impulse == 118) // Select Flare
  3154.         {
  3155.                 self.button1 = 14;
  3156.                 ShowGrenadeType (self);
  3157.         }
  3158.         if (self.impulse == 119) // Select Camera
  3159.         {
  3160.                 self.button1 = 15;
  3161.                 ShowGrenadeType (self);
  3162.         }
  3163.         if (self.impulse == 120) // Remove Camera
  3164.         {
  3165.          RemoveCameras ();
  3166.         }
  3167.         if (self.impulse == 121) // Nail Probe
  3168.         {
  3169.                 self.button1 = 16;
  3170.                 ShowGrenadeType (self);
  3171.         }
  3172.  
  3173.         if (self.impulse == 254) // Talk!
  3174.         {
  3175.          sound (self, CHAN_WEAPON, "talk/scum.wav", 1, ATTN_NORM);
  3176.         }
  3177.  
  3178.  
  3179.     if (self.impulse == 255)
  3180.         QuadCheat ();
  3181.  
  3182.     self.impulse = 0;
  3183. };
  3184.  
  3185. /*
  3186. ============
  3187. W_WeaponFrame
  3188.  
  3189. Called every frame so impulse events can be handled as well as possible
  3190. ============
  3191. */
  3192. void() W_WeaponFrame =
  3193. {
  3194.     if (time < self.attack_finished)
  3195.         return;
  3196.  
  3197.     ImpulseCommands ();
  3198.     
  3199. // check for attack
  3200.     if (self.button0)
  3201.     {
  3202.         SuperDamageSound ();
  3203.         W_Attack ();
  3204.     }
  3205. };
  3206.  
  3207. /*
  3208. ========
  3209. SuperDamageSound
  3210.  
  3211. Plays sound if needed
  3212. ========
  3213. */
  3214. void() SuperDamageSound =
  3215. {
  3216.     if (self.super_damage_finished > time)
  3217.     {
  3218.         if (self.super_sound < time)
  3219.         {
  3220.             self.super_sound = time + 1;
  3221.             sound (self, CHAN_BODY, "items/damage3.wav", 1, ATTN_NORM);
  3222.         }
  3223.     }
  3224.     return;
  3225. };
  3226.  
  3227.  
  3228.