]> icculus.org git repositories - divverent/nexuiz.git/blob - qcsrc/weapons.qc
info_notnull and target_position fix?
[divverent/nexuiz.git] / qcsrc / weapons.qc
1 void W_UpdateAmmo (void)
2 {
3         self.items = self.items - (self.items & (IT_NAILS | IT_SHELLS | IT_ROCKETS | IT_CELLS));
4         
5         if (self.weapon == IT_LASER)
6                 self.currentammo = 1;
7         else if (self.weapon == IT_UZI)
8         {
9                 self.currentammo = self.ammo_nails;
10                 self.items = self.items | IT_NAILS;
11         }
12         else if (self.weapon == IT_SHOTGUN)
13         {
14                 self.currentammo = self.ammo_shells;
15                 self.items = self.items | IT_SHELLS;
16         }
17         else if (self.weapon == IT_GRENADE_LAUNCHER || self.weapon == IT_HAGAR || self.weapon == IT_ROCKET_LAUNCHER)
18         {
19                 self.currentammo = self.ammo_rockets;
20                 self.items = self.items | IT_ROCKETS;
21         }
22         else if (self.weapon == IT_ELECTRO || self.weapon == IT_NEX || self.weapon == IT_CRYLINK)
23         {
24                 self.currentammo = self.ammo_cells;
25                 self.items = self.items | IT_CELLS;
26         }
27 }
28
29 void W_UpdateWeapon (void)
30 {
31         if (self.weapon == IT_LASER)
32                 self.weaponmodel = "models/weapons/w_lazer.zym";
33         else if (self.weapon == IT_UZI)
34                 self.weaponmodel = "models/weapons/w_uzi.md3";
35         else if (self.weapon == IT_SHOTGUN)
36                 self.weaponmodel = "models/weapons/w_shotgun.md3";
37         else if (self.weapon == IT_GRENADE_LAUNCHER)
38                 self.weaponmodel = "models/weapons/w_gl.md3";
39         else if (self.weapon == IT_ELECTRO)
40                 self.weaponmodel = "models/weapons/w_electro.md3";
41         else if (self.weapon == IT_CRYLINK)
42                 self.weaponmodel = "models/weapons/w_crylink.md3";
43         else if (self.weapon == IT_NEX)
44                 self.weaponmodel = "models/weapons/w_nex.md3";
45         else if (self.weapon == IT_HAGAR)
46                 self.weaponmodel = "models/weapons/w_hagar.md3";
47         else if (self.weapon == IT_ROCKET_LAUNCHER)
48                 self.weaponmodel = "models/weapons/w_rl.md3";
49         else
50                 objerror ("Illegal weapon - please register your guns please!");
51 }
52
53 float W_GetBestWeapon (void)
54 {
55         if ((self.items & IT_ROCKET_LAUNCHER) && self.ammo_rockets)
56                 return IT_ROCKET_LAUNCHER;
57         else if ((self.items & IT_HAGAR) && self.ammo_rockets)
58                 return IT_HAGAR;
59         else if ((self.items & IT_NEX) && self.ammo_cells)
60                 return IT_NEX;
61         else if ((self.items & IT_CRYLINK) && self.ammo_cells)
62                 return IT_CRYLINK;
63         else if ((self.items & IT_ELECTRO) && self.ammo_cells)
64                 return IT_ELECTRO;
65         else if ((self.items & IT_GRENADE_LAUNCHER) && self.ammo_rockets)
66                 return IT_GRENADE_LAUNCHER;
67         else if ((self.items & IT_SHOTGUN) && self.ammo_shells)
68                 return IT_SHOTGUN;
69         else if ((self.items & IT_UZI) && self.ammo_nails)
70                 return IT_UZI;
71         else
72                 return IT_LASER;
73 }
74
75 void W_GiveWeapon (entity e, float wep) // FIXME - make it 'smarter'
76 {
77         entity oldself;
78         
79         if (!wep)
80                 return;
81                 
82         if (!(e.items & wep))
83         {
84                 e.items = e.items | wep;
85                 e.weapon = wep;
86         }       
87         
88         oldself = self;
89         self = e;
90         
91         W_UpdateWeapon ();
92         W_UpdateAmmo ();
93         
94         self = oldself;
95 }
96
97 void W_SwitchWeapon (float wep)
98 {
99         float           nextwep;
100         var float       noammo = FALSE;
101         
102         if (wep == 1)
103                 nextwep = IT_LASER;
104         else if (wep == 2)
105         {
106                 nextwep = IT_UZI;
107                 if (!self.ammo_nails)
108                         noammo = TRUE;
109         }
110         else if (wep == 3)
111         {
112                 nextwep = IT_SHOTGUN;
113                 if (!self.ammo_shells)
114                         noammo = TRUE;
115         }
116         else if (wep == 4)
117         {
118                 nextwep = IT_GRENADE_LAUNCHER;
119                 if (!self.ammo_rockets)
120                         noammo = TRUE;
121         }
122         else if (wep == 5)
123         {
124                 nextwep = IT_ELECTRO;
125                 if (!self.ammo_cells)
126                         noammo = TRUE;
127         }
128         else if (wep == 6)
129         {
130                 nextwep = IT_CRYLINK;
131                 if (!self.ammo_cells)
132                         noammo = TRUE;
133         }
134         else if (wep == 7)
135         {
136                 nextwep = IT_NEX;
137                 if (!self.ammo_cells)
138                         noammo = TRUE;
139         }
140         else if (wep == 8)
141         {
142                 nextwep = IT_HAGAR;
143                 if (!self.ammo_rockets)
144                         noammo = TRUE;
145         }
146         else if (wep == 9)
147         {
148                 nextwep = IT_ROCKET_LAUNCHER;
149                 if (!self.ammo_rockets)
150                         noammo = TRUE;
151         }
152
153         
154         if (!(self.items & nextwep))
155         {
156                 sprint (self, "You don't own that weapon\n");
157                 return;
158         }
159         else if (noammo)
160         {
161                 sprint (self, "You don't have any ammo for that weapon\n");
162                 return;
163         }
164         
165         self.weapon = nextwep;
166         W_UpdateWeapon ();
167         W_UpdateAmmo ();
168         self.attack_finished = time + 0.2;
169         if (self.viewzoom != 1)
170                 self.viewzoom = 1;
171 }
172
173 void W_NextWeapon (void)
174 {
175         float   noammo;
176         
177         while (TRUE)
178         {
179                 noammo = FALSE;
180                 
181                 if (self.weapon == IT_CRYLINK)
182                         self.weapon = IT_LASER;
183                 else if (self.weapon == IT_LASER)
184                 {
185                         self.weapon = IT_UZI;
186                         if (!self.ammo_nails)
187                                 noammo = TRUE;
188                 }
189                 else if (self.weapon == IT_UZI)
190                 {
191                         self.weapon = IT_SHOTGUN;
192                         if (!self.ammo_shells)
193                                 noammo = TRUE;
194                 }
195                 else if (self.weapon == IT_SHOTGUN)
196                 {
197                         self.weapon = IT_GRENADE_LAUNCHER;
198                         if (!self.ammo_rockets)
199                                 noammo = TRUE;
200                 }
201                 else if (self.weapon == IT_GRENADE_LAUNCHER)
202                 {
203                         self.weapon = IT_ELECTRO;
204                         if (!self.ammo_cells)
205                                 noammo = TRUE;
206                 }
207                 else if (self.weapon == IT_ELECTRO)
208                 {
209                         self.weapon = IT_CRYLINK;
210                         if (!self.ammo_cells)
211                         noammo = TRUE;
212                 }
213                 else if (self.weapon == IT_CRYLINK)
214                 {
215                         self.weapon = IT_NEX;
216                         if (!self.ammo_cells)
217                         noammo = TRUE;
218                 }
219                 else if (self.weapon == IT_NEX)
220                 {
221                         self.weapon = IT_HAGAR;
222                         if (!self.ammo_rockets)
223                         noammo = TRUE;
224                 }
225                 else if (self.weapon == IT_HAGAR)
226                 {
227                         self.weapon = IT_ROCKET_LAUNCHER;
228                         if (!self.ammo_rockets)
229                                 noammo = TRUE;
230                 }
231
232                 if ((self.items & self.weapon) && !noammo)
233                 {
234                         W_UpdateWeapon ();
235                         W_UpdateAmmo ();
236                         return;
237                 }
238         }
239 }
240
241 void W_PreviousWeapon (void)
242 {
243         float   noammo;
244         
245         while (TRUE)
246         {
247                 noammo = FALSE;
248                 
249                 if (self.weapon == IT_UZI)
250                         self.weapon = IT_LASER;
251                 else if (self.weapon == IT_SHOTGUN)
252                 {
253                         self.weapon = IT_UZI;
254                         if (!self.ammo_nails)
255                                 noammo = TRUE;
256                 }
257                 else if (self.weapon == IT_GRENADE_LAUNCHER)
258                 {
259                         self.weapon = IT_SHOTGUN;
260                         if (!self.ammo_shells)
261                                 noammo = TRUE;
262                 }
263                 else if (self.weapon == IT_ELECTRO)
264                 {
265                         self.weapon = IT_GRENADE_LAUNCHER;
266                         if (!self.ammo_rockets)
267                                 noammo = TRUE;
268                 }
269                 else if (self.weapon == IT_CRYLINK)
270                 {
271                         self.weapon = IT_ELECTRO;
272                         if (!self.ammo_cells)
273                                 noammo = TRUE;
274                 }
275                 else if (self.weapon == IT_NEX)
276                 {
277                         self.weapon = IT_CRYLINK;
278                         if (!self.ammo_cells)
279                                 noammo = TRUE;
280                 }
281                 else if (self.weapon == IT_HAGAR)
282                 {
283                         self.weapon = IT_NEX;
284                         if (!self.ammo_cells)
285                                 noammo = TRUE;
286                 }
287                 else if (self.weapon == IT_ROCKET_LAUNCHER)
288                 {
289                         self.weapon = IT_HAGAR;
290                         if (!self.ammo_rockets)
291                                 noammo = TRUE;
292                 }
293                 else if (self.weapon == IT_LASER)
294                 {
295                         self.weapon = IT_ROCKET_LAUNCHER;
296                         if (!self.ammo_rockets)
297                                 noammo = TRUE;
298                 }
299                 
300                 if ((self.items & self.weapon) && !noammo)
301                 {
302                         W_UpdateWeapon ();
303                         W_UpdateAmmo ();
304                         return;
305                 }
306         }
307 }
308
309 float W_CheckAmmo (void)
310 {
311         W_UpdateAmmo ();
312         if (self.weapon == IT_LASER)
313                 return TRUE;
314         else if (self.currentammo)
315                 return TRUE;
316                 
317         self.weapon = W_GetBestWeapon ();
318         W_UpdateWeapon ();
319         
320         return FALSE;
321 }
322
323 void(vector src, float bdamage, vector dir) FireRailgunBullet =
324 {
325         local   vector  v, lastpos;             
326         local   entity  saveself, last;
327
328         if (bdamage < 1)
329                 return;
330
331         last = self;
332         lastpos = src;
333         
334         while (bdamage > 0)
335         {               
336                 traceline (lastpos + dir * 2, lastpos + dir*8192, FALSE, last);
337                 last = trace_ent;
338                 lastpos = trace_endpos;
339                 if (trace_fraction != 1.0)              
340                         {
341                                 if (pointcontents(trace_endpos - dir*4) == CONTENT_SKY)
342                                         return; 
343
344                                 if (trace_ent.takedamage)
345                                 {
346                                         Damage (self, trace_endpos, trace_ent, 0, bdamage);
347                                 }               
348                         }
349                 if (last.solid == SOLID_BSP)                    
350                         bdamage = 0;    
351         }
352 };
353
354 void fireBullet (vector dir, float spread, float damage, float dtype)
355 {
356         vector  org;
357         
358         makevectors (self.v_angle);
359         
360         // use traceline_hitcorpse to make sure it can hit gibs and corpses too
361         org = self.origin + self.view_ofs;
362         traceline_hitcorpse (self, org, org + v_forward * 4096 + v_right * crandom () * spread + v_up * crandom () * spread, FALSE, self);
363         
364         // FIXME - causes excessive 'tinking'. Hopefully remove "tink1.wav" from the ricochets with csqc
365         if ((trace_fraction != 1.0) && (trace_ent != self) && (pointcontents (trace_endpos) != CONTENT_SKY))
366         {
367                 if (trace_ent == world)
368                         te_spike (trace_endpos);
369                 else if (trace_ent.classname == "player" || trace_ent.classname == "corpse" || trace_ent.classname == "gib")
370                 {
371                         Damage (self, trace_endpos, trace_ent, 0, damage);
372                         if (random () < 0.5)
373                                 sound (trace_ent, CHAN_IMPACT, "misc/BodyImpact1.wav", 1, ATTN_NORM);
374                         else
375                                 sound (trace_ent, CHAN_IMPACT, "misc/BodyImpact2.wav", 1, ATTN_NORM);
376                 }
377         }
378 }
379
380 void W_Laser_Touch (void)
381 {
382         vector  dir;
383         
384         if (other == self.owner)
385                 return; 
386         else if (pointcontents (self.origin) == CONTENT_SKY)
387         {
388                 remove (self);
389                 return;
390         }
391         
392         dir = normalize (self.owner.origin - self.origin);
393         
394         sound (self, CHAN_BODY, "weapons/NexImpact.wav", 1, ATTN_NORM);
395         
396         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
397         WriteByte (MSG_BROADCAST, 79);
398         WriteCoord (MSG_BROADCAST, self.origin_x);
399         WriteCoord (MSG_BROADCAST, self.origin_y);
400         WriteCoord (MSG_BROADCAST, self.origin_z);
401         WriteCoord (MSG_BROADCAST, dir_x);
402         WriteCoord (MSG_BROADCAST, dir_y);
403         WriteCoord (MSG_BROADCAST, dir_z);
404         WriteByte (MSG_BROADCAST, 155);
405
406         remove (self);
407 }
408
409 void W_Laser_Attack (void)
410 {
411         entity  missile;
412         
413         sound (self, CHAN_WEAPON, "weapons/Electro_fire.wav", 1, ATTN_NORM);
414         
415         missile = spawn ();
416         missile.owner = self;
417         missile.classname = "laser";
418         
419         missile.movetype = MOVETYPE_FLY;
420         missile.solid = SOLID_BBOX;
421         
422         setmodel (missile, "models/bullet.mdl");
423         setsize (missile, '-4 -4 -4', '4 4 4');
424         setorigin (missile, self.origin + self.view_ofs);
425
426         makevectors (self.v_angle);
427         missile.velocity = v_forward * 2000;
428         missile.angles = vectoangles (missile.velocity);
429         missile.glow_color = 250; // 244, 250
430         missile.glow_size = 1020;
431         missile.glow_trail = 1; // ??? 256
432         missile.touch = W_Laser_Touch;
433         missile.think = SUB_Remove;
434         missile.nextthink = time + 2;
435         
436         self.punchangle_x = random () - 0.5;
437         self.punchangle_y = random () - 0.5;
438         self.punchangle_z = random () - 0.5;
439         
440         self.attack_finished = time + 0.3;
441 }
442
443 void W_Laser_Attack2 (void)
444 {
445         
446 }
447
448 void W_Uzi_Attack (void)
449 {
450         sound (self, CHAN_WEAPON, "weapons/Uzi_Fire.wav", 1, ATTN_NORM);
451
452         fireBullet (v_forward, 300, 8, IT_SHOTGUN);     
453
454         self.punchangle_x = random () * -2;
455         self.punchangle_y = crandom () * 0.5;
456         self.punchangle_z = crandom () * 0.5;
457         
458         self.attack_finished = time + 0.075;
459         self.ammo_nails = self.ammo_nails - 1;
460
461         vector  org; // casing code
462         org = self.origin + self.view_ofs + (v_right * 6) - (v_up * 1) + (v_forward * 20);
463         SpawnCasing (org, v_forward, ((random () * 50 + 50) * v_right) - ((random () * 25 + 25) * v_forward) - ((random () * 5 + 10) * v_up), 2);
464
465         self.effects = self.effects | EF_MUZZLEFLASH;
466 }
467
468 void W_Uzi_Attack2 (void)
469 {
470         sound (self, CHAN_WEAPON, "weapons/Uzi_Fire.wav", 1, ATTN_NORM);
471         
472         fireBullet (v_forward, 3, 10, IT_SHOTGUN);
473         
474         self.punchangle = '-0.5 0 0';
475         
476         self.attack_finished = time + 0.3;
477         self.ammo_nails = self.ammo_nails - 1;
478
479         vector  org;
480         org = self.origin + self.view_ofs + (v_right * 6) - (v_up * 1) + (v_forward * 20);
481         SpawnCasing (org, v_forward, ((random () * 50 + 50) * v_right) - ((random () * 25 + 25) * v_forward) - ((random () * 5 + 10) * v_up), 2);
482 }
483
484 void W_Shotgun_Attack (void)
485 {
486         sound (self, CHAN_WEAPON, "weapons/Shotgun_fire.wav", 1, ATTN_NORM);
487
488         float   sc;
489         sc = 7;
490         while (sc > 0)
491         {
492                 fireBullet (v_forward, 300, 8, IT_SHOTGUN);
493                 sc = sc - 1;
494         }
495
496         self.punchangle_x = -2;
497
498         self.ammo_shells = self.ammo_shells - 1;
499         self.attack_finished = time + 0.8;
500
501         vector  org; // casing code
502         org = self.origin + self.view_ofs + (v_right * 6) - (v_up * 4) + (v_forward * 15);
503         SpawnCasing (org, v_forward, ((random () * 50 + 50) * v_right) - ((random () * 25 + 25) * v_forward) - ((random () * 5 + 10) * v_up), 1);
504 }
505
506 void W_Shotgun_Attack2 (void)
507 {
508
509 }
510
511 void W_Grenade_Explode (entity ignore)
512 {
513         ImpactEffect (self, IT_GRENADE_LAUNCHER);
514
515         RadiusDamage (self.owner, self, 0, 100, world);
516
517         remove (self);
518 }
519
520 void W_Grenade_FuseExplode (void)
521 {
522         W_Grenade_Explode (world);
523 }
524
525 void W_Grenade_Touch (void)
526 {
527         if (other.classname == "player" || other.classname == "corpse")
528         {
529                 Damage (self.owner, self.origin, other, 0, 100);
530                 W_Grenade_Explode (other);
531         }
532         else
533                 sound (self, CHAN_BODY, "weapons/Grenade_Bounce.wav", 1, ATTN_NORM);
534 }
535
536 void W_Grenade_Attack (void)
537 {
538         entity  gren;
539         
540         sound (self, CHAN_WEAPON, "weapons/Grenade_fire.wav", 1, ATTN_NORM);
541         
542         self.punchangle_x = -4;
543         
544         gren = spawn ();
545         gren.owner = self;
546         gren.classname = "grenade";
547         
548         gren.movetype = MOVETYPE_BOUNCE;
549         gren.solid = SOLID_BBOX;
550         
551         gren.takedamage = DAMAGE_YES;
552         gren.health = 1;
553         gren.event_hurt = W_Grenade_Explode;
554
555         setmodel (gren, "models/grenademodel.md3");
556         setsize (gren, '0 0 0', '0 0 0');
557         
558         makevectors (self.v_angle);
559         setorigin (gren, self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -12);
560
561         gren.velocity = v_forward * 900 + v_up * 200;
562         gren.angles = vectoangles (gren.velocity);
563         gren.avelocity = '150 1500 150';
564         
565         gren.touch = W_Grenade_Touch;
566         gren.think = W_Grenade_FuseExplode;
567         gren.nextthink = time + 2;
568         
569         self.attack_finished = time + 1;
570         self.ammo_rockets = self.ammo_rockets - 1;
571 }
572
573 void W_Grenade_Attack2 (void)
574 {
575         entity  gren;
576         
577         sound (self, CHAN_WEAPON, "weapons/Grenade_fire.wav", 1, ATTN_NORM);
578         
579         self.punchangle_x = -4;
580         
581         gren = spawn ();
582         gren.owner = self;
583         gren.classname = "grenade";
584         
585         gren.movetype = MOVETYPE_TOSS;
586         gren.solid = SOLID_BBOX;
587         
588         gren.takedamage = DAMAGE_YES;
589         gren.health = 1;
590         gren.event_hurt = W_Grenade_Explode;
591         
592         setmodel (gren, "models/grenademodel.md3");
593         setsize (gren, '0 0 0', '0 0 0');
594         
595         makevectors (self.v_angle);
596         setorigin (gren, self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -12);
597         
598         gren.velocity = v_forward * 1400 + v_up * 100;
599         gren.angles = vectoangles (gren.velocity);
600         gren.avelocity = '150 1500 150';
601         
602         gren.touch = W_Grenade_Explode;
603         gren.think = W_Grenade_FuseExplode;
604         gren.nextthink = time + 5;
605         
606         self.attack_finished = time + 1;
607         self.ammo_rockets = self.ammo_rockets - 1;
608 }
609
610 void W_Electro_Touch (void)
611 {
612         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
613         WriteByte (MSG_BROADCAST, 79);
614         WriteCoord (MSG_BROADCAST, self.origin_x);
615         WriteCoord (MSG_BROADCAST, self.origin_y);
616         WriteCoord (MSG_BROADCAST, self.origin_z);
617         WriteCoord (MSG_BROADCAST, 0);          // SeienAbunae: groan... Useless clutter
618         WriteCoord (MSG_BROADCAST, 0);
619         WriteCoord (MSG_BROADCAST, 0);
620         WriteByte (MSG_BROADCAST, 155);
621         
622         if (other.classname == "player" || other.classname == "corpse" || other.classname == "gib")
623         {
624                 Damage (self.owner, self.origin, other, 0, 40);
625                 // SeienAbunae: the impact sound doesn't seem to play when the victim dies instead of being hurt
626                 // Maybe it's not that big of a deal
627                 if (random () < 0.5)
628                         sound (other, CHAN_IMPACT, "misc/BodyImpact1.wav", 1, ATTN_NORM);
629                 else
630                         sound (other, CHAN_IMPACT, "misc/BodyImpact2.wav", 1, ATTN_NORM);
631         }
632         
633         remove (self);
634 }
635
636 void W_Electro_Attack (void)
637 {
638         entity  proj;
639         
640         sound (self, CHAN_WEAPON, "weapons/Electro_fire.wav", 1, ATTN_NORM);
641         
642         proj = spawn ();
643         proj.owner = self;
644         proj.classname = "elec";
645         
646         proj.movetype = MOVETYPE_FLY;
647         proj.solid = SOLID_BBOX;
648         proj.effects = 1;
649         
650         makevectors (self.v_angle);
651         
652         setmodel (proj, "models/bullet.mdl");
653         setsize (proj, '0 0 0', '0 0 0');
654         setorigin (proj, self.origin + self.view_ofs + v_forward * 18 + v_right * 5 + v_up * -12);
655         
656         proj.velocity = v_forward * 3000;
657         proj.touch = W_Electro_Touch;
658         proj.think = SUB_Remove;
659         proj.nextthink = time + 3;
660         
661         self.attack_finished = time + 0.6;
662         self.ammo_cells = self.ammo_cells - 1;
663 }
664
665 void W_Plasma_Touch (void)
666 {
667         if (other.classname == "player" || other.classname == "corpse" || other.classname == "gib")
668                 Damage (self.owner, self.origin, other, 0, 15);
669
670         ImpactEffect (self, IT_ELECTRO);
671
672         remove (self);
673 }
674
675 void W_Electro_Attack2 (void)
676 {
677         entity  proj;
678         
679         sound (self, CHAN_WEAPON, "weapons/LazerGun_Fire.wav", 1, ATTN_NORM);
680         
681         proj = spawn ();
682         proj.owner = self;
683         proj.classname = "plasma";
684         
685         proj.movetype = MOVETYPE_FLY;
686         proj.solid = SOLID_BBOX;
687         proj.effects = EF_DIMLIGHT;
688         
689         makevectors (self.v_angle);
690         
691         setmodel (proj, "models/sprites/plasmashot.spr32");
692         setsize (proj, '0 0 0', '0 0 0');
693         setorigin (proj, self.origin + self.view_ofs + v_forward * 5 + v_right * 5 + v_up * -12);
694         
695         proj.velocity = v_forward * 5000;
696         proj.touch = W_Plasma_Touch;
697         proj.think = SUB_Remove;
698         proj.nextthink = time + 1.5;
699
700         self.attack_finished = time + 0.175;
701         self.ammo_cells = self.ammo_cells - 1;
702 }
703
704 void W_Crylink_Attack (void)
705 {
706         vector  org;
707         
708         sound (self, CHAN_WEAPON, "weapons/crylink.wav", 1, ATTN_NORM);
709         org = self.origin + self.view_ofs + v_forward * 18 + v_right * 7 + v_up * -9;
710         makevectors (self.v_angle);
711
712         // FIXME: make it accurate!
713
714         FireRailgunBullet (org, 25, v_forward); 
715
716         traceline (org, self.origin + self.view_ofs + (v_forward * 4096), FALSE, self);
717
718         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
719         WriteByte (MSG_BROADCAST, 76);
720         WriteCoord (MSG_BROADCAST, org_x);
721         WriteCoord (MSG_BROADCAST, org_y);
722         WriteCoord (MSG_BROADCAST, org_z);
723         WriteCoord (MSG_BROADCAST, trace_endpos_x);
724         WriteCoord (MSG_BROADCAST, trace_endpos_y);
725         WriteCoord (MSG_BROADCAST, trace_endpos_z);
726         WriteCoord (MSG_BROADCAST, self.v_angle_x);
727         WriteCoord (MSG_BROADCAST, self.v_angle_y);
728         WriteCoord (MSG_BROADCAST, self.v_angle_z);
729
730         te_spike (trace_endpos);
731
732         self.ammo_cells = self.ammo_cells - 0.25;
733         self.attack_finished = time + 0.165;
734 }
735
736 void W_Crylink_Attack2 (void)
737 {
738         vector  org;
739         vector  dir;
740         
741         makevectors (self.v_angle);
742
743         org = self.origin + self.view_ofs + v_forward * 19 + v_right * 5 + v_up * -7;
744         
745         traceline (org, self.origin + self.view_ofs + v_forward * 4096 + v_right * (random () * 100 - 50) + v_up * (random () * 100 - 50), FALSE, self);
746
747         Damage (self, trace_endpos, trace_ent, 0, 20);  
748         
749         te_lightning1 (self, org, trace_endpos);
750         
751         self.ammo_cells = self.ammo_cells - 0.25;
752         self.attack_finished = time + 0.075;
753 }
754
755 void W_Nex_Attack (void)
756 {
757         vector  org;
758         vector  dir;
759         
760         sound (self, CHAN_WEAPON, "weapons/NexFire.wav", 1, ATTN_NORM);
761         //self.effects = EF_MUZZLEFLASH;
762         self.punchangle_x = -4;
763         
764         makevectors (self.v_angle);
765
766         org = self.origin + self.view_ofs + v_forward * 18 + v_right * 8 + v_up * -5;
767         
768         // FIXME: iterate to hit multiple guys
769         // FIXME: make it accurate!
770
771         traceline (org, self.origin + self.view_ofs + (v_forward * 4096), FALSE, self);
772
773         FireRailgunBullet (org, 90, v_forward); 
774
775         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
776         WriteByte (MSG_BROADCAST, 76);
777         WriteCoord (MSG_BROADCAST, org_x);
778         WriteCoord (MSG_BROADCAST, org_y);
779         WriteCoord (MSG_BROADCAST, org_z);
780         WriteCoord (MSG_BROADCAST, trace_endpos_x);
781         WriteCoord (MSG_BROADCAST, trace_endpos_y);
782         WriteCoord (MSG_BROADCAST, trace_endpos_z);
783         WriteCoord (MSG_BROADCAST, self.v_angle_x);
784         WriteCoord (MSG_BROADCAST, self.v_angle_y);
785         WriteCoord (MSG_BROADCAST, self.v_angle_z);
786         
787         te_plasmaburn (trace_endpos);
788         
789         dir = trace_plane_normal * 100;
790         WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
791         WriteByte (MSG_BROADCAST, TE_FLAMEJET);
792         WriteCoord (MSG_BROADCAST, trace_endpos_x);
793         WriteCoord (MSG_BROADCAST, trace_endpos_y);
794         WriteCoord (MSG_BROADCAST, trace_endpos_z);
795         WriteCoord (MSG_BROADCAST, dir_x);
796         WriteCoord (MSG_BROADCAST, dir_y);
797         WriteCoord (MSG_BROADCAST, dir_z);
798         WriteByte (MSG_BROADCAST, 255);
799         
800         PointSound (trace_endpos, "weapons/NexImpact.wav", 1, ATTN_NORM);
801         
802         self.attack_finished = time + 1;
803         self.ammo_cells = self.ammo_cells - 1;
804 }
805
806 void W_Nex_Attack2 (void)
807 {
808
809 }
810
811 void W_Hagar_Explode (void)
812 {
813         ImpactEffect (self, IT_HAGAR);
814
815         RadiusDamage (self.owner, self, 0, 100, world);
816
817         remove (self);
818 }
819
820 void W_Hagar_Touch (void)
821 {
822         if (other == self.owner)
823                 return;
824         else if (pointcontents (self.origin) == CONTENT_SKY)
825         {
826                 remove (self);
827                 return;
828         }
829         
830         W_Hagar_Explode ();
831 }
832
833 void W_Hagar_Attack (void)
834 {
835         entity  missile;
836         vector  org;
837         
838         sound (self, CHAN_WEAPON, "weapons/Hagar_Fire.wav", 1, ATTN_NORM);
839         
840         missile = spawn ();
841         missile.owner = self;
842         missile.classname = "rocket";
843         
844         missile.takedamage = DAMAGE_YES;
845         missile.health = 1;
846         missile.event_hurt = W_Hagar_Explode;
847         
848         missile.movetype = MOVETYPE_FLY;
849         missile.solid = SOLID_BBOX;
850         setmodel (missile, "models/hagarmissile.mdl");
851         setsize (missile, '0 0 0', '0 0 0');
852         
853         makevectors (self.v_angle);
854         
855         org = self.origin + self.view_ofs + v_forward * 20 + v_right * 4 + v_up * -15;
856         
857         setorigin (missile, org);
858         missile.velocity = v_forward * 2000;
859         missile.angles = vectoangles (missile.velocity);
860         
861         missile.touch = W_Hagar_Touch;
862         missile.think = SUB_Remove;
863         missile.nextthink = time + 1;
864         
865         self.attack_finished = time + 0.2;
866         self.ammo_rockets = self.ammo_rockets - 0.25;
867 }
868
869 void W_Hagar_Attack2 (void)
870 {
871         entity  missile;
872         vector  org;
873         
874         sound (self, CHAN_WEAPON, "weapons/Hagar_Fire.wav", 1, ATTN_NORM);
875         
876         missile = spawn ();
877         missile.owner = self;
878         missile.classname = "rocket";
879         
880         missile.takedamage = DAMAGE_YES;
881         missile.health = 1;
882         missile.event_hurt = W_Hagar_Explode;
883         
884         missile.movetype = MOVETYPE_FLY;
885         missile.solid = SOLID_BBOX;
886         setmodel (missile, "models/hagarmissile.mdl");
887         setsize (missile, '0 0 0', '0 0 0');
888         
889         makevectors (self.v_angle);
890         
891         org = self.origin + self.view_ofs + v_forward * 20 + v_right * 4 + v_up * -15;
892         
893         setorigin (missile, org);
894         missile.velocity = v_forward * 15000;
895         missile.angles = vectoangles (missile.velocity);
896         
897         missile.touch = W_Hagar_Touch;
898         missile.think = SUB_Remove;
899         missile.nextthink = time + 1;
900         
901         self.attack_finished = time + 0.7;
902         self.ammo_rockets = self.ammo_rockets - 0.25;
903 }
904
905 void W_Rocket_Explode (entity ignore)
906 {
907         ImpactEffect (self, IT_ROCKET_LAUNCHER);
908
909         RadiusDamage (self.owner, self, 0, 100, ignore);
910
911         remove (self);
912 }
913
914 void W_Rocket_Touch (void)
915 {
916         if (other == self.owner)
917                 return;
918         else if (pointcontents (self.origin) == CONTENT_SKY)
919         {
920                 remove (self);
921                 return;
922         }
923         else if (other.classname == "player" || other.classname == "corpse")
924         {
925                 Damage (self.owner, self.origin, other, 0, 100);
926                 W_Rocket_Explode (other);
927         }
928         else
929                 W_Rocket_Explode (world);
930 }
931
932 void W_Rocket_Attack (void)
933 {
934         entity  missile;
935         vector  org;
936         
937         sound (self, CHAN_WEAPON, "weapons/Rocket_Fire.wav", 1, ATTN_NORM);
938         
939         missile = spawn ();
940         missile.owner = self;
941         missile.classname = "rocket";
942         
943         missile.takedamage = DAMAGE_YES;
944         missile.health = 1;
945         missile.event_hurt = W_Rocket_Explode;
946         
947         missile.movetype = MOVETYPE_FLY;
948         missile.solid = SOLID_BBOX;
949         setmodel (missile, "models/rocketmissile.mdl");
950         setsize (missile, '0 0 0', '0 0 0');
951         
952         makevectors (self.v_angle);
953         
954         org = self.origin + self.view_ofs + v_forward * 20 + v_right * 4 + v_up * -15;
955         
956         setorigin (missile, org);
957         missile.velocity = v_forward * 2500;
958         missile.angles = vectoangles (missile.velocity);
959         
960         missile.touch = W_Rocket_Touch;
961         missile.think = SUB_Remove;
962         missile.nextthink = time + 1;
963         
964         self.attack_finished = time + 0.5;
965         self.ammo_rockets = self.ammo_rockets - 1;
966         
967 }
968
969 void W_Rocket_Attack2 (void)
970 {
971
972 }
973
974 void W_Attack (void)
975 {
976         if (self.deadflag != DEAD_NO)
977         {
978                 if (self.death_time < time)
979                         PutClientInServer();
980
981                 return;
982         }
983
984         if (!W_CheckAmmo ())
985                 return;
986
987         if (self.weapon == IT_LASER)
988                 W_Laser_Attack ();
989         else if (self.weapon == IT_UZI)
990                 W_Uzi_Attack ();
991         else if (self.weapon == IT_SHOTGUN)
992                 W_Shotgun_Attack ();
993         else if (self.weapon == IT_GRENADE_LAUNCHER)
994                 W_Grenade_Attack ();
995         else if (self.weapon == IT_ELECTRO)
996                 W_Electro_Attack ();
997         else if (self.weapon == IT_CRYLINK)
998                 W_Crylink_Attack ();
999         else if (self.weapon == IT_NEX)
1000                 W_Nex_Attack ();
1001         else if (self.weapon == IT_HAGAR)
1002                 W_Hagar_Attack ();
1003         else if (self.weapon == IT_ROCKET_LAUNCHER)
1004                 W_Rocket_Attack ();
1005
1006         W_UpdateAmmo ();
1007 }
1008
1009 void W_SecondaryAttack (void)
1010 {
1011         if (self.deadflag != DEAD_NO)
1012         {
1013                 if (self.death_time < time)
1014                         PutClientInServer();
1015
1016                 return;
1017         }
1018
1019         if (!W_CheckAmmo ())
1020                 return;
1021
1022         if (self.weapon == IT_LASER)
1023                 W_Laser_Attack2 ();
1024         else if (self.weapon == IT_UZI)
1025                 W_Uzi_Attack2 ();
1026         else if (self.weapon == IT_SHOTGUN)
1027                 W_Shotgun_Attack2 ();
1028         else if (self.weapon == IT_GRENADE_LAUNCHER)
1029                 W_Grenade_Attack2 ();
1030         else if (self.weapon == IT_ELECTRO)
1031                 W_Electro_Attack2 ();
1032         else if (self.weapon == IT_CRYLINK)
1033                 W_Crylink_Attack2 ();
1034         else if (self.weapon == IT_NEX)
1035                 W_Nex_Attack2 ();
1036         else if (self.weapon == IT_HAGAR)
1037                 W_Hagar_Attack2 ();
1038         else if (self.weapon == IT_ROCKET_LAUNCHER)
1039                 W_Rocket_Attack2 ();
1040
1041         W_UpdateAmmo ();
1042 }