30 static void check_dodge(edict_t *
self, vec3_t start, vec3_t dir,
int speed)
38 if (
skill->value == 0) {
42 VectorMA(start, 8192, dir, end);
43 tr =
gi.trace(start, NULL, NULL, end,
self, MASK_SHOT);
44 if ((tr.ent) && (tr.ent->svflags & SVF_MONSTER) && (tr.ent->health > 0) && (tr.ent->monsterinfo.dodge) &&
infront(tr.ent,
self)) {
45 VectorSubtract(tr.endpos, start, v);
46 eta = (VectorLength(v) - tr.ent->maxs[0]) / speed;
47 tr.ent->monsterinfo.dodge(tr.ent,
self, eta);
59 qboolean
fire_hit(edict_t *
self, vec3_t aim,
int damage,
int kick)
69 VectorSubtract(self->enemy->s.origin, self->s.origin, dir);
70 range = VectorLength(dir);
74 if (aim[1] > self->mins[0] && aim[1] < self->maxs[0]) {
76 range -=
self->enemy->maxs[0];
80 aim[1] =
self->enemy->mins[0];
82 aim[1] =
self->enemy->maxs[0];
85 VectorMA(self->s.origin,
range, dir, point);
87 tr =
gi.trace(self->s.origin, NULL, NULL, point,
self, MASK_SHOT);
88 if (tr.fraction < 1) {
89 if (!tr.ent->takedamage)
92 if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
98 VectorMA(point, aim[1],
right, point);
99 VectorMA(point, aim[2],
up, point);
100 VectorSubtract(point, self->enemy->s.origin, dir);
105 if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
109 VectorMA(self->enemy->absmin, 0.5, self->enemy->size, v);
110 VectorSubtract(v, point, v);
112 VectorMA(self->enemy->velocity, kick, v, self->enemy->velocity);
113 if (self->enemy->velocity[2] > 0)
114 self->enemy->groundentity = NULL;
126 static void fire_lead(edict_t *
self, vec3_t start, vec3_t aimdir,
int damage,
int kick,
int te_impact,
int hspread,
int vspread,
int mod)
135 qboolean water = qfalse;
136 int content_mask = MASK_SHOT | MASK_WATER;
138 tr =
gi.trace(self->s.origin, NULL, NULL, start,
self, MASK_SHOT);
139 if (!(tr.fraction < 1.0)) {
145 VectorMA(start, 8192,
forward, end);
146 VectorMA(end, r,
right, end);
147 VectorMA(end, u,
up, end);
149 if (
gi.pointcontents(start) & MASK_WATER) {
151 VectorCopy(start, water_start);
152 content_mask &= ~MASK_WATER;
155 tr =
gi.trace(start, NULL, NULL, end,
self, content_mask);
158 if (tr.contents & MASK_WATER) {
162 VectorCopy(tr.endpos, water_start);
164 if (!VectorCompare(start, tr.endpos)) {
165 if (tr.contents & CONTENTS_WATER) {
166 if (strcmp(tr.surface->name,
"*brwater") == 0)
167 color = SPLASH_BROWN_WATER;
169 color = SPLASH_BLUE_WATER;
170 }
else if (tr.contents & CONTENTS_SLIME)
171 color = SPLASH_SLIME;
172 else if (tr.contents & CONTENTS_LAVA)
175 color = SPLASH_UNKNOWN;
177 if (
color != SPLASH_UNKNOWN) {
179 gi.WriteByte(TE_SPLASH);
181 gi.WritePosition(tr.endpos);
182 gi.WriteDir(tr.plane.normal);
184 gi.multicast(tr.endpos, MULTICAST_PVS);
188 VectorSubtract(end, start, dir);
193 VectorMA(water_start, 8192,
forward, end);
194 VectorMA(end, r,
right, end);
195 VectorMA(end, u,
up, end);
199 tr =
gi.trace(water_start, NULL, NULL, end,
self, MASK_SHOT);
204 if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) {
205 if (tr.fraction < 1.0) {
206 if (tr.ent->takedamage) {
209 if (strncmp(tr.surface->name,
"sky", 3) != 0) {
211 gi.WriteByte(te_impact);
212 gi.WritePosition(tr.endpos);
213 gi.WriteDir(tr.plane.normal);
214 gi.multicast(tr.endpos, MULTICAST_PVS);
227 VectorSubtract(tr.endpos, water_start, dir);
229 VectorMA(tr.endpos, -2, dir, pos);
230 if (
gi.pointcontents(pos) & MASK_WATER)
231 VectorCopy(pos, tr.endpos);
233 tr =
gi.trace(pos, NULL, NULL, water_start, tr.ent, MASK_WATER);
235 VectorAdd(water_start, tr.endpos, pos);
236 VectorScale(pos, 0.5, pos);
239 gi.WriteByte(TE_BUBBLETRAIL);
240 gi.WritePosition(water_start);
241 gi.WritePosition(tr.endpos);
242 gi.multicast(pos, MULTICAST_PVS);
255 void fire_bullet(edict_t *
self, vec3_t start, vec3_t aimdir,
int damage,
int kick,
int hspread,
int vspread,
int mod)
257 fire_lead(
self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod);
268 void fire_shotgun(edict_t *
self, vec3_t start, vec3_t aimdir,
int damage,
int kick,
int hspread,
int vspread,
int count,
int mod)
272 for (i = 0; i < count; i++)
273 fire_lead(
self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod);
288 if (
other == self->owner)
291 if (surf && (surf->flags & SURF_SKY)) {
296 if (self->owner->client)
299 if (
other->takedamage) {
300 if (self->spawnflags & 1)
307 gi.WriteByte(TE_BLASTER);
308 gi.WritePosition(self->s.origin);
312 gi.WriteDir(plane->normal);
313 gi.multicast(self->s.origin, MULTICAST_PVS);
319 void fire_blaster(edict_t *
self, vec3_t start, vec3_t dir,
int damage,
int speed,
int effect, qboolean hyper)
327 bolt->svflags = SVF_DEADMONSTER;
333 VectorCopy(start, bolt->s.origin);
334 VectorCopy(start, bolt->s.old_origin);
336 VectorScale(dir, speed, bolt->velocity);
338 bolt->clipmask = MASK_SHOT;
339 bolt->solid = SOLID_BBOX;
340 bolt->s.effects |= effect;
341 VectorClear(bolt->mins);
342 VectorClear(bolt->maxs);
343 bolt->s.modelindex =
gi.modelindex(
"models/objects/laser/tris.md2");
344 bolt->s.sound =
gi.soundindex(
"misc/lasfly.wav");
350 bolt->classname =
"bolt";
352 bolt->spawnflags = 1;
358 tr =
gi.trace(self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
359 if (tr.fraction < 1.0) {
360 VectorMA(bolt->s.origin, -10, dir, bolt->s.origin);
361 bolt->touch(bolt, tr.ent, NULL, NULL);
376 if (ent->owner->client)
385 VectorAdd(ent->enemy->mins, ent->enemy->maxs, v);
386 VectorMA(ent->enemy->s.origin, 0.5, v, v);
387 VectorSubtract(ent->s.origin, v, v);
388 points = ent->dmg - 0.5 * VectorLength(v);
389 VectorSubtract(ent->enemy->s.origin, ent->s.origin, dir);
390 if (ent->spawnflags & 1)
397 if (ent->spawnflags & 2)
399 else if (ent->spawnflags & 1)
403 T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, mod);
405 VectorMA(ent->s.origin, -0.02, ent->velocity,
origin);
407 if (ent->waterlevel) {
408 if (ent->groundentity)
409 gi.WriteByte(TE_GRENADE_EXPLOSION_WATER);
411 gi.WriteByte(TE_ROCKET_EXPLOSION_WATER);
413 if (ent->groundentity)
414 gi.WriteByte(TE_GRENADE_EXPLOSION);
416 gi.WriteByte(TE_ROCKET_EXPLOSION);
419 gi.multicast(ent->s.origin, MULTICAST_PHS);
426 if (
other == ent->owner)
429 if (surf && (surf->flags & SURF_SKY)) {
434 if (!
other->takedamage) {
435 if (ent->spawnflags & 1) {
437 gi.sound(ent, CHAN_VOICE,
gi.soundindex(
"weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
439 gi.sound(ent, CHAN_VOICE,
gi.soundindex(
"weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
441 gi.sound(ent, CHAN_VOICE,
gi.soundindex(
"weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
450 void fire_grenade(edict_t *
self, vec3_t start, vec3_t aimdir,
int damage,
int speed,
float timer,
float damage_radius)
460 VectorCopy(start, grenade->s.origin);
461 VectorScale(aimdir, speed, grenade->velocity);
462 VectorMA(grenade->velocity, 200 +
crandom() * 10.0,
up, grenade->velocity);
463 VectorMA(grenade->velocity,
crandom() * 10.0,
right, grenade->velocity);
464 VectorSet(grenade->avelocity, 300, 300, 300);
466 grenade->clipmask = MASK_SHOT;
467 grenade->solid = SOLID_BBOX;
468 grenade->s.effects |= EF_GRENADE;
469 VectorClear(grenade->mins);
470 VectorClear(grenade->maxs);
471 grenade->s.modelindex =
gi.modelindex(
"models/objects/grenade/tris.md2");
472 grenade->owner =
self;
476 grenade->dmg = damage;
477 grenade->dmg_radius = damage_radius;
478 grenade->classname =
"grenade";
480 gi.linkentity(grenade);
483 void fire_grenade2(edict_t *
self, vec3_t start, vec3_t aimdir,
int damage,
int speed,
float timer,
float damage_radius, qboolean held)
493 VectorCopy(start, grenade->s.origin);
494 VectorScale(aimdir, speed, grenade->velocity);
495 VectorMA(grenade->velocity, 200 +
crandom() * 10.0,
up, grenade->velocity);
496 VectorMA(grenade->velocity,
crandom() * 10.0,
right, grenade->velocity);
497 VectorSet(grenade->avelocity, 300, 300, 300);
499 grenade->clipmask = MASK_SHOT;
500 grenade->solid = SOLID_BBOX;
501 grenade->s.effects |= EF_GRENADE;
502 VectorClear(grenade->mins);
503 VectorClear(grenade->maxs);
504 grenade->s.modelindex =
gi.modelindex(
"models/objects/grenade2/tris.md2");
505 grenade->owner =
self;
509 grenade->dmg = damage;
510 grenade->dmg_radius = damage_radius;
511 grenade->classname =
"hgrenade";
513 grenade->spawnflags = 3;
515 grenade->spawnflags = 1;
516 grenade->s.sound =
gi.soundindex(
"weapons/hgrenc1b.wav");
521 gi.sound(
self, CHAN_WEAPON,
gi.soundindex(
"weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
522 gi.linkentity(grenade);
537 if (
other == ent->owner)
540 if (surf && (surf->flags & SURF_SKY)) {
545 if (ent->owner->client)
549 VectorMA(ent->s.origin, -0.02, ent->velocity,
origin);
551 if (
other->takedamage) {
556 if ((surf) && !(surf->flags & (SURF_WARP | SURF_TRANS33 | SURF_TRANS66 | SURF_FLOWING))) {
559 ThrowDebris(ent,
"models/objects/debris2/tris.md2", 2, ent->s.origin);
568 gi.WriteByte(TE_ROCKET_EXPLOSION_WATER);
570 gi.WriteByte(TE_ROCKET_EXPLOSION);
572 gi.multicast(ent->s.origin, MULTICAST_PHS);
577 void fire_rocket(edict_t *
self, vec3_t start, vec3_t dir,
int damage,
int speed,
float damage_radius,
int radius_damage)
582 VectorCopy(start, rocket->s.origin);
583 VectorCopy(dir, rocket->movedir);
585 VectorScale(dir, speed, rocket->velocity);
587 rocket->clipmask = MASK_SHOT;
588 rocket->solid = SOLID_BBOX;
589 rocket->s.effects |= EF_ROCKET;
590 VectorClear(rocket->mins);
591 VectorClear(rocket->maxs);
592 rocket->s.modelindex =
gi.modelindex(
"models/objects/rocket/tris.md2");
593 rocket->owner =
self;
595 rocket->nextthink =
level.
time + 8000 / speed;
597 rocket->dmg = damage;
598 rocket->radius_dmg = radius_damage;
599 rocket->dmg_radius = damage_radius;
600 rocket->s.sound =
gi.soundindex(
"weapons/rockfly.wav");
601 rocket->classname =
"rocket";
606 gi.linkentity(rocket);
615 void fire_rail(edict_t *
self, vec3_t start, vec3_t aimdir,
int damage,
int kick)
624 VectorMA(start, 8192, aimdir, end);
625 VectorCopy(start, from);
628 mask = MASK_SHOT | CONTENTS_SLIME | CONTENTS_LAVA;
630 tr =
gi.trace(from, NULL, NULL, end, ignore, mask);
632 if (tr.contents & (CONTENTS_SLIME | CONTENTS_LAVA)) {
633 mask &= ~(CONTENTS_SLIME | CONTENTS_LAVA);
637 if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client) ||
638 (tr.ent->solid == SOLID_BBOX))
643 if ((tr.ent !=
self) && (tr.ent->takedamage))
644 T_Damage(tr.ent,
self,
self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0,
MOD_RAILGUN);
647 VectorCopy(tr.endpos, from);
652 gi.WriteByte(TE_RAILTRAIL);
653 gi.WritePosition(start);
654 gi.WritePosition(tr.endpos);
655 gi.multicast(self->s.origin, MULTICAST_PHS);
659 gi.WriteByte(TE_RAILTRAIL);
660 gi.WritePosition(start);
661 gi.WritePosition(tr.endpos);
662 gi.multicast(tr.endpos, MULTICAST_PHS);
682 if (self->s.frame == 0) {
685 while ((ent =
findradius(ent, self->s.origin, self->dmg_radius)) != NULL) {
686 if (!ent->takedamage)
688 if (ent == self->owner)
695 VectorAdd(ent->mins, ent->maxs, v);
696 VectorMA(ent->s.origin, 0.5, v, v);
697 VectorSubtract(self->s.origin, v, v);
698 dist = VectorLength(v);
699 points =
self->radius_dmg * (1.0 - sqrt(dist / self->dmg_radius));
700 if (ent == self->owner)
701 points = points * 0.5;
704 gi.WriteByte(TE_BFG_EXPLOSION);
705 gi.WritePosition(ent->s.origin);
706 gi.multicast(ent->s.origin, MULTICAST_PHS);
713 if (self->s.frame == 5)
719 if (
other == self->owner)
722 if (surf && (surf->flags & SURF_SKY)) {
727 if (self->owner->client)
731 if (
other->takedamage)
735 gi.sound(
self, CHAN_VOICE,
gi.soundindex(
"weapons/bfg__x1b.wav"), 1, ATTN_NORM, 0);
736 self->solid = SOLID_NOT;
738 VectorMA(self->s.origin, -1 *
FRAMETIME, self->velocity, self->s.origin);
739 VectorClear(self->velocity);
740 self->s.modelindex =
gi.modelindex(
"sprites/s_bfg3.sp2");
743 self->s.effects &= ~EF_ANIM_ALLFAST;
749 gi.WriteByte(TE_BFG_BIGEXPLOSION);
750 gi.WritePosition(self->s.origin);
751 gi.multicast(self->s.origin, MULTICAST_PVS);
772 while ((ent =
findradius(ent, self->s.origin, 256)) != NULL) {
776 if (ent == self->owner)
779 if (!ent->takedamage)
782 if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname,
"misc_explobox") != 0))
785 VectorMA(ent->absmin, 0.5, ent->size, point);
787 VectorSubtract(point, self->s.origin, dir);
791 VectorCopy(self->s.origin, start);
792 VectorMA(start, 2048, dir, end);
794 tr =
gi.trace(start, NULL, NULL, end, ignore, CONTENTS_SOLID | CONTENTS_MONSTER | CONTENTS_DEADMONSTER);
800 if ((tr.ent->takedamage) && !(tr.ent->flags &
FL_IMMUNE_LASER) && (tr.ent != self->owner))
804 if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client)) {
806 gi.WriteByte(TE_LASER_SPARKS);
808 gi.WritePosition(tr.endpos);
809 gi.WriteDir(tr.plane.normal);
810 gi.WriteByte(self->s.skinnum);
811 gi.multicast(tr.endpos, MULTICAST_PVS);
816 VectorCopy(tr.endpos, start);
820 gi.WriteByte(TE_BFG_LASER);
821 gi.WritePosition(self->s.origin);
822 gi.WritePosition(tr.endpos);
823 gi.multicast(self->s.origin, MULTICAST_PHS);
830 void fire_bfg(edict_t *
self, vec3_t start, vec3_t dir,
int damage,
int speed,
float damage_radius)
835 VectorCopy(start, bfg->s.origin);
836 VectorCopy(dir, bfg->movedir);
838 VectorScale(dir, speed, bfg->velocity);
840 bfg->clipmask = MASK_SHOT;
841 bfg->solid = SOLID_BBOX;
842 bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST;
843 VectorClear(bfg->mins);
844 VectorClear(bfg->maxs);
845 bfg->s.modelindex =
gi.modelindex(
"sprites/s_bfg1.sp2");
848 bfg->nextthink =
level.
time + 8000 / speed;
850 bfg->radius_dmg = damage;
851 bfg->dmg_radius = damage_radius;
852 bfg->classname =
"bfg blast";
853 bfg->s.sound =
gi.soundindex(
"weapons/bfg__l1a.wav");
857 bfg->teammaster = bfg;
858 bfg->teamchain = NULL;
878 gi.WriteByte(TE_FLARE);
882 gi.WriteByte( self->timestamp -
level.
time < 14.75 ? 0 : 1);
884 gi.WritePosition(self->s.origin);
889 if (VectorLength(self->velocity) > 0.0)
902 gi.multicast(self->s.origin, MULTICAST_PVS);
948 cplane_t *plane, csurface_t *surf)
953 VectorClear(ent->velocity);
957 int damage,
int speed,
float timer,
968 VectorCopy(start, flare->s.origin);
969 VectorScale(aimdir, speed, flare->velocity);
970 VectorSet(flare->avelocity, 300, 300, 300);
972 flare->clipmask = MASK_SHOT;
973 flare->solid = SOLID_BBOX;
975 const float size = 4;
976 VectorSet(flare->mins, -size, -size, -size);
977 VectorSet(flare->maxs, size, size, size);
979 flare->s.modelindex =
gi.modelindex(
"models/objects/flare/tris.md2");
984 flare->radius_dmg = damage;
985 flare->dmg_radius = damage_radius;
986 flare->classname =
"flare";
988 gi.linkentity(flare);