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