1 #include "g_local.h"
2 #include "m_player.h"
3
4 extern qboolean is_quad;
5 extern byte is_silenced;
6
7 void playQuadSound(edict_t *ent);
8 void Weapon_Generic (edict_t *ent,
9 int FRAME_ACTIVATE_LAST,
10 int FRAME_FIRE_LAST,
11 int FRAME_IDLE_LAST,
12 int FRAME_DEACTIVATE_LAST,
13 int *pause_frames,
14 int *fire_frames,
15 void (*fire)(edict_t *ent));
16 void NoAmmoWeaponChange (edict_t *ent);
17 void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed);
18
19 void Grenade_Explode(edict_t *ent);
20 void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result);
21
22
23 void fire_sconnan (edict_t *self);
24 void fire_sconnanEffects (edict_t *self);
25
26 const int SC_MAXFIRETIME = 5; // in seconds...
27 const int SC_BASEDAMAGE = 10; // minimum damage
28 const int SC_DAMGERANGE = 990; // maximum damaged range (max damage possible is SC_BASEDAMAGE + SC_DAMGERANGE)
29 const int SC_MAXRADIUS = 500; // maximum blast radius
30 const int SC_MAXCELLS = 100; // maximum number of cells
31
VectorLengthSquared(vec3_t v)32 vec_t VectorLengthSquared(vec3_t v)
33 {
34 int i;
35 float length;
36
37 length = 0;
38 for (i=0 ; i< 3 ; i++)
39 length += v[i]*v[i];
40
41 return length;
42 }
43
angleToward(edict_t * self,vec3_t point,float speed)44 void angleToward(edict_t *self, vec3_t point, float speed)
45 {
46 vec3_t forward;
47 float yaw = 0.0;
48 float vel = 0.0;
49 vec3_t delta;
50 vec3_t destAngles;
51 VectorSubtract(point, self->s.origin, delta);
52 vectoangles(delta, destAngles);
53 self->ideal_yaw = destAngles[YAW];
54 self->yaw_speed = speed;
55 M_ChangeYaw(self);
56 yaw = self->s.angles[YAW];
57 self->ideal_yaw = destAngles[PITCH];
58 self->s.angles[YAW] = self->s.angles[PITCH];
59 M_ChangeYaw(self);
60 self->s.angles[PITCH] = self->s.angles[YAW];
61 self->s.angles[YAW] = yaw;
62 AngleVectors (self->s.angles, forward, NULL, NULL);
63 vel = VectorLength(self->velocity);
64 VectorScale(forward, vel, self->velocity);
65 }
66
67 #define MAXROTATION 20
68
69 /*
70 Laser Trip Bombs
71 */
72 // spawnflags
73 #define CHECK_BACK_WALL 1
74
75 // variables
76 #define TBOMB_DELAY 1.0
77 #define TBOMB_TIMEOUT 180
78 #define TBOMB_DAMAGE 150
79 #define TBOMB_RADIUS_DAMAGE 384
80 #define TBOMB_HEALTH 100
81 #define TBOMB_SHRAPNEL 5
82 #define TBOMB_SHRAPNEL_DMG 15
83 #define TBOMB_MAX_EXIST 25
84
shrapnel_touch(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)85 void shrapnel_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
86 {
87 // do damage if we can
88 if (!other->takedamage)
89 return;
90
91 if (VectorCompare(ent->velocity, vec3_origin))
92 return;
93
94 T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin,
95 plane->normal, TBOMB_SHRAPNEL_DMG, 8, 0, MOD_TRIPBOMB);
96 G_FreeEdict(ent);
97 }
98
TripBomb_Explode(edict_t * ent)99 void TripBomb_Explode (edict_t *ent)
100 {
101 vec3_t origin;
102 int i = 0;
103
104 T_RadiusDamage(ent, ent->owner ? ent->owner : ent, ent->dmg, ent->enemy, ent->dmg_radius, MOD_TRIPBOMB);
105
106 VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
107
108 gi.WriteByte (svc_temp_entity);
109 if (ent->waterlevel)
110 {
111 if (ent->groundentity)
112 gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
113 else
114 gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
115 }
116 else
117 {
118 if (ent->groundentity)
119 gi.WriteByte (TE_GRENADE_EXPLOSION);
120 else
121 gi.WriteByte (TE_ROCKET_EXPLOSION);
122 }
123 gi.WritePosition (origin);
124 gi.multicast (ent->s.origin, MULTICAST_PHS);
125
126 // throw off some debris
127 for (i = 0; i < TBOMB_SHRAPNEL; i++)
128 {
129 edict_t *sh = G_Spawn();
130 vec3_t forward, right, up;
131 sh->classname = "shrapnel";
132 sh->movetype = MOVETYPE_BOUNCE;
133 sh->solid = SOLID_BBOX;
134 sh->s.effects |= EF_GRENADE;
135 sh->s.modelindex = gi.modelindex("models/objects/shrapnel/tris.md2");
136 sh->owner = ent->owner;
137 VectorSet (sh->avelocity, 300, 300, 300);
138 VectorCopy(ent->s.origin, sh->s.origin);
139 AngleVectors (ent->s.angles, forward, right, up);
140 VectorScale(forward, 500, forward);
141 VectorMA(forward, crandom()*500, right, forward);
142 VectorMA(forward, crandom()*500, up, forward);
143 VectorCopy(forward, sh->velocity);
144 sh->touch = shrapnel_touch;
145 sh->think = G_FreeEdict;
146 sh->nextthink = level.time + 3.0 + crandom() * 1.5;
147 }
148
149 G_FreeEdict(ent);
150 }
151
tripbomb_laser_think(edict_t * self)152 void tripbomb_laser_think (edict_t *self)
153 {
154 vec3_t start;
155 vec3_t end;
156 vec3_t delta;
157 trace_t tr;
158 int count = 8;
159
160 self->nextthink = level.time + FRAMETIME;
161
162 if (level.time > self->timeout)
163 {
164 // play a sound
165 //gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_trig.wav"), 1, ATTN_NORM, 0);
166
167 // blow up
168 self->chain->think = TripBomb_Explode;
169 self->chain->nextthink = level.time + FRAMETIME;
170 G_FreeEdict(self);
171 return;
172 }
173
174 // randomly phase out or EMPNuke is in effect
175 if (EMPNukeCheck(self, self->s.origin) || random() < 0.1)
176 {
177 self->svflags != SVF_NOCLIENT;
178 return;
179 }
180
181 self->svflags &= ~SVF_NOCLIENT;
182 VectorCopy (self->s.origin, start);
183 VectorMA (start, 2048, self->movedir, end);
184 tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
185
186 if (!tr.ent)
187 return;
188
189 VectorSubtract(tr.endpos, self->move_origin, delta);
190 if (VectorCompare(self->s.origin, self->move_origin))
191 {
192 // we haven't done anything yet
193 VectorCopy(tr.endpos, self->move_origin);
194 if (self->spawnflags & 0x80000000)
195 {
196 self->spawnflags &= ~0x80000000;
197 gi.WriteByte (svc_temp_entity);
198 gi.WriteByte (TE_LASER_SPARKS);
199 gi.WriteByte (count);
200 gi.WritePosition (tr.endpos);
201 gi.WriteDir (tr.plane.normal);
202 gi.WriteByte (self->s.skinnum);
203 gi.multicast (tr.endpos, MULTICAST_PVS);
204 }
205 }
206 else if (VectorLength(delta) > 1.0)
207 {
208 // play a sound
209 //gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_trig.wav"), 1, ATTN_NORM, 0);
210
211 // blow up
212 self->chain->think = TripBomb_Explode;
213 self->chain->nextthink = level.time + FRAMETIME;
214 G_FreeEdict(self);
215 return;
216 }
217 VectorCopy(self->move_origin, self->s.old_origin);
218 }
219
tripbomb_laser_on(edict_t * self)220 void tripbomb_laser_on (edict_t *self)
221 {
222 self->svflags &= ~SVF_NOCLIENT;
223 self->think = tripbomb_laser_think;
224
225 // play a sound
226 gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_arm.wav"), 1, ATTN_NORM, 0);
227 tripbomb_laser_think(self);
228 //gi.positioned_sound(self->s.old_origin, self, CHAN_AUTO, gi.soundindex("weapons/ired/las_tink.wav"), 1, ATTN_NORM, 0);
229 }
230
create_tripbomb_laser(edict_t * bomb)231 void create_tripbomb_laser(edict_t *bomb)
232 {
233 // create the laser
234 edict_t *laser = G_Spawn();
235 bomb->chain = laser;
236 laser->classname = "laser trip bomb laser";
237 VectorCopy(bomb->s.origin, laser->s.origin);
238 VectorCopy(bomb->s.origin, laser->move_origin);
239 VectorCopy(bomb->s.angles, laser->s.angles);
240 G_SetMovedir (laser->s.angles, laser->movedir);
241 laser->owner = bomb;
242 laser->s.skinnum = 0xb0b1b2b3; // <- faint purple 0xf3f3f1f1 <-blue red-> 0xf2f2f0f0;
243 laser->s.frame = 2;
244 laser->movetype = MOVETYPE_NONE;
245 laser->solid = SOLID_NOT;
246 laser->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
247 laser->s.modelindex = 1;
248 laser->chain = bomb;
249 laser->spawnflags |= 0x80000001;
250 laser->think = tripbomb_laser_on;
251 laser->nextthink = level.time + FRAMETIME;
252 laser->svflags |= SVF_NOCLIENT;
253 laser->timeout = level.time + TBOMB_TIMEOUT;
254 gi.linkentity (laser);
255 }
256
use_tripbomb(edict_t * self,edict_t * other,edict_t * activator)257 void use_tripbomb(edict_t *self, edict_t *other, edict_t *activator)
258 {
259 if (self->chain)
260 {
261 // we already have a laser, remove it
262 G_FreeEdict(self->chain);
263 self->chain = NULL;
264 }
265 else
266 // create the laser
267 create_tripbomb_laser(self);
268 }
269
turnOffGlow(edict_t * self)270 void turnOffGlow(edict_t *self)
271 {
272 self->s.effects &= ~EF_COLOR_SHELL;
273 self->s.renderfx &= ~RF_SHELL_GREEN;
274 self->think = NULL;
275 self->nextthink = 0;
276 }
277
tripbomb_pain(edict_t * self,edict_t * other,float kick,int damage)278 void tripbomb_pain(edict_t *self, edict_t *other, float kick, int damage)
279 {
280 // play the green glow sound
281 //gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_glow.wav"), 1, ATTN_NORM, 0);
282
283 // turn on the glow
284 self->damage_debounce_time = level.time + 0.2;
285
286 // if we don't have a think function, then turn this thing on
287 if (self->think == NULL)
288 {
289 self->s.effects |= EF_COLOR_SHELL;
290 self->s.renderfx |= RF_SHELL_GREEN;
291 self->nextthink = self->damage_debounce_time;
292 self->think = turnOffGlow;
293 }
294 }
295
tripbomb_think(edict_t * self)296 void tripbomb_think(edict_t *self)
297 {
298 if (self->chain == NULL)
299 {
300 // check whether we need to create the laser
301 if (self->timeout < level.time)
302 {
303 create_tripbomb_laser(self);
304 }
305 }
306
307 // do we need to show damage?
308 if (self->damage_debounce_time > level.time)
309 {
310 self->s.effects |= EF_COLOR_SHELL;
311 self->s.renderfx |= RF_SHELL_GREEN;
312 }
313 else
314 {
315 self->s.effects &= ~EF_COLOR_SHELL;
316 self->s.renderfx &= ~RF_SHELL_GREEN;
317 }
318
319 self->nextthink = level.time + FRAMETIME;
320 }
321
setupBomb(edict_t * bomb,char * classname,float damage,float damage_radius)322 void setupBomb(edict_t *bomb, char *classname, float damage, float damage_radius)
323 {
324 bomb->classname = classname;
325 VectorSet(bomb->mins, -8, -8, -8);
326 VectorSet(bomb->maxs, 8, 8, 8);
327 bomb->solid = SOLID_BBOX;
328 bomb->movetype = MOVETYPE_NONE;
329 bomb->s.modelindex = gi.modelindex("models/objects/ired/tris.md2");
330 bomb->radius_dmg = damage;
331 bomb->dmg = damage;
332 bomb->dmg_radius = damage_radius;
333 bomb->health = 1;
334 bomb->takedamage = DAMAGE_IMMORTAL; // health will not be deducted
335 bomb->pain = tripbomb_pain;
336 }
337
removeOldest()338 void removeOldest()
339 {
340 edict_t *oldestEnt = NULL;
341 edict_t *e = NULL;
342 int count = 0;
343
344 while(1)
345 {
346 e = G_Find(e, FOFS(classname), "ired");
347 if (e == NULL) // no more
348 break;
349
350 count++;
351
352 if (oldestEnt == NULL ||
353 e->timestamp < oldestEnt->timestamp)
354 {
355 oldestEnt = e;
356 }
357 }
358
359 // do we have too many?
360 if (count > TBOMB_MAX_EXIST && oldestEnt != NULL)
361 {
362 // get this tbomb to explode
363 oldestEnt->think = TripBomb_Explode;
364 oldestEnt->nextthink = level.time + FRAMETIME;
365 G_FreeEdict(oldestEnt->chain);
366 }
367 }
368
fire_lasertripbomb(edict_t * self,vec3_t start,vec3_t dir,float timer,float damage,float damage_radius,qboolean quad)369 qboolean fire_lasertripbomb(edict_t *self, vec3_t start, vec3_t dir, float timer, float damage, float damage_radius, qboolean quad)
370 {
371 // trace a line
372 trace_t tr;
373 vec3_t endPos;
374 vec3_t _dir;
375 edict_t *bomb = NULL;
376 edict_t *laser = NULL;
377
378 VectorScale(dir, 64, _dir);
379 VectorAdd(start, _dir, endPos);
380
381 // trace ahead, looking for a wall
382 tr = gi.trace(start, NULL, NULL, endPos, self, MASK_SHOT);
383 if (tr.fraction == 1.0)
384 {
385 // not close enough
386 //gi.cprintf(self, PRINT_HIGH, "Not close enough to a wall");
387 return false;
388 }
389
390 if (Q_stricmp(tr.ent->classname, "worldspawn") != 0)
391 {
392 //gi.cprintf(self, PRINT_HIGH, "Hit something other than a wall");
393 return false;
394 }
395
396 // create the bomb
397 bomb = G_Spawn();
398 //VectorCopy(tr.endpos, bomb->s.origin);
399 VectorMA(tr.endpos, 3, tr.plane.normal, bomb->s.origin);
400 vectoangles(tr.plane.normal, bomb->s.angles);
401 bomb->owner = self;
402 setupBomb(bomb, "ired", damage, damage_radius);
403 gi.linkentity(bomb);
404
405 bomb->timestamp = level.time;
406 bomb->timeout = level.time + timer;
407 bomb->nextthink = level.time + FRAMETIME;
408 bomb->think = tripbomb_think;
409
410 // remove the oldest trip bomb
411 removeOldest();
412
413 // play a sound
414 gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_set.wav"), 1, ATTN_NORM, 0);
415 return true;
416 }
417
weapon_lasertripbomb_fire(edict_t * ent)418 void weapon_lasertripbomb_fire (edict_t *ent)
419 {
420 if (ent->client->ps.gunframe == 10)
421 {
422 vec3_t offset;
423 vec3_t forward;
424 vec3_t start;
425 int damage = TBOMB_DAMAGE;
426 float radius = TBOMB_RADIUS_DAMAGE;
427 if (is_quad)
428 damage *= 4;
429
430 // place the trip bomb
431 VectorSet(offset, 0, 0, ent->viewheight * 0.75);
432 AngleVectors (ent->client->v_angle, forward, NULL, NULL);
433 VectorAdd(ent->s.origin, offset, start);
434
435 if (fire_lasertripbomb(ent, start, forward, TBOMB_DELAY, damage, radius, is_quad))
436 {
437 ent->client->pers.inventory[ent->client->ammo_index] -= 1;
438
439 // switch models
440 ent->client->ps.gunindex = gi.modelindex("models/weapons/v_ired/hand.md2");
441
442 // play quad sound
443 playQuadSound(ent);
444 }
445 }
446 else if (ent->client->ps.gunframe == 15)
447 {
448 // switch models back
449 int mi = gi.modelindex("models/weapons/v_ired/tris.md2");
450 if (ent->client->ps.gunindex != mi)
451 {
452 ent->client->ps.gunindex = mi;
453 // go back to get another trip bomb
454 ent->client->ps.gunframe = 0;
455 return;
456 }
457 }
458 else if (ent->client->ps.gunframe == 6)
459 {
460 ent->client->ps.gunframe = 16;
461 return;
462 }
463
464 ent->client->ps.gunframe++;
465 }
466
Weapon_LaserTripBomb(edict_t * ent)467 void Weapon_LaserTripBomb(edict_t *ent)
468 {
469 static int pause_frames[] = {24, 33, 43, 0};
470 static int fire_frames[] = {6, 10, 15, 0};
471
472 const int deactivateFirst = 44;
473 const int deactivateLast = 48;
474 const int idleFirst = 16;
475 const int idleLast = 43;
476 const int fireFirst = 7;
477 const int fireLast = 15;
478 const int activateFirst = 0;
479 const int activateLast = 6;
480
481 if (ent->client->weaponstate == WEAPON_DROPPING)
482 {
483 if (ent->client->ps.gunframe == deactivateLast)
484 {
485 ChangeWeapon (ent);
486 return;
487 }
488
489 ent->client->ps.gunframe++;
490 return;
491 }
492
493 if (ent->client->weaponstate == WEAPON_ACTIVATING)
494 {
495 if (ent->client->ps.gunframe == activateLast)
496 {
497 ent->client->weaponstate = WEAPON_READY;
498 ent->client->ps.gunframe = idleFirst;
499 return;
500 }
501
502 ent->client->ps.gunframe++;
503 return;
504 }
505
506 if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING))
507 {
508 ent->client->weaponstate = WEAPON_DROPPING;
509 ent->client->ps.gunframe = deactivateFirst;
510 return;
511 }
512
513 if (ent->client->weaponstate == WEAPON_READY)
514 {
515 if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) )
516 {
517 ent->client->latched_buttons &= ~BUTTON_ATTACK;
518 if(ent->client->pers.inventory[ent->client->ammo_index])
519 {
520 ent->client->ps.gunframe = fireFirst;
521 ent->client->weaponstate = WEAPON_FIRING;
522
523 // start the animation
524 ent->client->anim_priority = ANIM_ATTACK;
525 if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
526 {
527 ent->s.frame = FRAME_crattak1-1;
528 ent->client->anim_end = FRAME_crattak9;
529 }
530 else
531 {
532 ent->s.frame = FRAME_attack1-1;
533 ent->client->anim_end = FRAME_attack8;
534 }
535 }
536 else
537 {
538 if (level.time >= ent->pain_debounce_time)
539 {
540 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
541 ent->pain_debounce_time = level.time + 1;
542 }
543 NoAmmoWeaponChange (ent);
544 }
545 }
546 else
547 {
548 if (ent->client->ps.gunframe == idleLast)
549 {
550 ent->client->ps.gunframe = idleFirst;
551 return;
552 }
553
554 if (pause_frames)
555 {
556 int n = 0;
557 for (n = 0; pause_frames[n]; n++)
558 {
559 if (ent->client->ps.gunframe == pause_frames[n])
560 {
561 if (rand()&15)
562 return;
563 }
564 }
565 }
566
567 ent->client->ps.gunframe++;
568 return;
569 }
570 }
571
572 if (ent->client->weaponstate == WEAPON_FIRING)
573 {
574 int n = 0;
575 for (n = 0; fire_frames[n]; n++)
576 {
577 if (ent->client->ps.gunframe == fire_frames[n])
578 {
579 weapon_lasertripbomb_fire(ent);
580 break;
581 }
582 }
583
584 if (!fire_frames[n])
585 ent->client->ps.gunframe++;
586
587 if (ent->client->ps.gunframe == idleFirst+1)
588 ent->client->weaponstate = WEAPON_READY;
589 }
590 }
591
SP_misc_lasertripbomb(edict_t * bomb)592 void SP_misc_lasertripbomb(edict_t *bomb)
593 {
594 // precache
595 gi.soundindex("weapons/ired/las_set.wav");
596 gi.soundindex("weapons/ired/las_arm.wav");
597 //gi.soundindex("weapons/ired/las_tink.wav");
598 //gi.soundindex("weapons/ired/las_trig.wav");
599 //gi.soundindex("weapons/ired/las_glow.wav");
600 gi.modelindex("models/objects/shrapnel/tris.md2");
601 gi.modelindex("models/objects/ired/tris.md2");
602
603 if (bomb->spawnflags & CHECK_BACK_WALL)
604 {
605 vec3_t forward, endPos;
606 trace_t tr;
607 // look backwards toward a wall
608 AngleVectors (bomb->s.angles, forward, NULL, NULL);
609 VectorMA(bomb->s.origin, -64.0, forward, endPos);
610 tr = gi.trace(bomb->s.origin, NULL, NULL, endPos, bomb, MASK_SOLID);
611 VectorCopy(tr.endpos, bomb->s.origin);
612 vectoangles(tr.plane.normal, bomb->s.angles);
613 }
614
615 // set up ourself
616 setupBomb(bomb, "misc_ired", TBOMB_DAMAGE, TBOMB_RADIUS_DAMAGE);
617
618 if (bomb->targetname)
619 {
620 bomb->use = use_tripbomb;
621 }
622 else
623 {
624 bomb->think = create_tripbomb_laser;
625 bomb->nextthink = level.time + TBOMB_DELAY;
626 }
627 gi.linkentity(bomb);
628 }
629
630 /*
631 ======================================================================
632
633 Sonic Cannon
634
635 ======================================================================
636 */
637
638
weapon_sc_fire(edict_t * ent)639 void weapon_sc_fire (edict_t *ent)
640 {
641 if (!(ent->client->buttons & BUTTON_ATTACK))
642 {
643 ent->client->ps.gunframe++;
644
645 if(ent->client->weapon_sound && ent->client->ps.gunframe < 18)
646 {
647 ent->client->ps.gunframe = 18;
648 }
649 }
650 else
651 {
652 if(EMPNukeCheck(ent, ent->s.origin))
653 {
654 gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
655
656 ent->client->ps.gunframe = 18;
657 ent->client->weapon_sound = 0;
658 ent->weaponsound_time = 0;
659
660 ent->dmg_radius = 0;
661 ent->client->startFireTime = 0;
662 return;
663 }
664
665 if(!ent->client->startFireTime)
666 {
667 ent->client->startFireTime = level.time;
668 }
669 else if(level.time - ent->client->startFireTime >= SC_MAXFIRETIME)
670 {
671 ent->client->ps.gunframe = 17;
672 }
673 else
674 {
675 int old_cells = (int)ent->dmg_radius;
676 ent->dmg_radius = ((level.time - ent->client->startFireTime) / SC_MAXFIRETIME) * SC_MAXCELLS;
677
678 if(old_cells < (int)ent->dmg_radius)
679 {
680 old_cells = (int)ent->dmg_radius - old_cells;
681
682
683 if(ent->client->pers.inventory[ent->client->ammo_index] < old_cells)
684 {
685 ent->dmg_radius -= (old_cells - ent->client->pers.inventory[ent->client->ammo_index]);
686 ent->client->pers.inventory[ent->client->ammo_index] = 0;
687 }
688 else
689 {
690 ent->client->pers.inventory[ent->client->ammo_index] -= old_cells;
691 }
692 }
693 }
694
695 if(!ent->client->pers.inventory[ent->client->ammo_index])
696 {
697 ent->client->ps.gunframe = 17;
698
699 if (level.time >= ent->pain_debounce_time)
700 {
701 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
702 ent->pain_debounce_time = level.time + 1;
703 }
704 NoAmmoWeaponChange (ent);
705 }
706 else
707 {
708 if(ent->weaponsound_time < level.time)
709 {
710 ent->client->weapon_sound = gi.soundindex("weapons/sonic/sc_fire.wav");
711 }
712 }
713
714 fire_sconnanEffects (ent);
715
716 ent->client->ps.gunframe++;
717 if (ent->client->ps.gunframe == 18 && (level.time - ent->client->startFireTime) < SC_MAXFIRETIME && ent->client->pers.inventory[ent->client->ammo_index])
718 ent->client->ps.gunframe = 12;
719 }
720
721 if (ent->client->ps.gunframe == 18)
722 {
723 ent->client->weapon_sound = 0;
724 ent->weaponsound_time = 0;
725
726 if(EMPNukeCheck(ent, ent->s.origin))
727 {
728 gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
729 }
730 else
731 {
732 if (!is_silenced)
733 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_cool.wav"), 1, ATTN_NORM, 0);
734 else
735 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_cool.wav"), 0.4, ATTN_NORM, 0);
736
737 if(ent->dmg_radius)
738 {
739 fire_sconnan (ent);
740 }
741 }
742
743 ent->dmg_radius = 0;
744 ent->client->startFireTime = 0;
745 }
746 }
747
748
749
750
Weapon_SonicCannon(edict_t * ent)751 void Weapon_SonicCannon (edict_t *ent)
752 {
753 static int pause_frames[] = {32, 42, 52, 0};
754 static int fire_frames[] = {12, 13, 14, 15, 16, 17, 0};
755
756 if (ent->client->ps.gunframe == 0)
757 {
758 if (deathmatch->value)
759 {
760 if (!is_silenced)
761 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_act.wav"), 1, ATTN_NORM, 0);
762 else
763 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_act.wav"), 0.4, ATTN_NORM, 0);
764 }
765 ent->weaponsound_time = 0;
766 ent->client->startFireTime = 0;
767 ent->dmg_radius = 0;
768 }
769 else if (ent->client->ps.gunframe == 53)
770 {
771 if (deathmatch->value)
772 {
773 if (!is_silenced)
774 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_dact.wav"), 1, ATTN_NORM, 0);
775 else
776 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_dact.wav"), 0.4, ATTN_NORM, 0);
777 }
778 }
779 else if((ent->client->buttons & BUTTON_ATTACK) && ent->weaponsound_time == 0)
780 {
781 ent->weaponsound_time = level.time + 0.4;
782
783 if (!is_silenced)
784 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_warm.wav"), 1, ATTN_NORM, 0);
785 else
786 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_warm.wav"), 0.4, ATTN_NORM, 0);
787 }
788
789 Weapon_Generic (ent, 6, 22, 52, 57, pause_frames, fire_frames, weapon_sc_fire);
790 }
791
792
793
794 void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage);
795
fire_sconnanEffects(edict_t * self)796 void fire_sconnanEffects (edict_t *self)
797 {
798 vec3_t start, end;
799 vec3_t forward, right;
800 vec3_t offset, v;
801 trace_t tr;
802
803 AngleVectors (self->client->v_angle, forward, right, NULL);
804
805 VectorScale (forward, -3, self->client->kick_origin);
806 self->client->kick_angles[0] = -3;
807
808 VectorSet(offset, 0, 7, self->viewheight-8);
809 P_ProjectSource (self->client, self->s.origin, offset, forward, right, start);
810
811 VectorMA (start, 8192, forward, end);
812
813 tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA);
814
815 VectorMA (tr.endpos, -5, forward, end);
816
817 VectorSet(v, crandom() * 10 - 20, crandom() * 10 - 20, crandom() * 10 - 20);
818 SpawnDamage(TE_SHIELD_SPARKS, end, v, 0);
819 }
820
821
822
scexplode_think(edict_t * self)823 void scexplode_think(edict_t *self)
824 {
825 gi.WriteByte (svc_temp_entity);
826 gi.WriteByte (TE_ROCKET_EXPLOSION);
827 gi.WritePosition (self->s.origin);
828 gi.multicast (self->s.origin, MULTICAST_PHS);
829
830 G_FreeEdict (self);
831 }
832
833
fire_sconnan(edict_t * self)834 void fire_sconnan (edict_t *self)
835 {
836 vec3_t start, end, explodepos;
837 vec3_t forward, right, up;
838 vec3_t offset;
839 trace_t tr;
840 float damage;
841 float radius;
842
843 damage = self->dmg_radius / SC_MAXCELLS;
844 radius = damage * SC_MAXRADIUS;
845 damage = SC_BASEDAMAGE + (damage * SC_DAMGERANGE);
846
847 AngleVectors (self->client->v_angle, forward, right, up);
848
849 VectorScale (forward, -3, self->client->kick_origin);
850 self->client->kick_angles[0] = -3;
851
852 VectorSet(offset, 0, 7, self->viewheight-8);
853 P_ProjectSource (self->client, self->s.origin, offset, forward, right, start);
854
855 VectorMA (start, 8192, forward, end);
856
857 tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA);
858
859 if ((tr.ent != self) && (tr.ent->takedamage))
860 {
861 T_Damage (tr.ent, self, self, forward, tr.endpos, tr.plane.normal, damage, 0, 0, MOD_SONICCANNON);
862 }
863
864 T_RadiusDamagePosition (tr.endpos, self, self, damage, tr.ent, radius, MOD_SONICCANNON);
865
866 VectorMA (tr.endpos, -5, forward, end);
867
868 gi.WriteByte (svc_temp_entity);
869 gi.WriteByte (TE_ROCKET_EXPLOSION);
870 gi.WritePosition (end);
871 gi.multicast (self->s.origin, MULTICAST_PHS);
872
873 damage -= 100;
874 radius = 0.1;
875
876 while(damage > 0)
877 {
878 edict_t *explode;
879
880 VectorMA (end, (50 * crandom()) - 5, forward, explodepos);
881 VectorMA (explodepos, (50 * crandom()) - 5, right, explodepos);
882 VectorMA (explodepos, (50 * crandom()) - 5, up, explodepos);
883
884 explode = G_Spawn();
885 VectorCopy (explodepos, explode->s.origin);
886
887 explode->classname = "sconnanExplode";
888 explode->nextthink = level.time + radius;
889 explode->think = scexplode_think;
890
891 radius += 0.1;
892 damage -= 100;
893 }
894
895 // play quad damage sound
896 playQuadSound(self);
897 }
898
899
900
901
902 /*
903 Flares
904 */
905 #define FLASH_RANGE 256.0
906 void FoundTarget (edict_t *self);
907
flare_flash(edict_t * ent)908 void flare_flash(edict_t *ent)
909 {
910 edict_t *target = NULL;
911
912 // flash
913 while (1)
914 {
915 float dist;
916 float ratio;
917 float dot;
918 vec3_t delta;
919 vec3_t forward;
920
921 // get the next entity near us
922 target = findradius(target, ent->s.origin, FLASH_RANGE);
923 if (target == NULL)
924 break;
925 if (!target->client && !(target->svflags & SVF_MONSTER))
926 continue;
927 if (target->deadflag)
928 continue;
929 if (!visible(ent, target))
930 continue;
931 //if (!infront(target, ent))
932 // continue;
933
934 // what's the distance, so that closer get's more
935 VectorSubtract(ent->s.origin, target->s.origin, delta);
936 dist = VectorLength(delta);
937 ratio = 1 - (dist/FLASH_RANGE);
938 if (ratio < 0)
939 ratio = 0;
940
941 // looking to the side get's less
942 AngleVectors(target->s.angles, forward, NULL, NULL);
943 VectorNormalize(delta);
944 dot = Z_MAX(0.0, DotProduct(delta, forward));
945 ratio *= dot;// * 1.25;
946
947 // set the flash counter
948 if (target->client)
949 {
950 target->client->flashTime += ratio*25;
951 if (target->client->flashTime > 25)
952 target->client->flashTime = 25;
953 target->client->flashBase = 30;
954
955 if (deathmatch->value &&
956 !target->client->pers.gl_polyblend &&
957 !(((int)zdmflags->value) & ZDM_NO_GL_POLYBLEND_DAMAGE))
958 T_Damage(target, ent, ent->owner, vec3_origin, target->s.origin, vec3_origin, (int)(10.0*ratio), 0, 0, MOD_GL_POLYBLEND);
959 }
960 else if ((target->svflags & SVF_MONSTER) && strcmp(target->classname, "monster_zboss") != 0)
961 {
962 target->monsterinfo.flashTime =
963 Z_MAX(target->monsterinfo.flashTime, ratio*150); // a little bit more advantageous
964 target->monsterinfo.flashBase = 50;
965 if (target->enemy == NULL)
966 {
967 target->enemy = ent->owner;
968 FoundTarget(target);
969 }
970 }
971 }
972 }
973
flare_think(edict_t * self)974 void flare_think(edict_t *self)
975 {
976 edict_t *target = NULL;
977 edict_t *closestEnt = NULL;
978 float closestDist = 0.0;
979
980 // on our last leg?
981 if (level.time > self->timeout)
982 {
983 self->s.effects &= ~EF_ROCKET;
984 self->think = G_FreeEdict;
985 self->nextthink = level.time + 4.0;
986 self->s.frame = 0;
987 self->s.sound = 0;
988 return;
989 }
990
991 self->s.frame++;
992
993 if (self->s.frame > 14)
994 self->s.frame = 5;
995
996 // hissing sound
997 self->s.sound = gi.soundindex ("weapons/flare/flarehis.wav");
998
999 // do the visual thing
1000 flare_flash(self);
1001
1002 // next frame
1003 self->nextthink = level.time + FRAMETIME;
1004 }
1005
fire_flare(edict_t * self,vec3_t start,vec3_t dir,int damage,int speed,float damage_radius,int radius_damage)1006 void fire_flare (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
1007 {
1008 edict_t *flare;
1009 vec3_t adir;
1010 vec3_t up;
1011
1012 vectoangles (dir, adir);
1013 AngleVectors (adir, NULL, NULL, up);
1014
1015 flare = G_Spawn();
1016 VectorCopy (start, flare->s.origin);
1017 VectorCopy (dir, flare->movedir);
1018 vectoangles (dir, flare->s.angles);
1019 VectorScale (dir, speed, flare->velocity);
1020 VectorMA (flare->velocity, 200 + crandom() * 10.0, up, flare->velocity);
1021 flare->movetype = MOVETYPE_BOUNCE;
1022 flare->clipmask = MASK_SHOT;
1023 flare->solid = SOLID_BBOX;
1024 flare->s.effects = EF_ROCKET;
1025 VectorSet(flare->mins, -4, -4, -4);
1026 VectorSet(flare->maxs, 4, 4, 4);
1027 flare->s.modelindex = gi.modelindex("models/objects/flare/tris.md2");
1028 flare->owner = self;
1029 flare->timeout = level.time + 8000/speed;
1030 flare->nextthink = level.time + 1.0;
1031 flare->think = flare_think;
1032 flare->dmg = damage;
1033 flare->radius_dmg = radius_damage;
1034 flare->dmg_radius = damage_radius;
1035 flare->classname = "flare";
1036
1037 if (self->client)
1038 check_dodge (self, flare->s.origin, dir, speed);
1039
1040 gi.linkentity (flare);
1041 }
1042
Weapon_FlareLauncher_Fire(edict_t * ent)1043 void Weapon_FlareLauncher_Fire (edict_t *ent)
1044 {
1045 vec3_t offset, start;
1046 vec3_t forward, right;
1047
1048 AngleVectors (ent->client->v_angle, forward, right, NULL);
1049
1050 VectorSet(offset, 8, 8, ent->viewheight-8);
1051 P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
1052 fire_flare(ent, start, forward, 1, 600, 1, 1);
1053
1054 ent->client->ps.gunframe++;
1055
1056 PlayerNoise(ent, start, PNOISE_WEAPON);
1057
1058 // play quad sound
1059 playQuadSound(ent);
1060
1061 if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
1062 ent->client->pers.inventory[ent->client->ammo_index]--;
1063
1064 // play shooting sound
1065 if (!is_silenced)
1066 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/flare/shoot.wav"), 1, ATTN_NORM, 0);
1067 else
1068 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/flare/shoot.wav"), 0.4, ATTN_NORM, 0);
1069 }
1070
Weapon_FlareGun(edict_t * ent)1071 void Weapon_FlareGun (edict_t *ent)
1072 {
1073 static int pause_frames[] = {15, 25, 35, 0};
1074 static int fire_frames[] = {8, 0};
1075
1076 Weapon_Generic (ent, 5, 14, 44, 48, pause_frames, fire_frames, Weapon_FlareLauncher_Fire);
1077 }
1078
1079 /******************************
1080 Sniper Rifle
1081 */
fire_sniper_bullet(edict_t * self,vec3_t start,vec3_t aimdir,int damage,int kick)1082 void fire_sniper_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
1083 {
1084 trace_t tr;
1085 vec3_t end;
1086 vec3_t s;
1087 edict_t *ignore = self;
1088 VectorMA (start, 8192, aimdir, end);
1089 VectorCopy(start, s);
1090 while(1)
1091 {
1092 tr = gi.trace (s, NULL, NULL, end, ignore, MASK_SHOT_NO_WINDOW);
1093 if (tr.fraction >= 1.0)
1094 return;
1095
1096 // if we hit a plasmashield, then pass thru it
1097 if (Q_stricmp(tr.ent->classname, "PlasmaShield") == 0)
1098 {
1099 ignore = tr.ent;
1100 VectorCopy(tr.endpos, s);
1101 }
1102 else
1103 break;
1104 }
1105
1106 gi.WriteByte (svc_temp_entity);
1107 if (gi.pointcontents(tr.endpos) & MASK_WATER)
1108 {
1109 if (tr.plane.normal[2] > 0.7)
1110 gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
1111 else
1112 gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
1113 }
1114 else
1115 {
1116 if (tr.plane.normal[2] > 0.7)
1117 gi.WriteByte (TE_GRENADE_EXPLOSION);
1118 else
1119 gi.WriteByte (TE_ROCKET_EXPLOSION);
1120 }
1121 gi.WritePosition (tr.endpos);
1122 gi.multicast (tr.endpos, MULTICAST_PHS);
1123
1124 if (tr.ent->takedamage)
1125 T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_NO_ARMOR, MOD_SNIPERRIFLE);
1126 }
1127
weapon_sniperrifle_fire(edict_t * ent)1128 void weapon_sniperrifle_fire (edict_t *ent)
1129 {
1130 vec3_t forward, right;
1131 vec3_t offset, start;
1132 int damage;
1133 int kick;
1134
1135 if (deathmatch->value)
1136 { // normal damage is too extreme in dm
1137 damage = 150;
1138 kick = 300;
1139 }
1140 else
1141 {
1142 damage = 250;
1143 kick = 400;
1144 }
1145
1146 if (is_quad)
1147 {
1148 damage *= 4;
1149 kick *= 4;
1150 }
1151
1152 AngleVectors (ent->client->v_angle, forward, right, NULL);
1153 // centre the shot
1154 VectorSet(offset, 0, 0, ent->viewheight);
1155 VectorAdd(ent->s.origin, offset, start);
1156 fire_sniper_bullet(ent, start, forward, damage, kick);
1157
1158 if (!is_silenced)
1159 gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/fire.wav"), 1, ATTN_NORM, 0);
1160 else
1161 gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/fire.wav"), 0.4, ATTN_NORM, 0);
1162
1163 PlayerNoise(ent, start, PNOISE_WEAPON);
1164
1165 // play quad sound
1166 playQuadSound(ent);
1167
1168 VectorScale (forward, -20, ent->client->kick_origin);
1169 ent->client->kick_angles[0] = -2;
1170 ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity;
1171 }
1172
1173 #define SNIPER_CHARGE_TIME 30
Weapon_SniperRifle(edict_t * ent)1174 void Weapon_SniperRifle(edict_t *ent)
1175 {
1176 /*
1177 Activate/Deactivate
1178 0 - 8 : Activate
1179 9 - 18 : Fire
1180 19 - 27 : Idle 1
1181 28 - 36 : Idle 2
1182 37 - 41 : Deactivate
1183
1184 Zoom
1185 0 - 1 Zoom
1186 Hold 1 while zoomed
1187 */
1188 const static int activateStart = 0;
1189 const static int activateEnd = 8;
1190 const static int deactivateStart = 37;
1191 const static int deactivateEnd = 41;
1192 const static int spFov = 15;
1193 const static int dmFov = 30;
1194
1195 if (ent->client->weaponstate == WEAPON_DROPPING)
1196 {
1197 ent->client->sniperFramenum = 0;
1198 if (ent->client->ps.gunframe == deactivateStart)
1199 {
1200 // back to old fov
1201 ent->client->ps.fov = 90;
1202 if (deathmatch->value)
1203 gi.sound(ent, CHAN_WEAPON2, gi.soundindex("weapons/sniper/snip_bye.wav"), 1, ATTN_NORM, 0);
1204 }
1205 else if (ent->client->ps.gunframe == deactivateEnd)
1206 {
1207 ChangeWeapon(ent);
1208 return;
1209 }
1210
1211 ent->client->ps.gunframe++;
1212 return;
1213 }
1214
1215 if (ent->client->weaponstate == WEAPON_ACTIVATING)
1216 {
1217 if (ent->client->ps.gunframe == activateStart)
1218 {
1219 // play the activation sound
1220 if (deathmatch->value)
1221 gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/snip_act.wav"), 1, ATTN_NORM, 0);
1222 }
1223 else if (ent->client->ps.gunframe == activateEnd)
1224 {
1225 ent->client->weaponstate = WEAPON_READY;
1226 ent->client->ps.gunindex = (deathmatch->value ?
1227 gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") :
1228 gi.modelindex("models/weapons/v_sniper/scope/tris.md2") );
1229 ent->client->ps.gunframe = 0;
1230 ent->client->ps.fov = (deathmatch->value ? dmFov : spFov);
1231 ent->client->sniperFramenum = level.framenum + SNIPER_CHARGE_TIME;
1232 return;
1233 }
1234
1235 ent->client->ps.gunframe++;
1236 return;
1237 }
1238
1239 if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING))
1240 {
1241 // back to other gun model
1242 ent->client->ps.gunindex = gi.modelindex("models/weapons/v_sniper/tris.md2");
1243 ent->client->weaponstate = WEAPON_DROPPING;
1244 ent->client->ps.gunframe = deactivateStart;
1245 return;
1246 }
1247
1248 if (ent->client->weaponstate == WEAPON_READY)
1249 {
1250 ent->client->ps.gunindex = (deathmatch->value ?
1251 gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") :
1252 gi.modelindex("models/weapons/v_sniper/scope/tris.md2") );
1253
1254 ent->client->ps.fov = (deathmatch->value ? dmFov : spFov);
1255
1256 // beep if the sniper frame num is a multiple of 10
1257 if (ent->client->sniperFramenum >= level.framenum)
1258 {
1259 if ((ent->client->sniperFramenum - level.framenum) % 10 == 1)
1260 gi.sound(ent, CHAN_WEAPON2, gi.soundindex("weapons/sniper/beep.wav"), 1, ATTN_NORM, 0);
1261 }
1262
1263 if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) )
1264 {
1265 if (level.framenum >= ent->client->sniperFramenum)
1266 {
1267 ent->client->latched_buttons &= ~BUTTON_ATTACK;
1268 if ((!ent->client->ammo_index) ||
1269 ( ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity))
1270 {
1271 ent->client->weaponstate = WEAPON_FIRING;
1272
1273 // start the animation
1274 ent->client->anim_priority = ANIM_ATTACK;
1275 if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
1276 {
1277 ent->s.frame = FRAME_crattak1-1;
1278 ent->client->anim_end = FRAME_crattak9;
1279 }
1280 else
1281 {
1282 ent->s.frame = FRAME_attack1-1;
1283 ent->client->anim_end = FRAME_attack8;
1284 }
1285 }
1286 else
1287 {
1288 if (level.time >= ent->pain_debounce_time)
1289 {
1290 gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
1291 ent->pain_debounce_time = level.time + 1;
1292 }
1293 NoAmmoWeaponChange (ent);
1294 }
1295 }
1296 }
1297 }
1298
1299 if (ent->client->weaponstate == WEAPON_FIRING)
1300 {
1301 ent->client->ps.gunindex = (deathmatch->value ?
1302 gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") :
1303 gi.modelindex("models/weapons/v_sniper/scope/tris.md2") );
1304
1305 ent->client->ps.fov = (deathmatch->value ? dmFov : spFov);
1306 //if (ent->client->quad_framenum > level.framenum)
1307 // gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
1308
1309 // fire
1310 weapon_sniperrifle_fire(ent);
1311
1312 // start recharge
1313 ent->client->weaponstate = WEAPON_READY;
1314 ent->client->sniperFramenum = level.framenum + SNIPER_CHARGE_TIME;
1315 }
1316 }
1317
1318 /*****************************
1319 Armageddon 2000
1320 */
1321
weapon_a2k_exp_think(edict_t * self)1322 void weapon_a2k_exp_think(edict_t *self)
1323 {
1324 self->s.frame++;
1325 self->s.skinnum++;
1326
1327 if (self->s.frame == 6)
1328 {
1329 G_FreeEdict(self);
1330 return;
1331 }
1332 self->nextthink = level.time + FRAMETIME;
1333 }
1334
Z_RadiusDamageVisible(edict_t * inflictor,edict_t * attacker,float damage,edict_t * ignore,float radius,int mod)1335 void Z_RadiusDamageVisible(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
1336 {
1337 float points;
1338 edict_t *ent = NULL;
1339 vec3_t v;
1340 vec3_t dir;
1341
1342 while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
1343 {
1344 if (ent == ignore)
1345 continue;
1346 if (!ent->takedamage)
1347 continue;
1348 if (!visible(inflictor, ent))
1349 continue;
1350
1351 VectorAdd (ent->mins, ent->maxs, v);
1352 VectorMA (ent->s.origin, 0.5, v, v);
1353 VectorSubtract (inflictor->s.origin, v, v);
1354 points = damage - 0.5 * VectorLength (v);
1355 if (ent == attacker)
1356 points = points * 0.5;
1357 if (points > 0)
1358 {
1359 if (CanDamage (ent, inflictor))
1360 {
1361 VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
1362 T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
1363 }
1364 }
1365 }
1366 }
1367
1368
1369 #define A2K_FRAMENUM 50
weapon_a2k_fire(edict_t * ent)1370 void weapon_a2k_fire (edict_t *ent)
1371 {
1372 if (ent->client->ps.gunframe == 14)
1373 {
1374 ent->client->a2kFramenum = level.framenum + A2K_FRAMENUM;
1375 ent->client->pers.inventory[ent->client->ammo_index]--;
1376 ent->client->ps.gunframe++;
1377
1378 // start scream sound
1379 //ent->client->weapon_sound = gi.soundindex("weapons/a2k/countdn.wav");
1380 gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/a2k/countdn.wav"), 1, ATTN_NORM, 0);
1381 //gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/a2k/ak_trig.wav"), 1, ATTN_NORM, 0);
1382
1383 // play quad sound
1384 playQuadSound(ent);
1385 }
1386 else if (ent->client->a2kFramenum == level.framenum)
1387 {
1388 // boom
1389 edict_t *exp = NULL;
1390 float damage = 2500;
1391 float dmg_radius = 512;
1392 edict_t *e = NULL;
1393 // play quad sound
1394 playQuadSound(ent);
1395 if (is_quad)
1396 {
1397 damage *= 4;
1398 dmg_radius *= 4;
1399 }
1400 // do some damage
1401 T_RadiusDamage(ent, ent, damage, NULL, dmg_radius, MOD_A2K);
1402
1403 // ok, now, do who ever's visible within 1024 units
1404 Z_RadiusDamageVisible(ent, ent, damage, NULL, dmg_radius * 2, MOD_A2K);
1405
1406 exp = G_Spawn();
1407 exp->classname = "A2K Explosion";
1408 exp->solid = SOLID_NOT;
1409 exp->movetype = MOVETYPE_NONE;
1410 VectorClear(exp->mins);
1411 VectorClear(exp->maxs);
1412 VectorCopy(ent->s.origin, exp->s.origin);
1413 exp->s.modelindex = gi.modelindex("models/objects/b_explode/tris.md2");
1414 exp->s.frame = 0;
1415 exp->s.skinnum = 6;
1416 exp->think = weapon_a2k_exp_think;
1417 exp->nextthink = level.time + FRAMETIME;
1418 gi.linkentity(exp);
1419 gi.positioned_sound(exp->s.origin, exp, CHAN_AUTO, gi.soundindex("weapons/a2k/ak_exp01.wav"), 1, ATTN_NORM, 0);
1420 ent->client->ps.gunframe++;
1421 ent->client->weapon_sound = 0;
1422 return;
1423 }
1424 else if (ent->client->ps.gunframe == 19)
1425 {
1426 // don't increase the gunframe
1427 return;
1428 }
1429 }
1430
Weapon_A2k(edict_t * ent)1431 void Weapon_A2k (edict_t *ent)
1432 {
1433 /*
1434 00-09 Active
1435 10-19 Boom (14 actual fire frame)
1436 20-29 Idle1
1437 30-39 Idle2
1438 40-49 Idle3
1439 50-55 Away
1440 */
1441 static int pause_frames[] = {20, 30, 40, 0};
1442 static int fire_frames[] = {14, 19, 0};
1443
1444 Weapon_Generic (ent, 9, 19, 49, 55, pause_frames, fire_frames, weapon_a2k_fire);
1445 }
1446
1447 /********************************************
1448 Push
1449
1450 0 - Start push
1451 4 - Contact
1452 8 - End Push
1453 */
1454
push_hit(edict_t * self,vec3_t start,vec3_t aim,int damage,int kick)1455 qboolean push_hit (edict_t *self, vec3_t start, vec3_t aim, int damage, int kick)
1456 {
1457 trace_t tr;
1458 vec3_t end;
1459 vec3_t v;
1460 edict_t *e = NULL;
1461
1462 //see if enemy is in range
1463 VectorMA(start, 64, aim, end);
1464 tr = gi.trace(start, NULL, NULL, end, self, MASK_SHOT);
1465 if (tr.fraction >= 1)
1466 return false;
1467
1468 // play sound
1469 gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/push/contact.wav"), 1, ATTN_NORM, 0);
1470
1471 if (tr.ent->svflags & SVF_MONSTER ||
1472 tr.ent->client)
1473 {
1474 // do our special form of knockback here
1475 VectorMA (tr.ent->absmin, 0.75, tr.ent->size, v);
1476 VectorSubtract (v, start, v);
1477 VectorNormalize (v);
1478 VectorMA (tr.ent->velocity, kick, v, tr.ent->velocity);
1479 //tr.ent->velocity[2] = 48;
1480 if (tr.ent->velocity[2] > 0)
1481 tr.ent->groundentity = NULL;
1482 }
1483 else if (tr.ent->movetype == MOVETYPE_FALLFLOAT)
1484 {
1485 if (tr.ent->touch)
1486 {
1487 float mass = tr.ent->mass;
1488 tr.ent->mass *= 0.25;
1489 tr.ent->touch(tr.ent, self, NULL, NULL);
1490 tr.ent->mass = mass;
1491 }
1492 }
1493
1494 // ok, we hit something, damage it
1495 if (!tr.ent->takedamage)
1496 return false;
1497
1498 // do the damage
1499 T_Damage (tr.ent, self, self, aim, tr.endpos, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT);
1500
1501 return true;
1502 }
1503
Action_Push(edict_t * ent)1504 void Action_Push (edict_t *ent)
1505 {
1506 if (ent->client->ps.gunframe == 0)
1507 {
1508 // play grunt sound
1509 //gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/push/grunt.wav"), 1, ATTN_NORM, 0);
1510 ent->client->ps.gunframe++;
1511 }
1512 else if (ent->client->ps.gunframe == 4)
1513 {
1514 vec3_t forward;
1515 vec3_t offset;
1516 vec3_t start;
1517
1518 // contact
1519 AngleVectors(ent->client->v_angle, forward, NULL, NULL);
1520 VectorSet(offset, 0, 0, ent->viewheight * 0.5);
1521 VectorAdd(ent->s.origin, offset, start);
1522 push_hit(ent, start, forward, 2, 512);
1523 ent->client->ps.gunframe++;
1524 }
1525 else if (ent->client->ps.gunframe == 8)
1526 {
1527 // go back to old weapon
1528 ent->client->newweapon = ent->client->pers.lastweapon;
1529 ChangeWeapon(ent);
1530 }
1531 else
1532 ent->client->ps.gunframe++;
1533 }
1534
1535