/* Copyright (C) 1997-2001 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "g_local.h" // bullet spreads //#define DEFAULT_SHOTGUN_HSPREAD 1000 //#define DEFAULT_SHOTGUN_VSPREAD 500 /* // //================= //check_dodge // //This is a support routine used when a client is firing //a non-instant attack weapon. It checks to see if a //monster's dodge function should be called. //================= // static void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed) { vec3_t end; vec3_t v; trace_t tr; float eta; // easy mode only ducks one quarter the time if (skill->integer == 0) { if (random() > 0.25) return; } VectorMA (start, 8192, dir, end); trap_Trace (&tr, start, NULL, NULL, end, self, MASK_SHOT); if ((tr.fraction < 1) && (game.edicts[tr.ent].r.svflags & SVF_MONSTER) && (game.edicts[tr.ent].health > 0) && (game.edicts[tr.ent].monsterinfo.dodge) && infront(&game.edicts[tr.ent], self)) { VectorSubtract (tr.endpos, start, v); eta = (VectorLength(v) - game.edicts[tr.ent].r.maxs[0]) / speed; game.edicts[tr.ent].monsterinfo.dodge (&game.edicts[tr.ent], self, eta); } } // //================= //fire_hit // //Used for all impact (hit/punch/slash) attacks //================= // qboolean fire_hit (edict_t *self, vec3_t aim, int damage, int kick) { trace_t tr; vec3_t forward, right, up; vec3_t v; vec3_t point; float range; vec3_t dir; //see if enemy is in range VectorSubtract (self->enemy->s.origin, self->s.origin, dir); range = VectorLength(dir); if (range > aim[0]) return qfalse; if (aim[1] > self->r.mins[0] && aim[1] < self->r.maxs[0]) { // the hit is straight on so back the range up to the edge of their bbox range -= self->enemy->r.maxs[0]; } else { // this is a side hit so adjust the "right" value out to the edge of their bbox if (aim[1] < 0) aim[1] = self->enemy->r.mins[0]; else aim[1] = self->enemy->r.maxs[0]; } VectorMA (self->s.origin, range, dir, point); trap_Trace (&tr, self->s.origin, NULL, NULL, point, self, MASK_SHOT); if (tr.fraction < 1) { if (!game.edicts[tr.ent].takedamage) return qfalse; // if it will hit any client/monster then hit the one we wanted to hit if ((game.edicts[tr.ent].r.svflags & SVF_MONSTER) || (game.edicts[tr.ent].r.client)) tr.ent = self->enemy - game.edicts; } AngleVectors(self->s.angles, forward, right, up); VectorMA (self->s.origin, range, forward, point); VectorMA (point, aim[1], right, point); VectorMA (point, aim[2], up, point); VectorSubtract (point, self->enemy->s.origin, dir); // do the damage T_Damage (&game.edicts[tr.ent], self, self, dir, point, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT); if (!(game.edicts[tr.ent].r.svflags & SVF_MONSTER) && (!game.edicts[tr.ent].r.client)) return qfalse; // do our special form of knockback here VectorMA (self->enemy->r.absmin, 0.5, self->enemy->r.size, v); VectorSubtract (v, point, v); VectorNormalize (v); VectorMA (self->enemy->velocity, kick, v, self->enemy->velocity); if (self->enemy->velocity[2] > 0) self->enemy->groundentity = NULL; return qtrue; } // //================= //fire_lead // //This is an internal support routine used for bullet/pellet based weapons. //Must match CG_fireLead in cg_events.c //================= // static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, vec3_t axis[3], int damage, int kick, int hspread, int vspread, int *seed, int mod) { trace_t tr; vec3_t dir; vec3_t end; float r; float u; vec3_t water_start; int content_mask = MASK_SHOT | MASK_WATER; trap_Trace (&tr, self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) { r = Q_crandom (seed) * hspread; u = Q_crandom (seed) * vspread; VectorMA (start, 8192, axis[0], end); VectorMA (end, r, axis[1], end); VectorMA (end, u, axis[2], end); if (trap_PointContents (start) & MASK_WATER) { VectorCopy (start, water_start); content_mask &= ~MASK_WATER; } trap_Trace (&tr, start, NULL, NULL, end, self, content_mask); // see if we hit water if (tr.contents & MASK_WATER) { VectorCopy (tr.endpos, water_start); if (!VectorCompare (start, tr.endpos)) { vec3_t forward, right, up; // change bullet's course when it enters water VectorSubtract (end, start, dir); VecToAngles (dir, dir); AngleVectors (dir, forward, right, up); r = Q_crandom (seed) * hspread * 2; u = Q_crandom (seed) * vspread * 2; VectorMA (water_start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); } // re-trace ignoring water this time trap_Trace (&tr, water_start, NULL, NULL, end, self, MASK_SHOT); } } // send gun puff / flash if (tr.fraction < 1.0) { if (game.edicts[tr.ent].takedamage) { T_Damage (&game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod); } else { if ( !(tr.surfFlags & SURF_NOIMPACT) ) { if (self->r.client) PlayerNoise (self, tr.endpos, PNOISE_IMPACT); } } } } // //================= //fire_bullet // //Fires a single round. Used for machinegun and chaingun. Would be fine for //pistols, rifles, etc.... //================= // void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int mod) { vec3_t dir, axis[3]; edict_t *event; int seed = rand() & 255; VecToAngles ( aimdir, dir ); AngleVectors ( dir, axis[0], axis[1], axis[2] ); // send the event event = G_SpawnEvent ( EV_FIRE_BULLET, seed, start ); event->s.ownerNum = self - game.edicts; event->r.svflags = SVF_FORCEOLDORIGIN; VectorScale ( axis[0], 1024, event->s.origin2 ); // DirToByte is too inaccurate fire_lead ( self, start, aimdir, axis, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, &seed, mod ); } // //================= //fire_shotgun // //Shoots shotgun pellets. Used by shotgun and super shotgun. //================= // void fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int count, int mod) { vec3_t dir, axis[3]; edict_t *event; int i, seed = rand() & 255; VecToAngles ( aimdir, dir ); AngleVectors ( dir, axis[0], axis[1], axis[2] ); // send the event event = G_SpawnEvent ( EV_FIRE_SHOTGUN, seed, start ); event->s.eventCount = count; event->s.ownerNum = self - game.edicts; event->r.svflags = SVF_FORCEOLDORIGIN; VectorScale ( axis[0], 4096, event->s.origin2 ); // DirToByte is too inaccurate for ( i = 0; i < count; i++ ) { fire_lead ( self, start, aimdir, axis, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, &seed, mod ); } } // //================= //fire_blaster // //Fires a single blaster bolt. Used by the blaster and hyper blaster. //================= // void blaster_touch (edict_t *self, edict_t *other, cplane_t *plane, int surfFlags) { if (other == self->r.owner) return; if (surfFlags & SURF_NOIMPACT) { G_FreeEdict (self); return; } if (self->r.owner->r.client) PlayerNoise(self->r.owner, self->s.origin, PNOISE_IMPACT); if (other->takedamage) { T_Damage (other, self, self->r.owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, self->style); } else { // turn entity into event G_TurnEntityIntoEvent ( self, EV_BLASTER, DirToByte (plane ? plane->normal : NULL) ); return; } G_FreeEdict (self); } void fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int type, int mod) { edict_t *bolt; trace_t tr; VectorNormalize (dir); bolt = G_Spawn(); bolt->r.svflags = SVF_PROJECTILE; // when prediction is used against the object // (blaster/hyperblaster shots), the player won't be solid clipped against // the object. Right now trying to run into a firing hyperblaster // is very jerky since you are predicted 'against' the shots. VectorCopy (start, bolt->s.origin); VectorCopy (start, bolt->s.old_origin); VecToAngles (dir, bolt->s.angles); VectorScale (dir, speed, bolt->velocity); bolt->movetype = MOVETYPE_FLYMISSILE; bolt->r.clipmask = MASK_SHOT; bolt->r.solid = SOLID_BBOX; bolt->s.type = type; bolt->s.renderfx |= RF_NOSHADOW; VectorClear (bolt->r.mins); VectorClear (bolt->r.maxs); bolt->s.modelindex = trap_ModelIndex ("models/objects/laser/tris.md2"); bolt->s.sound = trap_SoundIndex ("sounds/misc/lasfly.wav"); bolt->r.owner = self; bolt->touch = blaster_touch; bolt->nextthink = level.time + 2; bolt->think = G_FreeEdict; bolt->dmg = damage; bolt->classname = "bolt"; bolt->style = mod; trap_LinkEntity (bolt); //newgametypes (this was for q2 monsters, who cares about them) trap_Trace (&tr, self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT); if (tr.fraction < 1.0) { VectorMA (bolt->s.origin, -10, dir, bolt->s.origin); bolt->touch (bolt, &game.edicts[tr.ent], &tr.plane, 0); } } */ /* //================= //fire_grenade //================= static void Grenade_Explode (edict_t *ent) { int mod; if (ent->r.owner->r.client) PlayerNoise(ent->r.owner, ent->s.origin, PNOISE_IMPACT); //FIXME: if we are onground then raise our Z just a bit since we are a point? if (ent->enemy) { float points; vec3_t v; vec3_t dir; VectorAdd (ent->enemy->r.mins, ent->enemy->r.maxs, v); VectorMA (ent->enemy->s.origin, 0.5, v, v); VectorSubtract (ent->s.origin, v, v); points = ent->dmg - 0.5 * VectorLength (v); VectorSubtract (ent->enemy->s.origin, ent->s.origin, dir); if (ent->spawnflags & 1) mod = MOD_HANDGRENADE; else mod = MOD_GRENADE; T_Damage (ent->enemy, ent, ent->r.owner, dir, ent->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod); } if (ent->spawnflags & 2) mod = MOD_HELD_GRENADE; else if (ent->spawnflags & 1) mod = MOD_HG_SPLASH; else mod = MOD_G_SPLASH; T_RadiusDamage(ent, ent->r.owner, NULL, ent->dmg, ent->radius_dmg, ent->enemy, ent->dmg_radius, mod); // turn entity into event VectorMA (ent->s.origin, -0.02, ent->velocity, ent->s.origin); G_TurnEntityIntoEvent ( ent, ent->groundentity ? EV_GRENADE_EXPLOSION : EV_ROCKET_EXPLOSION, DirToByte (ent->movedir) ); } static void Grenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags) { if (other == ent->r.owner) return; if (surfFlags & SURF_NOIMPACT) { G_FreeEdict (ent); return; } if (!other->takedamage) { G_AddEvent (ent, EV_GRENADE_BOUNCE, 0, qtrue); return; } ent->enemy = other; Grenade_Explode (ent); } static void LaserGrenade_SetupLaser (edict_t *ent) { edict_t *owner; edict_t *laser; if( !(owner = ent->r.owner) ) { ent->nextthink = level.time + FRAMETIME; ent->think = G_FreeEdict; return; } laser = G_Spawn(); laser->s.sound = trap_SoundIndex ("world/laser.wav"); laser->classname = "grenade_laser"; laser->enemy = NULL; laser->target = NULL; laser->r.owner = ent; laser->activator = owner; laser->dmg = 25; laser->delay = 10; laser->count = MOD_LASER_TRAP; laser->spawnflags = 1; //newgametypes[start] JALFIXME: just hacked it in being always color red. The color should match the team color //if( !ctf->integer || !owner->r.client || owner->r.client->resp.ctf_team == CTF_TEAM1 ) laser->spawnflags |= 2; // red //else //laser->spawnflags |= 8; // blue //newgametypes[end] VectorCopy( ent->s.origin, laser->s.origin ); VectorCopy( ent->s.angles, laser->s.angles ); target_laser_start (laser); } static void LaserGrenade_Use (edict_t *self, edict_t *other, edict_t *activator) { self->enemy = NULL; Grenade_Explode (self); } static void LaserGrenade_Touch (edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags) { if (other == ent->r.owner) return; if (surfFlags & SURF_NOIMPACT) { G_FreeEdict (ent); return; } if (other != world) { if (other->takedamage) ent->enemy = other; Grenade_Explode (ent); return; } ent->use = LaserGrenade_Use; ent->touch = NULL; ent->movetype = MOVETYPE_NONE; ent->s.sound = 0; G_AddEvent (ent, EV_GRENADE_BOUNCE, 0, qtrue); ent->nextthink = level.time + 2; ent->think = LaserGrenade_SetupLaser; VectorClear (ent->velocity); VectorClear (ent->avelocity); VecToAngles (plane->normal, ent->s.angles); VectorCopy (plane->normal, ent->movedir); trap_LinkEntity (ent); } void fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius) { edict_t *grenade; vec3_t dir; vec3_t forward, right, up; VecToAngles (aimdir, dir); AngleVectors (dir, forward, right, up); grenade = G_Spawn(); VectorCopy (start, grenade->s.origin); VectorScale (aimdir, speed, grenade->velocity); grenade->velocity[2] += 200 * up[2] + crandom() * 10.0; VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); VectorSet (grenade->avelocity, 300, 300, 300); VectorSet (grenade->movedir, 0, 0, 1); grenade->movetype = MOVETYPE_BOUNCEGRENADE; grenade->r.clipmask = MASK_SHOT; grenade->r.solid = SOLID_BBOX; grenade->s.type = ET_GRENADE; grenade->s.renderfx |= RF_NOSHADOW; VectorClear (grenade->r.mins); VectorClear (grenade->r.maxs); grenade->s.modelindex = trap_ModelIndex ("models/ammo/grenade1.md3"); grenade->r.owner = self; grenade->touch = Grenade_Touch; grenade->nextthink = level.time + timer; grenade->think = Grenade_Explode; grenade->use = NULL; grenade->dmg = damage; grenade->dmg_radius = damage_radius; grenade->classname = "grenade"; trap_LinkEntity (grenade); } void fire_grenade2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held) { edict_t *grenade; vec3_t dir; vec3_t forward, right, up; VecToAngles (aimdir, dir); AngleVectors (dir, forward, right, up); grenade = G_Spawn(); VectorCopy (start, grenade->s.origin); VectorScale (aimdir, speed, grenade->velocity); VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity); VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity); VectorSet (grenade->avelocity, 300, 300, 300); VectorSet (grenade->movedir, 0, 0, 1); grenade->movetype = MOVETYPE_BOUNCE; grenade->r.clipmask = MASK_SHOT; grenade->r.solid = SOLID_BBOX; grenade->s.renderfx |= RF_NOSHADOW; VectorClear (grenade->r.mins); VectorClear (grenade->r.maxs); grenade->s.modelindex = trap_ModelIndex ("models/objects/grenade2/tris.md2"); grenade->r.owner = self; grenade->touch = LaserGrenade_Touch; grenade->use = NULL; grenade->nextthink = level.time + timer; grenade->think = Grenade_Explode; grenade->dmg = damage; grenade->dmg_radius = damage_radius; grenade->classname = "hgrenade"; if (held) grenade->spawnflags = 3; else grenade->spawnflags = 1; grenade->s.sound = trap_SoundIndex("sounds/weapons/hgrenc1b.wav"); if (timer <= 0.0) Grenade_Explode (grenade); else { //splitmodels (handgrenade drop sound moved to cgame) trap_LinkEntity (grenade); } } // //================= //fire_rocket //================= // void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags) { if (other == ent->r.owner) return; if (surfFlags & SURF_NOIMPACT) { G_FreeEdict (ent); return; } if (ent->r.owner->r.client) PlayerNoise (ent->r.owner, ent->s.origin, PNOISE_IMPACT); if (other->takedamage) T_Damage (other, ent, ent->r.owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET); T_RadiusDamage(ent, ent->r.owner, NULL, ent->dmg, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH); // turn entity into event VectorMA ( ent->s.origin, -0.02, ent->velocity, ent->s.origin ); G_TurnEntityIntoEvent ( ent, EV_ROCKET_EXPLOSION, DirToByte (plane ? plane->normal : NULL) ); } void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) { edict_t *rocket; rocket = G_Spawn(); VectorCopy (start, rocket->s.origin); VectorCopy (dir, rocket->movedir); VecToAngles (dir, rocket->s.angles); VectorScale (dir, speed, rocket->velocity); rocket->movetype = MOVETYPE_FLYMISSILE; rocket->r.clipmask = MASK_SHOT; rocket->r.solid = SOLID_BBOX; rocket->s.type = ET_ROCKET; rocket->s.renderfx |= RF_NOSHADOW; VectorClear (rocket->r.mins); VectorClear (rocket->r.maxs); rocket->s.modelindex = trap_ModelIndex ("models/ammo/rocket/rocket.md3"); rocket->r.owner = self; rocket->touch = rocket_touch; rocket->nextthink = level.time + 8000/speed; rocket->think = G_FreeEdict; rocket->dmg = damage; rocket->radius_dmg = radius_damage; rocket->dmg_radius = damage_radius; rocket->s.sound = trap_SoundIndex ("sounds/weapons/rocket/rockfly.wav"); rocket->classname = "rocket"; //newgametypes (this was for q2 monsters, who cares about them) trap_LinkEntity (rocket); } // //================= //fire_rail //================= // void fire_rail (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick) { vec3_t from; vec3_t end; trace_t tr; edict_t *ignore, *event; int mask; VectorMA (start, 8192, aimdir, end); VectorCopy (start, from); ignore = self; mask = MASK_SHOT; while (ignore) { trap_Trace (&tr, from, NULL, NULL, end, ignore, mask); //ZOID--added so rail goes through SOLID_BBOX entities (gibs, etc) if ((game.edicts[tr.ent].r.svflags & SVF_MONSTER) || (game.edicts[tr.ent].r.client) || (game.edicts[tr.ent].r.solid == SOLID_BBOX)) ignore = &game.edicts[tr.ent]; else ignore = NULL; if ((&game.edicts[tr.ent] != self) && (game.edicts[tr.ent].takedamage)) T_Damage (&game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN); VectorCopy (tr.endpos, from); } // send gun puff / flash event = G_SpawnEvent ( EV_RAILTRAIL, 0, start ); event->r.svflags = SVF_FORCEOLDORIGIN; VectorCopy ( tr.endpos, event->s.origin2 ); event->s.ownerNum = self->s.number;//splitmodels (identify who fired for cgame) if (self->r.client) PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } */ /* // //================= //fire_bfg //================= // void bfg_explode (edict_t *self) { edict_t *ent; float points; vec3_t v; float dist; if (self->s.frame == 0) { // the BFG effect ent = NULL; while ((ent = findradius(ent, self->s.origin, self->dmg_radius)) != NULL) { if (!ent->takedamage) continue; if (ent == self->r.owner) continue; if (!CanDamage (ent, self)) continue; if (!CanDamage (ent, self->r.owner)) continue; VectorAdd (ent->r.mins, ent->r.maxs, v); VectorMA (ent->s.origin, 0.5, v, v); VectorSubtract (self->s.origin, v, v); dist = VectorLength(v); points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius)); if (ent == self->r.owner) points = points * 0.5; G_SpawnEvent ( EV_BFG_EXPLOSION, 0, ent->s.origin ); T_Damage (ent, self, self->r.owner, self->velocity, ent->s.origin, vec3_origin, (int)points, 0, DAMAGE_ENERGY, MOD_BFG_EFFECT); } } self->nextthink = level.time + FRAMETIME; self->count++; if (self->count == 5) self->think = G_FreeEdict; } void bfg_touch (edict_t *self, edict_t *other, cplane_t *plane, int surfFlags) { if (other == self->r.owner) return; if (surfFlags & SURF_NOIMPACT) { G_FreeEdict (self); return; } if (self->r.owner->r.client) PlayerNoise(self->r.owner, self->s.origin, PNOISE_IMPACT); // core explosion - prevents firing it into the wall/floor if (other->takedamage) T_Damage (other, self, self->r.owner, self->velocity, self->s.origin, plane->normal, 200, 0, 0, MOD_BFG_BLAST); T_RadiusDamage(self, self->r.owner, NULL, 200, 0, other, 100, MOD_BFG_BLAST); G_Sound (self, CHAN_VOICE, trap_SoundIndex ("sounds/weapons/bfg__x1b.wav"), 1, ATTN_NORM); self->r.solid = SOLID_NOT; self->touch = NULL; self->r.svflags = SVF_NOCLIENT; VectorMA (self->s.origin, -1 * FRAMETIME, self->velocity, self->s.origin); VectorClear (self->velocity); self->think = bfg_explode; self->nextthink = level.time + FRAMETIME; self->enemy = other; self->count = 0; trap_LinkEntity (self); G_SpawnEvent ( EV_BFG_BIGEXPLOSION, 0, self->s.origin ); } void bfg_think (edict_t *self) { edict_t *ent; edict_t *ignore; edict_t *event; vec3_t point; vec3_t dir; vec3_t start; vec3_t end; int dmg; trace_t tr; dmg = 5; ent = NULL; while ((ent = findradius(ent, self->s.origin, 256)) != NULL) { if (ent == self) continue; if (ent == self->r.owner) continue; if (!ent->takedamage) continue; if (!(ent->r.svflags & SVF_MONSTER) && !(ent->r.client) && (strcmp(ent->classname, "misc_explobox") != 0)) continue; //ZOID //newgametypes //ZOID VectorMA (ent->r.absmin, 0.5, ent->r.size, point); VectorSubtract (point, self->s.origin, dir); VectorNormalize (dir); ignore = self; VectorCopy (self->s.origin, start); VectorMA (start, 2048, dir, end); while(1) { trap_Trace (&tr, start, NULL, NULL, end, ignore, MASK_SHOT); if (!tr.ent) break; // hurt it if we can if ((game.edicts[tr.ent].takedamage) && !(game.edicts[tr.ent].flags & FL_IMMUNE_LASER) && (&game.edicts[tr.ent] != self->r.owner)) T_Damage (&game.edicts[tr.ent], self, self->r.owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER); // if we hit something that's not a monster or player we're done if (!(game.edicts[tr.ent].r.svflags & SVF_MONSTER) && (!game.edicts[tr.ent].r.client)) { edict_t *event; event = G_SpawnEvent ( EV_LASER_SPARKS, DirToByte (tr.plane.normal), tr.endpos ); event->s.eventCount = 4; event->s.skinnum = self->s.skinnum; break; } ignore = &game.edicts[tr.ent]; VectorCopy (tr.endpos, start); } event = G_SpawnEvent ( EV_BFG_LASER, 0, self->s.origin ); event->r.svflags = SVF_FORCEOLDORIGIN; VectorCopy ( tr.endpos, event->s.origin2 ); } self->nextthink = level.time + FRAMETIME; } void fire_bfg (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius) { edict_t *bfg; bfg = G_Spawn(); VectorCopy (start, bfg->s.origin); VectorCopy (dir, bfg->movedir); VectorClear (bfg->s.angles); VectorScale (dir, speed, bfg->velocity); bfg->movetype = MOVETYPE_FLYMISSILE; bfg->r.clipmask = MASK_SHOT; bfg->r.solid = SOLID_BBOX; bfg->s.type = ET_BFG; VectorClear (bfg->r.mins); VectorClear (bfg->r.maxs); bfg->s.modelindex = trap_ModelIndex ("sprites/s_bfg1.sp2"); bfg->r.owner = self; bfg->touch = bfg_touch; bfg->nextthink = level.time + 8000/speed; bfg->think = G_FreeEdict; bfg->radius_dmg = damage; bfg->dmg_radius = damage_radius; bfg->classname = "bfg blast"; bfg->s.sound = trap_SoundIndex ("sounds/weapons/bfg__l1a.wav"); bfg->think = bfg_think; bfg->nextthink = level.time + FRAMETIME; bfg->teammaster = bfg; bfg->teamchain = NULL; //newgametypes (this was for q2 monsters, who cares about them) trap_LinkEntity (bfg); } */ /* // kept for GB static void W_Fire_Lead_OLD( edict_t *self, vec3_t start, vec3_t aimdir, vec3_t axis[3], int damage, int kick, int hspread, int vspread, int *seed, int dflags, int mod ) { trace_t tr; //vec3_t dir; vec3_t end; float r; float u; vec3_t water_start; int content_mask = MASK_SHOT | MASK_WATER; trap_Trace( &tr, self->s.origin, NULL, NULL, start, self, MASK_SHOT ); if( !(tr.fraction < 1.0) ) { r = Q_crandom(seed) * hspread; u = Q_crandom(seed) * vspread; VectorMA( start, 8192, axis[0], end ); VectorMA( end, r, axis[1], end ); VectorMA( end, u, axis[2], end ); if( trap_PointContents (start) & MASK_WATER ) { VectorCopy( start, water_start ); content_mask &= ~MASK_WATER; } trap_Trace( &tr, start, NULL, NULL, end, self, content_mask ); // see if we hit water //if( tr.contents & MASK_WATER ) //{ //VectorCopy( tr.endpos, water_start ); //if( !VectorCompare(start, tr.endpos) ) //{ //vec3_t forward, right, up; // change bullet's course when it enters water //VectorSubtract( end, start, dir ); //VecToAngles( dir, dir ); //AngleVectors (dir, forward, right, up); //r = Q_crandom(seed) * hspread * 2; //u = Q_crandom(seed) * vspread * 2; //VectorMA( water_start, 8192, forward, end ); //VectorMA( end, r, right, end ); //VectorMA( end, u, up, end ); //} // re-trace ignoring water this time //trap_Trace( &tr, water_start, NULL, NULL, end, self, MASK_SHOT ); //} } // send gun puff / flash if( tr.fraction < 1.0 ) { if( game.edicts[tr.ent].takedamage ) { T_Damage( &game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, dflags, mod ); } else { if( !(tr.surfFlags & SURF_NOIMPACT) ) { } } } } */ //================== //W_Fire_Lead //the seed is important to be as pointer for cgame prediction accuracy //================== static void W_Fire_Lead( edict_t *self, vec3_t start, vec3_t aimdir, vec3_t axis[3], int damage, int kick, int hspread, int vspread, int *seed, int dflags, int mod ) { trace_t tr; vec3_t dir; vec3_t end; float r; float u; vec3_t water_start; int content_mask = MASK_SHOT | MASK_WATER; trap_Trace (&tr, self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) { #if 1 // circle double alpha=M_PI*Q_crandom(seed); // [-PI ..+PI] double s=fabs(Q_crandom(seed)); // [0..1] r= s*cos(alpha)*hspread; u= s*sin(alpha)*vspread; #else // square r = Q_crandom (seed) * hspread; u = Q_crandom (seed) * vspread; #endif VectorMA (start, 8192, axis[0], end); VectorMA (end, r, axis[1], end); VectorMA (end, u, axis[2], end); if (trap_PointContents (start) & MASK_WATER) { VectorCopy (start, water_start); content_mask &= ~MASK_WATER; } trap_Trace (&tr, start, NULL, NULL, end, self, content_mask); // see if we hit water if (tr.contents & MASK_WATER) { VectorCopy (tr.endpos, water_start); if (!VectorCompare (start, tr.endpos)) { vec3_t forward, right, up; // change bullet's course when it enters water VectorSubtract (end, start, dir); VecToAngles (dir, dir); AngleVectors (dir, forward, right, up); r = Q_crandom (seed) * hspread * 2; u = Q_crandom (seed) * vspread * 2; VectorMA (water_start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); } // re-trace ignoring water this time trap_Trace (&tr, water_start, NULL, NULL, end, self, MASK_SHOT); } } // send gun puff / flash if (tr.fraction < 1.0) { if (game.edicts[tr.ent].takedamage) { T_Damage (&game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, dflags, mod); } else { if ( !(tr.surfFlags & SURF_NOIMPACT) ) { } } } } //================== //W_Touch_Projectile - Generic projectile touch func. Only for replacement in tests //================== void W_Touch_Projectile( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { vec3_t dir; int radius; //don't hurt onwner for the 1st second? if( other == ent->r.owner ) { if( !g_projectile_touch_owner->integer || (g_projectile_touch_owner->integer && ent->timestamp + 1.0 > level.time) ) return; } if( surfFlags & SURF_NOIMPACT ) { G_FreeEdict( ent ); return; } if( other->takedamage ) { VectorSubtract( other->s.origin, ent->s.origin, dir ); VectorNormalize( dir ); T_Damage( other, ent, ent->r.owner, dir, ent->s.origin, plane->normal, ent->dmg, ent->dmg, 0, MOD_EXPLOSIVE ); } T_RadiusDamage( ent, ent->r.owner, plane, ent->dmg, ent->dmg_knockback, ent->radius_dmg, other, ent->dmg_radius, MOD_EXPLOSIVE ); // turn entity into event radius = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8); VectorMA( ent->s.origin, -0.02, ent->velocity, ent->s.origin ); G_TurnEntityIntoEvent( ent, EV_EXPLOSION1, DirToByte (plane ? plane->normal : NULL) ); ent->s.firemode = FIRE_MODE_STRONG; ent->s.weapon = radius; } //================== //W_Prestep //================== #define PRESTEP_BY_TIME static void W_Prestep( edict_t *ent, edict_t *ignore ) { trace_t tr; vec3_t dest; #ifndef PRESTEP_BY_TIME vec3_t dir; VectorCopy( ent->velocity, dir ); VectorNormalize( dir ); VectorMA( ent->s.origin, g_projectile_prestep->value, dir, dest ); #else VectorMA( ent->s.origin, g_projectile_prestep->value * 0.001, ent->velocity, dest ); #endif trap_Trace( &tr, ent->s.origin, ent->r.mins, ent->r.maxs, dest, ignore, MASK_SHOT ); VectorCopy( tr.endpos, ent->s.origin ); VectorCopy( tr.endpos, ent->s.old_origin ); if( tr.allsolid || tr.startsolid ) { if( ent->touch ) ent->touch( ent, &game.edicts[tr.ent], NULL, 0 ); } else if( tr.fraction != 1.0 ) { if( ent->touch ) ent->touch( ent, &game.edicts[tr.ent], &tr.plane, tr.surfFlags ); } } //================== //W_Fire_Projectile - Spawn a generic projectile without a model, touch func, sound nor mod //================== edict_t *W_Fire_Projectile( edict_t *self, vec3_t start, vec3_t dir, int speed, int damage, int knockback, int radius_damage, int radius, int timeout ) { edict_t *projectile; VectorNormalize( dir ); projectile = G_Spawn(); VectorCopy( start, projectile->s.origin ); VectorCopy( start, projectile->s.old_origin ); VecToAngles( dir, projectile->s.angles ); VectorScale( dir, speed, projectile->velocity ); projectile->movetype = MOVETYPE_FLYMISSILE; // wsw: make missile fly through players in race if(game.gametype==GAMETYPE_RACE) projectile->r.clipmask = MASK_SOLID; else projectile->r.clipmask = MASK_SHOT; projectile->r.solid = SOLID_BBOX; projectile->s.renderfx = RF_NOSHADOW; projectile->r.svflags = SVF_PROJECTILE; VectorClear( projectile->r.mins ); VectorClear( projectile->r.maxs ); //projectile->s.modelindex = trap_ModelIndex ("models/objects/projectile/plasmagun/proj_plasmagun2.md3"); projectile->s.modelindex = 0; projectile->r.owner = self; projectile->touch = W_Touch_Projectile; //generic one. Should be replaced after calling this func projectile->nextthink = level.timemsec + timeout; projectile->think = G_FreeEdict; projectile->dmg = damage; projectile->dmg_knockback = knockback; projectile->radius_dmg = radius_damage; projectile->dmg_radius = radius; projectile->classname = "projectile"; //generic one. Should be replaced after calling this func. projectile->style = 0; projectile->s.sound = 0; projectile->timestamp = level.time; trap_LinkEntity( projectile ); return projectile; } // ------------ the actual weapons -------------- //================== //W_Touch_Generic // Generic projectile touch function, that should be called once before projectile is removed // Returns false if normal function shouldn't continue //================== static qboolean W_Touch_Generic( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { // don't hurt owner for the first second if( other == ent->r.owner ) { if( !g_projectile_touch_owner->integer || (g_projectile_touch_owner->integer && ent->timestamp + 1.0 > level.time) ) return qfalse; } if( other->takedamage && !G_IsTeamDamage(ent->r.owner, other) && other != ent->r.owner && G_ModToAmmo(ent->style) != AMMO_NONE ) { ent->r.owner->r.client->resp.accuracy_hits_direct[G_ModToAmmo(ent->style)-AMMO_CELLS]++; } return qtrue; } //================== //W_Fire_Blade //================== void W_Fire_Blade( edict_t *self, int range, vec3_t start, vec3_t aimdir, int damage, int knockback, int mod) { edict_t *event, *other = NULL; vec3_t end; trace_t trace; VectorMA( start, range, aimdir, end ); trap_Trace( &trace, start, NULL, NULL, end, self, MASK_SHOT ); if( trace.fraction == 1.0 ) //didn't touch anything return; // find out what touched other = &game.edicts[trace.ent]; if( !other->takedamage ) // it was the world { // wall impact VectorMA( trace.endpos, -0.02, aimdir, end ); event = G_SpawnEvent( EV_BLADE_IMPACT, 0, end ); event->s.ownerNum = self - game.edicts; VectorCopy( trace.plane.normal, event->s.origin2); event->r.svflags = SVF_FORCEOLDORIGIN; return; } // it was a player T_Damage( other, self, self, aimdir, other->s.origin, vec3_origin, damage, knockback, 0, mod ); } //================== //W_Touch_GunbladeBlast //================== void W_Touch_GunbladeBlast( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { vec3_t dir; // int power; if( !W_Touch_Generic(ent, other, plane, surfFlags) ) return; //if (surfFlags & SURF_NOIMPACT) //{ // G_FreeEdict (ent); // return; //} if( other->takedamage ) { float pushFrac; pushFrac = G_KnockbackPushFrac( ent->s.origin, other->s.origin, other->r.mins, other->r.maxs, dir, ent->dmg_radius ); T_Damage( other, ent, ent->r.owner, dir, ent->s.origin, plane->normal, ent->dmg, ent->dmg_knockback * pushFrac, 0, ent->style ); } T_RadiusDamage( ent, ent->r.owner, plane, ent->dmg, ent->dmg_knockback, ent->radius_dmg, other, ent->dmg_radius, MOD_GUNBLADE_S ); //fixme : splash mod // add explosion event if( !(surfFlags & SURF_NOIMPACT) && !other->takedamage ) { edict_t *event; //power = (int)(128.0f * ((float)ent->dmg / (float)g_weaponInfos[WEAP_GUNBLADE].firedef->damage) ); //if( power > 128 ) power = 128; event = G_SpawnEvent( EV_GUNBLADEBLAST_IMPACT, DirToByte(plane ? plane->normal : NULL), ent->s.origin ); event->s.weapon = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8); event->s.skinnum = ((ent->dmg_knockback*1/8) > 255) ? 255 : (ent->dmg_knockback*1/8); } // free at next frame ent->think = G_FreeEdict; ent->touch = NULL; ent->nextthink = level.timemsec + game.framemsec; } //================== //W_Fire_GunbladeBlast //================== void W_Fire_GunbladeBlast( edict_t *self, vec3_t start, vec3_t dir, int damage, int knockback, int radius_damage, int radius, int speed, int timeout, int mod ) { edict_t *blast; blast = W_Fire_Projectile( self, start, dir, speed, damage, knockback, radius_damage, radius, timeout ); blast->s.modelindex = trap_ModelIndex( PATH_GUNBLADEBLAST_STRONG_MODEL ); blast->s.type = ET_BLASTER; blast->touch = W_Touch_GunbladeBlast; blast->classname = "Gunblade blast"; blast->s.renderfx |= RF_FULLBRIGHT; blast->style = mod; blast->s.sound = trap_SoundIndex( S_WEAPON_PLASMAGUN_S_FLY ); W_Prestep( blast, self ); } //================== //W_Fire_Gunblade_Bullet //================== void W_Fire_Gunblade_Bullet( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int knockback, int mod) { vec3_t dir, axis[3]; edict_t *event; int seed = rand() & 255; VecToAngles( aimdir, dir ); AngleVectors( dir, axis[0], axis[1], axis[2] ); // send the event event = G_SpawnEvent( EV_FIRE_BULLET, seed, start ); event->s.ownerNum = self - game.edicts; event->r.svflags = SVF_FORCEOLDORIGIN; VectorScale( axis[0], 1024, event->s.origin2 ); // DirToByte is too inaccurate W_Fire_Lead( self, start, aimdir, axis, damage, knockback, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, &seed, DAMAGE_BULLET, mod ); } //================== //W_Touch_Shockwave //================== void W_Touch_Shockwave( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { G_FreeEdict(ent); } //================== //W_Think_Shockwave //================== void W_Think_Shockwave( edict_t *wave ) { edict_t *ent; if( wave->timestamp + wave->timeout < level.time ) { G_FreeEdict(wave); return; } wave->nextthink = level.timemsec + G_FRAMETIME; wave->think = W_Think_Shockwave; ent = NULL; while( (ent = G_FindBoxInRadius(ent, wave->s.origin, wave->dmg_radius)) != NULL ) { //don't hurt owner for the one second if( ent == wave->r.owner ) { if( !g_projectile_touch_owner->integer || (g_projectile_touch_owner->integer && wave->timestamp + 1.0 > level.time) ) continue; } if( !ent->takedamage || !ent->r.client ) continue; ent->r.client->ps.pmove.stats[PM_STAT_SLOW] = G_FRAMETIME; } } //================== //W_Fire_Shockwave //================== void W_Fire_Shockwave( edict_t *self, vec3_t start, vec3_t aimdir, float speed, int radius, int timeout ) { edict_t *wave; wave = W_Fire_Projectile( self, start, aimdir, (int)speed, 0, 0, 0, radius, 0 ); wave->timeout = timeout/1000.0; wave->accel = -750.0; wave->r.clipmask = MASK_SOLID; wave->nextthink = level.timemsec + G_FRAMETIME; wave->think = W_Think_Shockwave; wave->s.modelindex = trap_ModelIndex( PATH_ROCKET_EXPLOSION_MODEL ); wave->touch = W_Touch_Shockwave; wave->classname = "wave"; wave->s.renderfx |= RF_FULLBRIGHT; wave->s.type = ET_SHOCKWAVE; //wave->style = mod; W_Prestep( wave, self ); } void W_Fire_Riotgun2( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int knockback, int hspread, int vspread, int count, int dflags, int mod ) { vec3_t dir, axis[3]; edict_t *event; int i, seed = rand() & 255; VecToAngles( aimdir, dir ); AngleVectors( dir, axis[0], axis[1], axis[2] ); // send the event event = G_SpawnEvent( EV_FIRE_RIOTGUN, seed, start ); event->s.eventCount = count; event->s.ownerNum = self - game.edicts; event->r.svflags = SVF_FORCEOLDORIGIN; VectorScale( axis[0], 4096, event->s.origin2 ); // DirToByte is too inaccurate //send spreads inside these (the free shorts I found) event->s.light = hspread; event->s.skinnum = vspread; for( i = 0; i < count; i++ ) W_Fire_Lead( self, start, aimdir, axis, damage, knockback, hspread, vspread, &seed, dflags, mod ); } void W_Fire_Riotgun( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int knockback, int hspread, int vspread, int count, int dflags, int mod ) { W_Fire_Riotgun2( self, start, aimdir, damage, knockback, hspread, vspread, count, dflags, mod ); } //================== //W_Grenade_Explode //================== static void W_Grenade_Explode( edict_t *ent ) { vec3_t origin; int radius; //FIXME: if we are onground then raise our Z just a bit since we are a point? if( ent->enemy ) { vec3_t dir; float pushFrac; pushFrac = G_KnockbackPushFrac( ent->s.origin, ent->enemy->s.origin, ent->enemy->r.mins, ent->enemy->r.maxs, dir, ent->dmg_radius ); T_Damage( ent->enemy, ent, ent->r.owner, dir, ent->s.origin, vec3_origin, ent->dmg, ent->dmg_knockback * pushFrac, 0, ent->style); } T_RadiusDamage( ent, ent->r.owner, NULL, ent->dmg, ent->dmg_knockback, ent->radius_dmg, ent->enemy, ent->dmg_radius, (ent->style == MOD_GRENADE_S) ? MOD_GRENADE_SPLASH_S : MOD_GRENADE_SPLASH_W ); radius = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8); VectorMA( ent->s.origin, -0.02, ent->velocity, origin ); G_TurnEntityIntoEvent( ent, EV_GRENADE_EXPLOSION, ent->groundentity ? DirToByte (ent->movedir) : 0 ); ent->s.firemode = (ent->style == MOD_GRENADE_S) ? FIRE_MODE_STRONG : FIRE_MODE_WEAK ; ent->s.weapon = radius; } //================== //W_Touch_Grenade //================== static void W_Touch_Grenade( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { if( !W_Touch_Generic(ent, other, plane, surfFlags) ) return; if( surfFlags & SURF_NOIMPACT ) { G_FreeEdict(ent); return; } if( !other->takedamage ) { #if 1 if( ent->style == MOD_GRENADE_S ) G_AddEvent( ent, EV_GRENADE_BOUNCE, FIRE_MODE_STRONG, qtrue ); else G_AddEvent( ent, EV_GRENADE_BOUNCE, FIRE_MODE_WEAK, qtrue ); #else if( plane->normal[2] < 0.7 || abs(ent->velocity[2]) > 50 ) { VectorScale( ent->velocity, 1.2, ent->velocity ); if( ent->style == MOD_GRENADE_S ) G_AddEvent( ent, EV_GRENADE_BOUNCE, FIRE_MODE_STRONG, qtrue ); else G_AddEvent( ent, EV_GRENADE_BOUNCE, FIRE_MODE_WEAK, qtrue ); } #endif return; } ent->enemy = other; W_Grenade_Explode(ent); } //================== //W_Fire_Grenade //================== void W_Fire_Grenade( edict_t *self, vec3_t start, vec3_t dir, int speed, int damage, int knockback, int radius_damage, float radius, int timeout, int mod ) { edict_t *grenade; vec3_t up, angles; cvar_t *g_grenade_gravity = trap_Cvar_Get( "g_grenade_gravity", "1.3", CVAR_ARCHIVE ); //cvar_t *g_grenade_relative = trap_Cvar_Get( "g_grenade_relative", "1.0", CVAR_ARCHIVE ); grenade = W_Fire_Projectile( self, start, dir, speed, damage, knockback, radius_damage, radius, timeout ); VecToAngles( grenade->velocity, angles ); AngleVectors( angles, NULL, NULL, up ); //VectorMA( grenade->velocity, 200, up, grenade->velocity ); //VectorMA( grenade->velocity, g_grenade_relative->value, self->velocity, grenade->velocity ); VectorSet( grenade->avelocity, 300, 300, 300 ); VectorSet( grenade->movedir, 0, 0, 1 ); grenade->style = mod; grenade->s.type = ET_GRENADE; grenade->movetype = MOVETYPE_BOUNCEGRENADE; grenade->s.renderfx |= RF_FULLBRIGHT; grenade->touch = W_Touch_Grenade; grenade->use = NULL; grenade->think = W_Grenade_Explode; grenade->classname = "grenade"; grenade->gravity = g_grenade_gravity->value; if( mod == MOD_GRENADE_S ) grenade->s.modelindex = trap_ModelIndex( PATH_GRENADE_STRONG_MODEL ); else grenade->s.modelindex = trap_ModelIndex( PATH_GRENADE_WEAK_MODEL ); //grenade->s.sound = trap_SoundIndex ( S_WEAPON_GRENADE_FLY ); W_Prestep( grenade, self ); trap_LinkEntity( grenade ); } //================== //W_Touch_Rocket //================== void W_Touch_Rocket( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { int mod_splash, fire_mode; vec3_t dir; float dmg_radius; if( !W_Touch_Generic(ent, other, plane, surfFlags) ) return; if( ent->style == MOD_ROCKET_S ) { fire_mode = FIRE_MODE_STRONG; mod_splash = MOD_ROCKET_SPLASH_S; } else { fire_mode = FIRE_MODE_WEAK; mod_splash = MOD_ROCKET_SPLASH_W; } dmg_radius=ent->dmg_radius; // wsw: pb midair hack if(game.gametype == GAMETYPE_MIDAIR) { dmg_radius*=2.0f; } if( other->takedamage ) { float pushFrac; pushFrac = G_KnockbackPushFrac( ent->s.origin, other->s.origin, other->r.mins, other->r.maxs, dir, dmg_radius ); T_Damage( other, ent, ent->r.owner, dir, ent->s.origin, plane->normal, ent->dmg, ent->dmg_knockback * pushFrac, 0, ent->style ); } T_RadiusDamage( ent, ent->r.owner, plane, ent->dmg, ent->dmg_knockback, ent->radius_dmg, other, dmg_radius, mod_splash ); // spawn the explosion if( !(surfFlags & SURF_NOIMPACT) ) { edict_t *event; vec3_t explosion_origin; VectorMA( ent->s.origin, -0.02, ent->velocity, explosion_origin ); event = G_SpawnEvent( EV_ROCKET_EXPLOSION, DirToByte (plane ? plane->normal : NULL), explosion_origin ); event->s.firemode = fire_mode; event->s.weapon = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8); } // free the rocket at next frame ent->think = G_FreeEdict; ent->touch = NULL; ent->nextthink = level.timemsec + game.framemsec; } //================== //W_Fire_Rocket //================== void W_Fire_Rocket( edict_t *self, vec3_t start, vec3_t dir, int speed, int damage, int knockback, int radius_damage, int radius, int timeout, int mod ) { edict_t *rocket; rocket = W_Fire_Projectile( self, start, dir, speed, damage, knockback, radius_damage, radius, timeout ); rocket->s.type = ET_ROCKET; //rocket trail sfx if ( mod == MOD_ROCKET_S ) rocket->s.modelindex = trap_ModelIndex( PATH_ROCKET_STRONG_MODEL ); else rocket->s.modelindex = trap_ModelIndex( PATH_ROCKET_WEAK_MODEL ); rocket->touch = W_Touch_Rocket; rocket->think = G_FreeEdict; rocket->s.renderfx |= RF_FULLBRIGHT; rocket->classname = "rocket"; rocket->style = mod; if ( mod == MOD_ROCKET_S ) rocket->s.sound = trap_SoundIndex( S_WEAPON_ROCKET_S_FLY ); else rocket->s.sound = trap_SoundIndex( S_WEAPON_ROCKET_W_FLY ); W_Prestep( rocket, self ); } //================== //W_Touch_Plasma //================== void W_Touch_Plasma( edict_t *ent, edict_t *other, cplane_t *plane, int surfFlags ) { int radius; if( !W_Touch_Generic(ent, other, plane, surfFlags) ) return; if( surfFlags & SURF_NOIMPACT ) { G_FreeEdict( ent ); return; } if( ent->style == MOD_PLASMA_S ) { T_RadiusDamage( ent, ent->r.owner, plane, ent->dmg, ent->dmg_knockback, ent->radius_dmg, other, ent->dmg_radius, MOD_PLASMA_SPLASH_S ); if( other->takedamage ) { vec3_t dir; float pushFrac; pushFrac = G_KnockbackPushFrac( ent->s.origin, other->s.origin, other->r.mins, other->r.maxs, dir, ent->dmg_radius ); T_Damage( other, ent, ent->r.owner, dir, ent->s.origin, plane->normal, ent->dmg, ent->dmg_knockback * pushFrac, 0, MOD_PLASMA_S ); } else { radius = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8); G_TurnEntityIntoEvent( ent, EV_PLASMA_EXPLOSION, DirToByte (plane ? plane->normal : NULL) ); ent->s.firemode = FIRE_MODE_STRONG; ent->s.weapon = radius; return; } } else { T_RadiusDamage( ent, ent->r.owner, plane, ent->dmg, ent->dmg_knockback, ent->radius_dmg, other, ent->dmg_radius, MOD_PLASMA_SPLASH_W ); if( other->takedamage ) { vec3_t dir; float pushFrac; pushFrac = G_KnockbackPushFrac( ent->s.origin, other->s.origin, other->r.mins, other->r.maxs, dir, ent->dmg_radius ); T_Damage( other, ent, ent->r.owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, ent->dmg_knockback, 0, MOD_PLASMA_W ); } else { radius = ((ent->dmg_radius*1/8) > 255) ? 255 : (ent->dmg_radius*1/8); G_TurnEntityIntoEvent( ent, EV_PLASMA_EXPLOSION, DirToByte (plane ? plane->normal : NULL) ); ent->s.firemode = FIRE_MODE_WEAK; ent->s.weapon = radius; return; } } G_FreeEdict( ent ); } //================== //W_Fire_Plasma //================== void W_Fire_Plasma( edict_t *self, vec3_t start, vec3_t dir, int damage, int knockback, int radius_damage, int radius, int speed, int timeout, int mod ) { edict_t *plasma; plasma = W_Fire_Projectile( self, start, dir, speed, damage, knockback, radius_damage, radius, timeout ); plasma->s.type = ET_PLASMA; plasma->touch = W_Touch_Plasma; plasma->classname = "plasma"; plasma->s.renderfx |= RF_FULLBRIGHT; plasma->style = mod; if( mod == MOD_PLASMA_S ) { plasma->s.modelindex = trap_ModelIndex( PATH_PLASMA_STRONG_MODEL ); plasma->s.sound = trap_SoundIndex( S_WEAPON_PLASMAGUN_S_FLY ); } else { plasma->s.modelindex = trap_ModelIndex( PATH_PLASMA_WEAK_MODEL ); plasma->s.sound = trap_SoundIndex( S_WEAPON_PLASMAGUN_W_FLY ); } W_Prestep( plasma, self ); } //================== //W_Touch_Bolt //================== void W_Touch_Bolt( edict_t *self, edict_t *other, cplane_t *plane, int surfFlags ) { if( !W_Touch_Generic(self, other, plane, surfFlags) ) return; if( other->takedamage ) { T_Damage( other, self, self->r.owner, self->velocity, self->s.origin, plane->normal, self->dmg, self->dmg_knockback, 0, MOD_ELECTROBOLT_W ); } else if( !(surfFlags & SURF_NOIMPACT) ) // add explosion event { edict_t *event; event = G_SpawnEvent( EV_BOLT_EXPLOSION, DirToByte(plane ? plane->normal : NULL), self->s.origin ); event->s.firemode = FIRE_MODE_WEAK; } // free at next frame self->think = G_FreeEdict; self->touch = NULL; self->nextthink = level.timemsec + game.framemsec; } //================== //W_Fire_Electrobolt_Strong //================== void W_Fire_Electrobolt_Strong( edict_t *self, vec3_t start, vec3_t aimdir, float speed, int damage, int knockback, int range, int dflags, int mod ) { vec3_t from; vec3_t end; trace_t tr; edict_t *ignore, *event; int mask; VectorMA( start, range, aimdir, end ); VectorCopy( start, from ); ignore = self; mask = MASK_SHOT; while( ignore ) { trap_Trace( &tr, from, NULL, NULL, end, ignore, mask ); // allow trail to go through SOLID_BBOX entities (players, gibs, etc) if( (game.edicts[tr.ent].r.svflags & SVF_MONSTER) || (game.edicts[tr.ent].r.client) || (game.edicts[tr.ent].r.solid == SOLID_BBOX) ) ignore = &game.edicts[tr.ent]; else ignore = NULL; if( (&game.edicts[tr.ent] != self) && (game.edicts[tr.ent].takedamage) ) { T_Damage( &game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, knockback, dflags, mod ); // spawn a impact event on each damaged ent event = G_SpawnEvent( EV_BOLT_EXPLOSION, DirToByte(tr.plane.normal), self->s.origin ); event->s.firemode = FIRE_MODE_STRONG; } VectorCopy( tr.endpos, from ); } // send the weapon fire effect event = G_SpawnEvent( EV_ELECTROTRAIL, 0, start ); event->r.svflags = SVF_FORCEOLDORIGIN; VectorCopy( from, event->s.origin2 ); event->s.ownerNum = ENTNUM(self); // identify who fired for cgame } //================== //W_Fire_Electrobolt_Strong //================== void Old_W_Fire_Electrobolt_Strong( edict_t *self, vec3_t start, vec3_t aimdir, float speed, int damage, int knockback, int range, int dflags, int mod ) { vec3_t end; edict_t *event; trace_t tr; // hitscan VectorMA( start, range, aimdir, end ); trap_Trace( &tr, start, NULL, NULL, end, self, MASK_SHOT ); // send gun puff / flash if( tr.fraction < 1.0 ) { if( game.edicts[tr.ent].takedamage ) { T_Damage( &game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, knockback, dflags, mod ); } else { if( !(tr.surfFlags & SURF_NOIMPACT) ) { } } } // send the weapon fire effect event = G_SpawnEvent( EV_ELECTROTRAIL, 0, start ); event->r.svflags = SVF_FORCEOLDORIGIN; VectorCopy( tr.endpos, event->s.origin2 ); event->s.ownerNum = ENTNUM(self);//identify who fired for cgame } //================== //W_Fire_Electrobolt_Weak //================== void W_Fire_Electrobolt_Weak( edict_t *self, vec3_t start, vec3_t aimdir, float speed, int damage, int knockback, int timeout, int dflags, int mod ) { edict_t *bolt; // projectile, weak mode bolt = W_Fire_Projectile( self, start, aimdir, speed, damage, knockback, 0, 0, timeout ); bolt->s.modelindex = trap_ModelIndex( PATH_ELECTROBOLT_WEAK_MODEL ); bolt->s.type = ET_ELECTRO_WEAK; //add particle trail and light bolt->touch = W_Touch_Bolt; bolt->classname = "bolt"; bolt->s.renderfx |= RF_FULLBRIGHT; bolt->style = mod; W_Prestep( bolt, self ); } //================== //W_Fire_Lasergun //================== void W_Fire_Lasergun( edict_t *self, vec3_t start, vec3_t aimdir, int damage, int knockback, int range, int dflags, int mod ) { int i, playernum; edict_t *e, *laser; trace_t tr; vec3_t end; // first of all, see if we already have a beam entity for this laser laser = NULL; playernum = ENTNUM(self); for( i = game.maxclients; i < game.maxentities; i++ ) { e = &game.edicts[i]; if( !e->r.inuse ) continue; if( e->s.ownerNum == playernum && e->s.type == ET_LASERBEAM ) { laser = e; break; } } // if no ent was found we have to create one if( laser == NULL ) { // we send the muzzleflash event only when new laserbeam is created G_AddEvent( self, EV_MUZZLEFLASH, FIRE_MODE_STRONG, qtrue ); laser = G_Spawn(); laser->s.type = ET_LASERBEAM; laser->s.ownerNum = playernum; laser->movetype = MOVETYPE_NONE; laser->r.solid = SOLID_NOT; laser->r.svflags = SVF_FORCEOLDORIGIN; laser->s.modelindex = trap_ModelIndex( PATH_LASERBEAM_STRONG_MODEL ); laser->s.sound = trap_SoundIndex( S_WEAPON_LASERGUN_S_HUM ); } VectorMA( start, range, aimdir, end ); trap_Trace( &tr, start, NULL, NULL, end, self, MASK_SHOT ); // send gun puff / flash if( tr.fraction < 1.0 ) { if( game.edicts[tr.ent].takedamage ) { T_Damage( &game.edicts[tr.ent], self, self, aimdir, tr.endpos, tr.plane.normal, damage, knockback, dflags, mod ); } else { if( !(tr.surfFlags & SURF_NOIMPACT) ) { } } } // give it 2 server frames before freeing itself, so we can relink it if we are still firing laser->think = G_FreeEdict; laser->nextthink = level.timemsec + ( game.framemsec * 3 ); trap_LinkEntity( laser ); //copy points (warning: origin2 must be copied after linking the entity) VectorCopy( tr.endpos, laser->s.origin ); VectorCopy( start, laser->s.origin2 ); // used in the case the owner isn't in the PVS laser->s.skinnum = range; } void AITools_DrawLine(vec3_t origin, vec3_t dest); void W_Fire_Lasergun_Weak( edict_t *self, vec3_t start, vec3_t end, vec3_t aimdir, int damage, int knockback, int range, int dflags, int mod ) { int i, playernum; edict_t *e, *laser; vec3_t from; // first of all, see if we already have a beam entity for this laser laser = NULL; playernum = ENTNUM(self); for( i = game.maxclients; i < game.maxentities; i++ ) { e = &game.edicts[i]; if( !e->r.inuse ) continue; if( e->s.ownerNum == playernum && e->s.type == ET_CURVELASERBEAM ) { laser = e; break; } } // if no ent was found we have to create one if( laser == NULL ) { // we send the muzzleflash event only when new laserbeam is created G_AddEvent( self, EV_MUZZLEFLASH, FIRE_MODE_WEAK, qtrue ); laser = G_Spawn(); laser->s.type = ET_CURVELASERBEAM; laser->s.ownerNum = playernum; laser->movetype = MOVETYPE_NONE; laser->r.solid = SOLID_NOT; laser->r.svflags = SVF_FORCEOLDORIGIN; laser->s.modelindex = trap_ModelIndex( PATH_LASERBEAM_STRONG_MODEL ); laser->s.sound = trap_SoundIndex( S_WEAPON_LASERGUN_W_HUM ); G_AddEvent( laser, EV_TELEPORT, 0, qtrue ); } // trace the beam curve { int j; float subdivisions = 5, frac; vec3_t dir, impactangles, tmpangles; vec3_t segmentStart, segmentEnd; edict_t *segmentIgnore; VectorSubtract( end, start, dir ); VecToAngles( dir, impactangles ); segmentIgnore = self; VectorCopy( start, segmentStart ); for( i = 1; i <= (int)subdivisions; i++ ) { frac = ( ((float)range/subdivisions)*(float)i ) / (float)range; for( j = 0; j < 3; j++ )tmpangles[j] = LerpAngle( self->s.angles[j], impactangles[j], frac ); AngleVectors( tmpangles, dir, NULL, NULL ); VectorMA( start, range*frac, dir, segmentEnd ); //segment is ready here { trace_t trace; edict_t *ignore; int mask; VectorCopy( segmentStart, from ); ignore = segmentIgnore; mask = MASK_SHOT; while( ignore ) { trap_Trace( &trace, from, NULL, NULL, segmentEnd, ignore, mask ); // allow trail to go through SOLID_BBOX entities (players, gibs, etc) if( (game.edicts[trace.ent].r.svflags & SVF_MONSTER) || (game.edicts[trace.ent].r.client) || (game.edicts[trace.ent].r.solid == SOLID_BBOX) ) { ignore = &game.edicts[trace.ent]; segmentIgnore = ignore; } else ignore = NULL; if( (&game.edicts[trace.ent] != self) && (game.edicts[trace.ent].takedamage) ) { T_Damage( &game.edicts[trace.ent], self, self, dir, trace.endpos, trace.plane.normal, damage, knockback, dflags, mod ); // spawn a impact event on each damaged ent //event = G_SpawnEvent( EV_BOLT_EXPLOSION, DirToByte(trace.plane.normal), self->s.origin ); //event->s.firemode = FIRE_MODE_STRONG; } VectorCopy( trace.endpos, from ); // if found the world, stop if( ignore == NULL && trace.fraction < 1.0f ) { goto beamfinished; } } } //AITools_DrawLine( segmentStart, from ); // copy start point for next segment VectorCopy ( segmentEnd, segmentStart ); } } beamfinished: // give it 2 server frames before freeing itself, so we can relink it if we are still firing laser->think = G_FreeEdict; laser->nextthink = level.timemsec + ( game.framemsec * 3 ); trap_LinkEntity( laser ); //copy points (warning: origin2 must be copied after linking the entity) VectorCopy( end, laser->s.origin ); VectorCopy( start, laser->s.origin2 ); // used in the case the owner isn't in the PVS VectorCopy( self->s.angles, laser->s.angles ); // (need player angles for curved) used in the case the owner isn't in the PVS laser->s.skinnum = range; //laser->r.svflags |= SVF_BROADCAST; // ignore PVS VectorCopy( from, laser->s.origin ); VectorCopy( start, laser->s.origin2 ); // used in the case the owner isn't in the PVS VectorCopy( self->s.angles, laser->s.angles ); // (need player angles for curved) used in the case the owner isn't in the PVS VectorSubtract( from, start, from ); laser->s.skinnum = (int)VectorLengthFast( from ); } //==============================================================================