thirtyflightsofloving/missionpack/z_weapon.c
Knightmare66 0d4e872ce9 Added LMCTF / LM Escape plasma rifle to missionpack DLL.
Added plasma guards (monster_soldier_plasma_re and monster_soldier_plasma_sp) from LM Escape to missionpack DLL.
Added Zaero items/weapons to missionpack DLL.
Added support for Zaero doors  to missionpack DLL.
Fixed crash caused by killtargeting sentien (laser edict not freed) in missionpack DLL.
Fixed bug with broken Rogue turrets in missionpack DLL.
Fixed crash in g_combat.c->M_ReactToDamage() caused by attacker with NULL classname in missionpack DLL.
2020-08-09 02:45:19 -04:00

1647 lines
No EOL
41 KiB
C

#include "g_local.h"
#include "m_player.h"
extern qboolean is_quad;
extern qboolean is_double;
extern qboolean is_quadfire;
extern byte is_silenced;
//void playQuadSound(edict_t *ent);
void Weapon_Generic (edict_t *ent,
int FRAME_ACTIVATE_LAST,
int FRAME_FIRE_LAST,
int FRAME_IDLE_LAST,
int FRAME_DEACTIVATE_LAST,
int *pause_frames,
int *fire_frames,
void (*fire)(edict_t *ent));
void NoAmmoWeaponChange (edict_t *ent);
void check_dodge (edict_t *self, vec3_t start, vec3_t dir, int speed);
void Grenade_Explode(edict_t *ent);
void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result);
#ifdef USE_ZAERO_WEAPONS
void fire_sconnan (edict_t *self);
void fire_sconnanEffects (edict_t *self);
#endif // USE_ZAERO_WEAPONS
const int SC_MAXFIRETIME = 5; // in seconds...
const int SC_BASEDAMAGE = 10; // minimum damage
const int SC_DAMGERANGE = 990; // maximum damaged range (max damage possible is SC_BASEDAMAGE + SC_DAMGERANGE)
const int SC_MAXRADIUS = 500; // maximum blast radius
const int SC_MAXCELLS = 100; // maximum number of cells
#if 0
vec_t VectorLengthSquared (vec3_t v)
{
int i;
float length;
length = 0;
for (i=0 ; i< 3 ; i++)
length += v[i]*v[i];
return length;
}
#endif
void angleToward (edict_t *self, vec3_t point, float speed)
{
vec3_t forward;
float yaw = 0.0;
float vel = 0.0;
vec3_t delta;
vec3_t destAngles;
VectorSubtract(point, self->s.origin, delta);
vectoangles(delta, destAngles);
self->ideal_yaw = destAngles[YAW];
self->yaw_speed = speed;
M_ChangeYaw(self);
yaw = self->s.angles[YAW];
self->ideal_yaw = destAngles[PITCH];
self->s.angles[YAW] = self->s.angles[PITCH];
M_ChangeYaw(self);
self->s.angles[PITCH] = self->s.angles[YAW];
self->s.angles[YAW] = yaw;
AngleVectors (self->s.angles, forward, NULL, NULL);
vel = VectorLength(self->velocity);
VectorScale(forward, vel, self->velocity);
}
#define MAXROTATION 20
/*
Laser Trip Bombs
*/
#define TBOMB_KILLABLE
// spawnflags
#define CHECK_BACK_WALL 1
// variables
#define TBOMB_DELAY 1.0
#define TBOMB_TIMEOUT 180
#define TBOMB_DAMAGE 150
#define TBOMB_RADIUS_DAMAGE 384
#define TBOMB_HEALTH 100
#define TBOMB_SHRAPNEL 5
#define TBOMB_SHRAPNEL_DMG 15
#define TBOMB_MAX_EXIST 25
//#ifdef USE_ZAERO_ITEMS_WEAPONS
void shrapnel_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
{
// do damage if we can
if (!other->takedamage)
return;
if (VectorCompare(ent->velocity, vec3_origin))
return;
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin,
plane->normal, sk_tbomb_shrapnel_damage->value, 8, 0, MOD_TRIPBOMB);
G_FreeEdict(ent);
}
void TripBomb_Explode (edict_t *ent)
{
vec3_t origin;
int i = 0, type;
T_RadiusDamage(ent, ent->owner ? ent->owner : ent, ent->dmg, ent->enemy, ent->dmg_radius, MOD_TRIPBOMB);
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
if (ent->waterlevel)
{
if (ent->groundentity)
// gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
type = TE_GRENADE_EXPLOSION_WATER;
else
// gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
type = TE_ROCKET_EXPLOSION_WATER;
}
else
{
if (ent->groundentity)
// gi.WriteByte (TE_GRENADE_EXPLOSION);
type = TE_GRENADE_EXPLOSION;
else
// gi.WriteByte (TE_ROCKET_EXPLOSION);
type = TE_ROCKET_EXPLOSION;
}
gi.WriteByte (svc_temp_entity);
gi.WriteByte (type);
gi.WritePosition (origin);
gi.multicast (ent->s.origin, MULTICAST_PHS);
// Lazarus reflections
if (level.num_reflectors)
ReflectExplosion (type, origin);
// throw off some debris
for (i = 0; i < (int)sk_tbomb_shrapnel->value; i++)
{
edict_t *sh = G_Spawn();
vec3_t forward, right, up;
sh->classname = "shrapnel";
sh->movetype = MOVETYPE_BOUNCE;
sh->solid = SOLID_BBOX;
sh->s.effects |= EF_GRENADE;
sh->s.modelindex = gi.modelindex("models/objects/shrapnel/tris.md2");
sh->owner = ent->owner;
VectorSet (sh->avelocity, 300, 300, 300);
VectorCopy(ent->s.origin, sh->s.origin);
AngleVectors (ent->s.angles, forward, right, up);
VectorScale(forward, 500, forward);
VectorMA(forward, crandom()*500, right, forward);
VectorMA(forward, crandom()*500, up, forward);
VectorCopy(forward, sh->velocity);
sh->touch = shrapnel_touch;
#ifdef KMQUAKE2_ENGINE_MOD
sh->think = gib_fade; // Knightmare- have the debris fade away
#else
sh->think = G_FreeEdict;
#endif // KMQUAKE2_ENGINE_MOD
sh->nextthink = level.time + 3.0 + crandom() * 1.5;
}
G_FreeEdict (ent);
}
// Knightmare added
#ifdef TBOMB_KILLABLE
void tripbomb_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
{
// gi.dprintf("tripbomb_die\n");
// if set off by another TripBomb, delay a little (chained explosions)
if ( strcmp(inflictor->classname, "ired") )
{
self->takedamage = DAMAGE_NO;
if (self->chain) {
G_FreeEdict (self->chain);
self->chain = NULL;
}
TripBomb_Explode (self);
}
else
{
self->takedamage = DAMAGE_NO;
if (self->chain) {
G_FreeEdict (self->chain);
self->chain = NULL;
}
self->think = TripBomb_Explode;
self->nextthink = level.time + FRAMETIME;
}
}
#endif // TBOMB_KILLABLE
// end Knightmare
void tripbomb_laser_think (edict_t *self)
{
vec3_t start;
vec3_t end;
vec3_t delta;
trace_t tr;
int count = 8;
self->nextthink = level.time + FRAMETIME;
if (level.time > self->timeout)
{
// play a sound
//gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_trig.wav"), 1, ATTN_NORM, 0);
// blow up
self->chain->think = TripBomb_Explode;
self->chain->nextthink = level.time + FRAMETIME;
G_FreeEdict(self);
return;
}
// randomly phase out or EMPNuke is in effect
if (EMPNukeCheck(self, self->s.origin) || random() < 0.1)
{
self->svflags |= SVF_NOCLIENT;
return;
}
self->svflags &= ~SVF_NOCLIENT;
VectorCopy (self->s.origin, start);
VectorMA (start, 2048, self->movedir, end);
tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT);
if (!tr.ent)
return;
VectorSubtract(tr.endpos, self->move_origin, delta);
if (VectorCompare(self->s.origin, self->move_origin))
{
// we haven't done anything yet
VectorCopy(tr.endpos, self->move_origin);
if (self->spawnflags & 0x80000000)
{
self->spawnflags &= ~0x80000000;
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_LASER_SPARKS);
gi.WriteByte (count);
gi.WritePosition (tr.endpos);
gi.WriteDir (tr.plane.normal);
gi.WriteByte (self->s.skinnum);
gi.multicast (tr.endpos, MULTICAST_PVS);
}
}
else if (VectorLength(delta) > 1.0)
{
// play a sound
//gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_trig.wav"), 1, ATTN_NORM, 0);
// blow up
self->chain->think = TripBomb_Explode;
self->chain->nextthink = level.time + FRAMETIME;
G_FreeEdict(self);
return;
}
VectorCopy(self->move_origin, self->s.old_origin);
}
void tripbomb_laser_on (edict_t *self)
{
self->svflags &= ~SVF_NOCLIENT;
self->think = tripbomb_laser_think;
// play a sound
gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_arm.wav"), 1, ATTN_NORM, 0);
tripbomb_laser_think(self);
//gi.positioned_sound(self->s.old_origin, self, CHAN_AUTO, gi.soundindex("weapons/ired/las_tink.wav"), 1, ATTN_NORM, 0);
}
void create_tripbomb_laser (edict_t *bomb)
{
// create the laser
edict_t *laser = G_Spawn();
bomb->chain = laser;
laser->classname = "laser trip bomb laser";
VectorCopy(bomb->s.origin, laser->s.origin);
VectorCopy(bomb->s.origin, laser->move_origin);
VectorCopy(bomb->s.angles, laser->s.angles);
G_SetMovedir (laser->s.angles, laser->movedir);
laser->owner = bomb;
laser->s.skinnum = 0xb0b1b2b3; // <- faint purple 0xf3f3f1f1 <-blue red-> 0xf2f2f0f0;
laser->s.frame = 2;
laser->movetype = MOVETYPE_NONE;
laser->solid = SOLID_NOT;
laser->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
laser->s.modelindex = 1;
laser->chain = bomb;
laser->spawnflags |= 0x80000001;
laser->think = tripbomb_laser_on;
laser->nextthink = level.time + FRAMETIME;
laser->svflags |= SVF_NOCLIENT;
laser->timeout = level.time + sk_tbomb_life->value; // was TBOMB_TIMEOUT;
gi.linkentity (laser);
}
void use_tripbomb (edict_t *self, edict_t *other, edict_t *activator)
{
if (self->chain)
{
// we already have a laser, remove it
G_FreeEdict (self->chain);
self->chain = NULL;
}
else
// create the laser
create_tripbomb_laser (self);
}
void turnOffGlow (edict_t *self)
{
self->s.effects &= ~EF_COLOR_SHELL;
self->s.renderfx &= ~RF_SHELL_GREEN;
self->think = NULL;
self->nextthink = 0;
}
// Knightmare- removed this for shootable tripbombs
#ifdef TBOMB_KILLABLE
void tripbomb_pain (edict_t *self, edict_t *other, float kick, int damage)
{
}
#else
void tripbomb_pain (edict_t *self, edict_t *other, float kick, int damage)
{
// play the green glow sound
//gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_glow.wav"), 1, ATTN_NORM, 0);
// turn on the glow
self->damage_debounce_time = level.time + 0.2;
// if we don't have a think function, then turn this thing on
if (self->think == NULL)
{
self->s.effects |= EF_COLOR_SHELL;
self->s.renderfx |= RF_SHELL_GREEN;
self->nextthink = self->damage_debounce_time;
self->think = turnOffGlow;
}
}
#endif // TBOMB_KILLABLE
void tripbomb_think (edict_t *self)
{
if (self->chain == NULL)
{
// check whether we need to create the laser
if (self->timeout < level.time)
{
create_tripbomb_laser(self);
}
}
// do we need to show damage?
if (self->damage_debounce_time > level.time)
{
self->s.effects |= EF_COLOR_SHELL;
self->s.renderfx |= RF_SHELL_GREEN;
}
else
{
self->s.effects &= ~EF_COLOR_SHELL;
self->s.renderfx &= ~RF_SHELL_GREEN;
}
self->nextthink = level.time + FRAMETIME;
}
void setupBomb (edict_t *bomb, char *classname, float damage, float damage_radius)
{
bomb->classname = classname;
VectorSet(bomb->mins, -8, -8, -8);
VectorSet(bomb->maxs, 8, 8, 8);
bomb->solid = SOLID_BBOX;
bomb->movetype = MOVETYPE_NONE;
bomb->s.modelindex = gi.modelindex("models/objects/ired/tris.md2");
bomb->radius_dmg = damage;
bomb->dmg = damage;
bomb->dmg_radius = damage_radius;
// Knightmare- made the tripbomb destroyable
#ifdef TBOMB_KILLABLE
bomb->health = (int)sk_tbomb_health->value; // was 1
bomb->takedamage = DAMAGE_AIM;
bomb->pain = NULL;
bomb->die = tripbomb_die;
#else
bomb->health = 1;
bomb->takedamage = DAMAGE_IMMORTAL; // health will not be deducted
bomb->pain = tripbomb_pain;
#endif // TBOMB_KILLABLE
}
void removeOldest (void)
{
edict_t *oldestEnt = NULL;
edict_t *e = NULL;
int count = 0;
while (1)
{
e = G_Find(e, FOFS(classname), "ired");
if (e == NULL) // no more
break;
count++;
if (oldestEnt == NULL ||
e->timestamp < oldestEnt->timestamp)
{
oldestEnt = e;
}
}
// do we have too many?
if (count > TBOMB_MAX_EXIST && oldestEnt != NULL)
{
// get this tbomb to explode
oldestEnt->think = TripBomb_Explode;
oldestEnt->nextthink = level.time + FRAMETIME;
G_FreeEdict(oldestEnt->chain);
}
}
qboolean fire_lasertripbomb (edict_t *self, vec3_t start, vec3_t dir, float timer, float damage, float damage_radius, qboolean quad)
{
// trace a line
trace_t tr;
vec3_t endPos;
vec3_t _dir;
edict_t *bomb = NULL;
edict_t *laser = NULL;
VectorScale(dir, 64, _dir);
VectorAdd(start, _dir, endPos);
// trace ahead, looking for a wall
tr = gi.trace(start, NULL, NULL, endPos, self, MASK_SHOT);
if (tr.fraction == 1.0)
{
// not close enough
// gi.cprintf(self, PRINT_HIGH, "Not close enough to a wall");
return false;
}
if (Q_stricmp(tr.ent->classname, "worldspawn") != 0)
{
// gi.cprintf(self, PRINT_HIGH, "Hit something other than a wall");
return false;
}
// create the bomb
bomb = G_Spawn();
// VectorCopy(tr.endpos, bomb->s.origin);
VectorMA(tr.endpos, 3, tr.plane.normal, bomb->s.origin);
vectoangles(tr.plane.normal, bomb->s.angles);
bomb->owner = self;
setupBomb (bomb, "ired", damage, damage_radius);
gi.linkentity(bomb);
bomb->timestamp = level.time;
bomb->timeout = level.time + timer;
bomb->nextthink = level.time + FRAMETIME;
bomb->think = tripbomb_think;
// remove the oldest trip bomb
removeOldest();
// play a sound
gi.sound(self, CHAN_VOICE, gi.soundindex("weapons/ired/las_set.wav"), 1, ATTN_NORM, 0);
return true;
}
void weapon_lasertripbomb_fire (edict_t *ent)
{
if (ent->client->ps.gunframe == 10)
{
vec3_t offset;
vec3_t forward;
vec3_t start;
int damage = sk_tbomb_damage->value;
float radius = sk_tbomb_radius->value;
if (is_quad)
damage *= 4;
if (is_double)
damage *= 2;
// place the trip bomb
VectorSet(offset, 0, 0, ent->viewheight * 0.75);
AngleVectors (ent->client->v_angle, forward, NULL, NULL);
VectorAdd(ent->s.origin, offset, start);
if (fire_lasertripbomb(ent, start, forward, TBOMB_DELAY, damage, radius, is_quad))
{
ent->client->pers.inventory[ent->client->ammo_index] -= 1;
// switch models
ent->client->ps.gunindex = gi.modelindex("models/weapons/v_ired/hand.md2");
// play quad sound
// playQuadSound (ent);
}
}
else if (ent->client->ps.gunframe == 15)
{
// switch models back
int mi = gi.modelindex("models/weapons/v_ired/tris.md2");
if (ent->client->ps.gunindex != mi)
{
ent->client->ps.gunindex = mi;
// go back to get another trip bomb
ent->client->ps.gunframe = 0;
return;
}
}
else if (ent->client->ps.gunframe == 6)
{
ent->client->ps.gunframe = 16;
return;
}
ent->client->ps.gunframe++;
}
void Weapon_LaserTripBomb (edict_t *ent)
{
static int pause_frames[] = {24, 33, 43, 0};
static int fire_frames[] = {6, 10, 15, 0};
const int deactivateFirst = 44;
const int deactivateLast = 48;
const int idleFirst = 16;
const int idleLast = 43;
const int fireFirst = 7;
const int fireLast = 15;
const int activateFirst = 0;
const int activateLast = 6;
if (ent->client->weaponstate == WEAPON_DROPPING)
{
if (ent->client->ps.gunframe == deactivateLast)
{
ChangeWeapon (ent);
return;
}
ent->client->ps.gunframe++;
return;
}
if (ent->client->weaponstate == WEAPON_ACTIVATING)
{
if (ent->client->ps.gunframe == activateLast)
{
ent->client->weaponstate = WEAPON_READY;
ent->client->ps.gunframe = idleFirst;
return;
}
ent->client->ps.gunframe++;
return;
}
if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING))
{
ent->client->weaponstate = WEAPON_DROPPING;
ent->client->ps.gunframe = deactivateFirst;
return;
}
if (ent->client->weaponstate == WEAPON_READY)
{
if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) )
{
ent->client->latched_buttons &= ~BUTTON_ATTACK;
if(ent->client->pers.inventory[ent->client->ammo_index])
{
ent->client->ps.gunframe = fireFirst;
ent->client->weaponstate = WEAPON_FIRING;
// start the animation
ent->client->anim_priority = ANIM_ATTACK;
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
{
ent->s.frame = FRAME_crattak1-1;
ent->client->anim_end = FRAME_crattak9;
}
else
{
ent->s.frame = FRAME_attack1-1;
ent->client->anim_end = FRAME_attack8;
}
}
else
{
if (level.time >= ent->pain_debounce_time)
{
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
ent->pain_debounce_time = level.time + 1;
}
NoAmmoWeaponChange (ent);
}
}
else
{
if (ent->client->ps.gunframe == idleLast)
{
ent->client->ps.gunframe = idleFirst;
return;
}
if (pause_frames)
{
int n = 0;
for (n = 0; pause_frames[n]; n++)
{
if (ent->client->ps.gunframe == pause_frames[n])
{
if (rand()&15)
return;
}
}
}
ent->client->ps.gunframe++;
return;
}
}
if (ent->client->weaponstate == WEAPON_FIRING)
{
int n = 0;
for (n = 0; fire_frames[n]; n++)
{
if (ent->client->ps.gunframe == fire_frames[n])
{
weapon_lasertripbomb_fire(ent);
break;
}
}
if (!fire_frames[n])
ent->client->ps.gunframe++;
if (ent->client->ps.gunframe == idleFirst+1)
ent->client->weaponstate = WEAPON_READY;
}
}
void SP_misc_lasertripbomb (edict_t *bomb)
{
// precache
gi.soundindex("weapons/ired/las_set.wav");
gi.soundindex("weapons/ired/las_arm.wav");
// gi.soundindex("weapons/ired/las_tink.wav");
// gi.soundindex("weapons/ired/las_trig.wav");
// gi.soundindex("weapons/ired/las_glow.wav");
gi.modelindex("models/objects/shrapnel/tris.md2");
gi.modelindex("models/objects/ired/tris.md2");
if (bomb->spawnflags & CHECK_BACK_WALL)
{
vec3_t forward, endPos;
trace_t tr;
// look backwards toward a wall
AngleVectors (bomb->s.angles, forward, NULL, NULL);
VectorMA(bomb->s.origin, -64.0, forward, endPos);
tr = gi.trace(bomb->s.origin, NULL, NULL, endPos, bomb, MASK_SOLID);
VectorCopy(tr.endpos, bomb->s.origin);
vectoangles(tr.plane.normal, bomb->s.angles);
}
// set up ourself
setupBomb(bomb, "misc_ired", sk_tbomb_damage->value, sk_tbomb_radius->value);
if (bomb->targetname)
{
bomb->use = use_tripbomb;
}
else
{
bomb->think = create_tripbomb_laser;
bomb->nextthink = level.time + TBOMB_DELAY;
}
gi.linkentity(bomb);
}
//#endif // USE_ZAERO_ITEMS_WEAPONS
/*
======================================================================
Sonic Cannon
======================================================================
*/
#ifdef USE_ZAERO_WEAPONS
void weapon_sc_fire (edict_t *ent)
{
int maxfiretime = sk_soniccannon_maxfiretime->value;
if (!(ent->client->buttons & BUTTON_ATTACK))
{
ent->client->ps.gunframe++;
if(ent->client->weapon_sound && ent->client->ps.gunframe < 18)
{
ent->client->ps.gunframe = 18;
}
}
else
{
if(EMPNukeCheck(ent, ent->s.origin))
{
gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
ent->client->ps.gunframe = 18;
ent->client->weapon_sound = 0;
ent->weaponsound_time = 0;
ent->dmg_radius = 0;
ent->client->startFireTime = 0;
return;
}
if(!ent->client->startFireTime)
{
ent->client->startFireTime = level.time;
}
else if(level.time - ent->client->startFireTime >= maxfiretime)
{
ent->client->ps.gunframe = 17;
}
else
{
int old_cells = (int)ent->dmg_radius;
ent->dmg_radius = ((level.time - ent->client->startFireTime) / maxfiretime) * sk_soniccannon_maxcells->value;
if(old_cells < (int)ent->dmg_radius)
{
old_cells = (int)ent->dmg_radius - old_cells;
if(ent->client->pers.inventory[ent->client->ammo_index] < old_cells)
{
ent->dmg_radius -= (old_cells - ent->client->pers.inventory[ent->client->ammo_index]);
ent->client->pers.inventory[ent->client->ammo_index] = 0;
}
else
{
ent->client->pers.inventory[ent->client->ammo_index] -= old_cells;
}
}
}
if(!ent->client->pers.inventory[ent->client->ammo_index])
{
ent->client->ps.gunframe = 17;
if (level.time >= ent->pain_debounce_time)
{
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
ent->pain_debounce_time = level.time + 1;
}
NoAmmoWeaponChange (ent);
}
else
{
if(ent->weaponsound_time < level.time)
{
ent->client->weapon_sound = gi.soundindex("weapons/sonic/sc_fire.wav");
}
}
fire_sconnanEffects (ent);
ent->client->ps.gunframe++;
if (ent->client->ps.gunframe == 18 && (level.time - ent->client->startFireTime) < maxfiretime && ent->client->pers.inventory[ent->client->ammo_index])
ent->client->ps.gunframe = 12;
}
if (ent->client->ps.gunframe == 18)
{
ent->client->weapon_sound = 0;
ent->weaponsound_time = 0;
if(EMPNukeCheck(ent, ent->s.origin))
{
gi.sound (ent, CHAN_AUTO, gi.soundindex("items/empnuke/emp_missfire.wav"), 1, ATTN_NORM, 0);
}
else
{
if (!is_silenced)
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_cool.wav"), 1, ATTN_NORM, 0);
else
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_cool.wav"), 0.4, ATTN_NORM, 0);
if(ent->dmg_radius)
{
fire_sconnan (ent);
}
}
ent->dmg_radius = 0;
ent->client->startFireTime = 0;
}
}
void Weapon_SonicCannon (edict_t *ent)
{
static int pause_frames[] = {32, 42, 52, 0};
static int fire_frames[] = {12, 13, 14, 15, 16, 17, 0};
if (ent->client->ps.gunframe == 0)
{
#ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows...
if (deathmatch->value)
#endif
{
if (!is_silenced)
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_act.wav"), 1, ATTN_NORM, 0);
else
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_act.wav"), 0.4, ATTN_NORM, 0);
}
ent->weaponsound_time = 0;
ent->client->startFireTime = 0;
ent->dmg_radius = 0;
}
else if (ent->client->ps.gunframe == 53)
{
#ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows...
if (deathmatch->value)
#endif
{
if (!is_silenced)
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_dact.wav"), 1, ATTN_NORM, 0);
else
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_dact.wav"), 0.4, ATTN_NORM, 0);
}
}
else if((ent->client->buttons & BUTTON_ATTACK) && ent->weaponsound_time == 0)
{
ent->weaponsound_time = level.time + 0.4;
if (!is_silenced)
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_warm.wav"), 1, ATTN_NORM, 0);
else
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/sonic/sc_warm.wav"), 0.4, ATTN_NORM, 0);
}
Weapon_Generic (ent, 6, 22, 52, 57, pause_frames, fire_frames, weapon_sc_fire);
if (is_quadfire)
Weapon_Generic (ent, 6, 22, 52, 57, pause_frames, fire_frames, weapon_sc_fire);
}
void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage);
void fire_sconnanEffects (edict_t *self)
{
vec3_t start, end;
vec3_t forward, right;
vec3_t offset, v;
trace_t tr;
AngleVectors (self->client->v_angle, forward, right, NULL);
VectorScale (forward, -3, self->client->kick_origin);
self->client->kick_angles[0] = -3;
VectorSet(offset, 0, 7, self->viewheight-8);
P_ProjectSource (self->client, self->s.origin, offset, forward, right, start);
VectorMA (start, WORLD_SIZE, forward, end); // was 8192
tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA);
VectorMA (tr.endpos, -5, forward, end);
VectorSet(v, crandom() * 10 - 20, crandom() * 10 - 20, crandom() * 10 - 20);
SpawnDamage(TE_SHIELD_SPARKS, end, v, 0);
}
void scexplode_think (edict_t *self)
{
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_ROCKET_EXPLOSION);
gi.WritePosition (self->s.origin);
gi.multicast (self->s.origin, MULTICAST_PHS);
G_FreeEdict (self);
}
void fire_sconnan (edict_t *self)
{
vec3_t start, end, explodepos;
vec3_t forward, right, up;
vec3_t offset;
trace_t tr;
float damage, radius;
damage = self->dmg_radius / sk_soniccannon_maxcells->value;
radius = damage * sk_soniccannon_radius->value;
damage = sk_soniccannon_damage->value + (damage * sk_soniccannon_damage2->value);
AngleVectors (self->client->v_angle, forward, right, up);
VectorScale (forward, -3, self->client->kick_origin);
self->client->kick_angles[0] = -3;
VectorSet(offset, 0, 7, self->viewheight-8);
P_ProjectSource (self->client, self->s.origin, offset, forward, right, start);
VectorMA (start, WORLD_SIZE, forward, end); // was 8192
tr = gi.trace (start, NULL, NULL, end, self, MASK_SHOT|CONTENTS_SLIME|CONTENTS_LAVA);
if ((tr.ent != self) && (tr.ent->takedamage))
{
T_Damage (tr.ent, self, self, forward, tr.endpos, tr.plane.normal, damage, 0, 0, MOD_SONICCANNON);
}
T_RadiusDamagePosition (tr.endpos, self, self, damage, tr.ent, radius, MOD_SONICCANNON);
VectorMA (tr.endpos, -5, forward, end);
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_ROCKET_EXPLOSION);
gi.WritePosition (end);
gi.multicast (self->s.origin, MULTICAST_PHS);
damage -= 100;
radius = 0.1;
while (damage > 0)
{
edict_t *explode;
VectorMA (end, (50 * crandom()) - 5, forward, explodepos);
VectorMA (explodepos, (50 * crandom()) - 5, right, explodepos);
VectorMA (explodepos, (50 * crandom()) - 5, up, explodepos);
explode = G_Spawn();
VectorCopy (explodepos, explode->s.origin);
explode->classname = "sconnanExplode";
explode->nextthink = level.time + radius;
explode->think = scexplode_think;
radius += 0.1;
damage -= 100;
}
// play quad damage sound
// playQuadSound (self);
}
#endif // USE_ZAERO_WEAPONS
/*
======================================================================
Flares
======================================================================
*/
#define FLASH_RANGE 256.0
void FoundTarget (edict_t *self);
void flare_flash(edict_t *ent)
{
edict_t *target = NULL;
// flash
while (1)
{
float dist;
float ratio;
float dot;
vec3_t delta;
vec3_t forward;
// get the next entity near us
target = findradius (target, ent->s.origin, FLASH_RANGE);
if (target == NULL)
break;
if (!target->client && !(target->svflags & SVF_MONSTER))
continue;
if (target->deadflag)
continue;
if (!visible(ent, target))
continue;
// if (!infront(target, ent))
// continue;
// what's the distance, so that closer get's more
VectorSubtract (ent->s.origin, target->s.origin, delta);
dist = VectorLength (delta);
ratio = 1 - (dist/FLASH_RANGE);
if (ratio < 0)
ratio = 0;
// looking to the side get's less
AngleVectors (target->s.angles, forward, NULL, NULL);
VectorNormalize (delta);
dot = Z_MAX(0.0, DotProduct(delta, forward));
ratio *= dot;// * 1.25;
// set the flash counter
if (target->client)
{
target->client->flashTime += ratio*25;
if (target->client->flashTime > 25)
target->client->flashTime = 25;
target->client->flashBase = 30;
if (deathmatch->value &&
// !target->client->pers.gl_polyblend &&
!(((int)zdmflags->value) & ZDM_NO_GL_POLYBLEND_DAMAGE))
T_Damage(target, ent, ent->owner, vec3_origin, target->s.origin, vec3_origin, (int)(10.0*ratio), 0, 0, MOD_GL_POLYBLEND);
}
else if ((target->svflags & SVF_MONSTER) && strcmp(target->classname, "monster_zboss") != 0)
{
target->monsterinfo.flashTime =
Z_MAX(target->monsterinfo.flashTime, ratio*150); // a little bit more advantageous
target->monsterinfo.flashBase = 50;
if (target->enemy == NULL)
{
target->enemy = ent->owner;
FoundTarget(target);
}
}
}
}
void flare_think(edict_t *self)
{
edict_t *target = NULL;
edict_t *closestEnt = NULL;
float closestDist = 0.0;
// on our last leg?
if (level.time > self->timeout)
{
self->s.effects &= ~EF_ROCKET;
self->think = G_FreeEdict;
self->nextthink = level.time + 4.0;
self->s.frame = 0;
self->s.sound = 0;
return;
}
self->s.frame++;
if (self->s.frame > 14)
self->s.frame = 5;
// hissing sound
self->s.sound = gi.soundindex ("weapons/flare/flarehis.wav");
// do the visual thing
flare_flash(self);
// next frame
self->nextthink = level.time + FRAMETIME;
}
void fire_flare (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
{
edict_t *flare;
vec3_t adir;
vec3_t up;
vectoangles (dir, adir);
AngleVectors (adir, NULL, NULL, up);
flare = G_Spawn();
VectorCopy (start, flare->s.origin);
VectorCopy (dir, flare->movedir);
vectoangles (dir, flare->s.angles);
VectorScale (dir, speed, flare->velocity);
VectorMA (flare->velocity, 200 + crandom() * 10.0, up, flare->velocity);
flare->movetype = MOVETYPE_BOUNCE;
flare->clipmask = MASK_SHOT;
flare->solid = SOLID_BBOX;
flare->s.effects = EF_ROCKET;
VectorSet(flare->mins, -4, -4, -4);
VectorSet(flare->maxs, 4, 4, 4);
flare->s.modelindex = gi.modelindex("models/objects/flare/tris.md2");
flare->owner = self;
// flare->timeout = level.time + sk_flare_life->value;
flare->timeout = level.time + 8000/speed;
flare->nextthink = level.time + 1.0;
flare->think = flare_think;
flare->dmg = damage;
flare->radius_dmg = radius_damage;
flare->dmg_radius = damage_radius;
flare->classname = "flare";
if (self->client)
check_dodge (self, flare->s.origin, dir, speed);
gi.linkentity (flare);
}
//#ifdef USE_ZAERO_ITEMS_WEAPONS
void Weapon_FlareLauncher_Fire (edict_t *ent)
{
vec3_t offset, start;
vec3_t forward, right;
AngleVectors (ent->client->v_angle, forward, right, NULL);
VectorSet(offset, 8, 8, ent->viewheight-8);
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
fire_flare(ent, start, forward, sk_flaregun_damage->value, sk_flaregun_speed->value, sk_flaregun_radius->value, sk_flaregun_rdamage->value);
ent->client->ps.gunframe++;
PlayerNoise(ent, start, PNOISE_WEAPON);
// play quad sound
// playQuadSound (ent);
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
ent->client->pers.inventory[ent->client->ammo_index]--;
// play shooting sound
if (!is_silenced)
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/flare/shoot.wav"), 1, ATTN_NORM, 0);
else
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/flare/shoot.wav"), 0.4, ATTN_NORM, 0);
}
void Weapon_FlareGun (edict_t *ent)
{
static int pause_frames[] = {15, 25, 35, 0};
static int fire_frames[] = {8, 0};
Weapon_Generic (ent, 5, 14, 44, 48, pause_frames, fire_frames, Weapon_FlareLauncher_Fire);
if (is_quadfire)
Weapon_Generic (ent, 5, 14, 44, 48, pause_frames, fire_frames, Weapon_FlareLauncher_Fire);
}
//#endif // USE_ZAERO_ITEMS_WEAPONS
/*
======================================================================
Sniper Rifle
======================================================================
*/
#ifdef USE_ZAERO_WEAPONS
void fire_sniper_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick)
{
trace_t tr;
vec3_t end, s;
edict_t *ignore = self;
int i=0;
VectorMA (start, WORLD_SIZE, aimdir, end); // was 8192
VectorCopy(start, s);
while (i < 256) // Knightmare- prevent infinite loop, was 1
{
tr = gi.trace (s, NULL, NULL, end, ignore, MASK_SHOT_NO_WINDOW);
if (tr.fraction >= 1.0)
return;
// if we hit a plasmashield, then pass thru it
if (Q_stricmp(tr.ent->classname, "PlasmaShield") == 0)
{
ignore = tr.ent;
VectorCopy(tr.endpos, s);
}
else
break;
i++;
}
gi.WriteByte (svc_temp_entity);
if (gi.pointcontents(tr.endpos) & MASK_WATER)
{
if (tr.plane.normal[2] > 0.7)
gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
else
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
}
else
{
if (tr.plane.normal[2] > 0.7)
gi.WriteByte (TE_GRENADE_EXPLOSION);
else
gi.WriteByte (TE_ROCKET_EXPLOSION);
}
gi.WritePosition (tr.endpos);
gi.multicast (tr.endpos, MULTICAST_PHS);
if (tr.ent->takedamage)
T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_NO_ARMOR, MOD_SNIPERRIFLE);
}
void weapon_sniperrifle_fire (edict_t *ent)
{
vec3_t forward, right;
vec3_t offset, start;
int damage;
int kick;
if (deathmatch->value)
{ // normal damage is too extreme in dm
damage = sk_sniperrifle_damage_dm->value; // 150
kick = sk_sniperrifle_kick_dm->value; // 300
}
else
{
damage = sk_sniperrifle_damage->value; // 250
kick = sk_sniperrifle_kick->value; // 400
}
if (is_quad) {
damage *= 4;
kick *= 4;
}
if (is_double) {
damage *= 2;
kick *= 2;
}
AngleVectors (ent->client->v_angle, forward, right, NULL);
// centre the shot
VectorSet(offset, 0, 0, ent->viewheight);
VectorAdd(ent->s.origin, offset, start);
fire_sniper_bullet(ent, start, forward, damage, kick);
if (!is_silenced)
gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/fire.wav"), 1, ATTN_NORM, 0);
else
gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/fire.wav"), 0.4, ATTN_NORM, 0);
PlayerNoise(ent, start, PNOISE_WEAPON);
// play quad sound
// playQuadSound (ent);
VectorScale (forward, -20, ent->client->kick_origin);
ent->client->kick_angles[0] = -2;
ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity;
}
//#define SNIPER_CHARGE_TIME 30
void Weapon_SniperRifle(edict_t *ent)
{
/*
Activate/Deactivate
0 - 8 : Activate
9 - 18 : Fire
19 - 27 : Idle 1
28 - 36 : Idle 2
37 - 41 : Deactivate
Zoom
0 - 1 Zoom
Hold 1 while zoomed
*/
const static int activateStart = 0;
const static int activateEnd = 8;
const static int deactivateStart = 37;
const static int deactivateEnd = 41;
const static int spFov = 15;
const static int dmFov = 30;
if (ent->client->weaponstate == WEAPON_DROPPING)
{
ent->client->sniperFramenum = 0;
if (ent->client->ps.gunframe == deactivateStart)
{
// back to old fov
ent->client->ps.fov = 90;
#ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows...
if (deathmatch->value)
#endif
gi.sound(ent, CHAN_WEAPON2, gi.soundindex("weapons/sniper/snip_bye.wav"), 1, ATTN_NORM, 0);
}
else if (ent->client->ps.gunframe == deactivateEnd)
{
ChangeWeapon(ent);
return;
}
ent->client->ps.gunframe++;
return;
}
if (ent->client->weaponstate == WEAPON_ACTIVATING)
{
if (ent->client->ps.gunframe == activateStart)
{
// play the activation sound
#ifndef KMQUAKE2_ENGINE_MOD // Knightmare- no need to worry about overflows...
if (deathmatch->value)
#endif
gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sniper/snip_act.wav"), 1, ATTN_NORM, 0);
}
else if (ent->client->ps.gunframe == activateEnd)
{
ent->client->weaponstate = WEAPON_READY;
ent->client->ps.gunindex = (deathmatch->value ?
gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") :
gi.modelindex("models/weapons/v_sniper/scope/tris.md2") );
ent->client->ps.gunframe = 0;
ent->client->ps.fov = (deathmatch->value ? dmFov : spFov);
ent->client->sniperFramenum = level.framenum + (sk_sniper_charge_time->value * 10); // SNIPER_CHARGE_TIME
return;
}
ent->client->ps.gunframe++;
return;
}
if ((ent->client->newweapon) && (ent->client->weaponstate != WEAPON_FIRING))
{
// back to other gun model
ent->client->ps.gunindex = gi.modelindex("models/weapons/v_sniper/tris.md2");
ent->client->weaponstate = WEAPON_DROPPING;
ent->client->ps.gunframe = deactivateStart;
return;
}
if (ent->client->weaponstate == WEAPON_READY)
{
ent->client->ps.gunindex = (deathmatch->value ?
gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") :
gi.modelindex("models/weapons/v_sniper/scope/tris.md2") );
ent->client->ps.fov = (deathmatch->value ? dmFov : spFov);
// beep if the sniper frame num is a multiple of 10
if (ent->client->sniperFramenum >= level.framenum)
{
if ((ent->client->sniperFramenum - level.framenum) % 10 == 1)
gi.sound(ent, CHAN_WEAPON2, gi.soundindex("weapons/sniper/beep.wav"), 1, ATTN_NORM, 0);
}
if ( ((ent->client->latched_buttons|ent->client->buttons) & BUTTON_ATTACK) )
{
if (level.framenum >= ent->client->sniperFramenum)
{
ent->client->latched_buttons &= ~BUTTON_ATTACK;
if ((!ent->client->ammo_index) ||
( ent->client->pers.inventory[ent->client->ammo_index] >= ent->client->pers.weapon->quantity))
{
ent->client->weaponstate = WEAPON_FIRING;
// start the animation
ent->client->anim_priority = ANIM_ATTACK;
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
{
ent->s.frame = FRAME_crattak1-1;
ent->client->anim_end = FRAME_crattak9;
}
else
{
ent->s.frame = FRAME_attack1-1;
ent->client->anim_end = FRAME_attack8;
}
}
else
{
if (level.time >= ent->pain_debounce_time)
{
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
ent->pain_debounce_time = level.time + 1;
}
NoAmmoWeaponChange (ent);
}
}
}
}
if (ent->client->weaponstate == WEAPON_FIRING)
{
ent->client->ps.gunindex = (deathmatch->value ?
gi.modelindex("models/weapons/v_sniper/dmscope/tris.md2") :
gi.modelindex("models/weapons/v_sniper/scope/tris.md2") );
ent->client->ps.fov = (deathmatch->value ? dmFov : spFov);
//if (ent->client->quad_framenum > level.framenum)
// gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
// fire
weapon_sniperrifle_fire(ent);
// start recharge
ent->client->weaponstate = WEAPON_READY;
ent->client->sniperFramenum = level.framenum + (sk_sniper_charge_time->value * 10); // SNIPER_CHARGE_TIME;
}
}
#endif // USE_ZAERO_WEAPONS
/*
======================================================================
Armageddon 2000
======================================================================
*/
#ifdef USE_ZAERO_WEAPONS
void weapon_a2k_exp_think(edict_t *self)
{
self->s.frame++;
self->s.skinnum++;
if (self->s.frame == 6)
{
G_FreeEdict(self);
return;
}
self->nextthink = level.time + FRAMETIME;
}
void Z_RadiusDamageVisible(edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
{
float points;
edict_t *ent = NULL;
vec3_t v;
vec3_t dir;
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
{
if (ent == ignore)
continue;
if (!ent->takedamage)
continue;
if (!visible(inflictor, ent))
continue;
VectorAdd (ent->mins, ent->maxs, v);
VectorMA (ent->s.origin, 0.5, v, v);
VectorSubtract (inflictor->s.origin, v, v);
points = damage - 0.5 * VectorLength (v);
if (ent == attacker)
points = points * 0.5;
if (points > 0)
{
if (CanDamage (ent, inflictor))
{
VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
}
}
}
}
//#define A2K_FRAMENUM 50
void weapon_a2k_fire (edict_t *ent)
{
if (ent->client->ps.gunframe == 14)
{
ent->client->a2kFramenum = level.framenum + (sk_a2k_detonate_time->value * 10); // A2K_FRAMENUM;
ent->client->pers.inventory[ent->client->ammo_index]--;
ent->client->ps.gunframe++;
// start scream sound
// ent->client->weapon_sound = gi.soundindex("weapons/a2k/countdn.wav");
gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/a2k/countdn.wav"), 1, ATTN_NORM, 0);
// gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/a2k/ak_trig.wav"), 1, ATTN_NORM, 0);
// play quad sound
// playQuadSound (ent);
}
else if (ent->client->a2kFramenum == level.framenum)
{
// boom
edict_t *exp = NULL;
edict_t *e = NULL;
float damage = sk_a2k_damage->value; // was 2500
float dmg_radius = sk_a2k_radius->value; // was 512
// play quad sound
// playQuadSound (ent);
if (is_quad) {
damage *= 4;
dmg_radius *= 4;
}
if (is_double) {
damage *= 4;
dmg_radius *= 4;
}
// do some damage
T_RadiusDamage(ent, ent, damage, NULL, dmg_radius, MOD_A2K);
// ok, now, do who ever's visible within 1024 units
Z_RadiusDamageVisible(ent, ent, damage, NULL, dmg_radius * 2, MOD_A2K);
exp = G_Spawn();
exp->classname = "A2K Explosion";
exp->solid = SOLID_NOT;
exp->movetype = MOVETYPE_NONE;
VectorClear(exp->mins);
VectorClear(exp->maxs);
VectorCopy(ent->s.origin, exp->s.origin);
exp->s.modelindex = gi.modelindex("models/objects/b_explode/tris.md2");
exp->s.frame = 0;
exp->s.skinnum = 6;
exp->think = weapon_a2k_exp_think;
exp->nextthink = level.time + FRAMETIME;
gi.linkentity(exp);
gi.positioned_sound(exp->s.origin, exp, CHAN_AUTO, gi.soundindex("weapons/a2k/ak_exp01.wav"), 1, ATTN_NORM, 0);
ent->client->ps.gunframe++;
ent->client->weapon_sound = 0;
return;
}
else if (ent->client->ps.gunframe == 19)
{
// don't increase the gunframe
return;
}
}
void Weapon_A2k (edict_t *ent)
{
/*
00-09 Active
10-19 Boom (14 actual fire frame)
20-29 Idle1
30-39 Idle2
40-49 Idle3
50-55 Away
*/
static int pause_frames[] = {20, 30, 40, 0};
static int fire_frames[] = {14, 19, 0};
Weapon_Generic (ent, 9, 19, 49, 55, pause_frames, fire_frames, weapon_a2k_fire);
}
#endif // USE_ZAERO_WEAPONS
/*
======================================================================
Push
0 - Start push
4 - Contact
8 - End Push
======================================================================
*/
#if 0
qboolean push_hit (edict_t *self, vec3_t start, vec3_t aim, int damage, int kick)
{
trace_t tr;
vec3_t end;
vec3_t v;
edict_t *e = NULL;
//see if enemy is in range
VectorMA(start, 64, aim, end);
tr = gi.trace(start, NULL, NULL, end, self, MASK_SHOT);
if (tr.fraction >= 1)
return false;
// play sound
gi.sound(self, CHAN_WEAPON, gi.soundindex("weapons/push/contact.wav"), 1, ATTN_NORM, 0);
if (tr.ent->svflags & SVF_MONSTER ||
tr.ent->client)
{
// do our special form of knockback here
VectorMA (tr.ent->absmin, 0.75, tr.ent->size, v);
VectorSubtract (v, start, v);
VectorNormalize (v);
VectorMA (tr.ent->velocity, kick, v, tr.ent->velocity);
//tr.ent->velocity[2] = 48;
if (tr.ent->velocity[2] > 0)
tr.ent->groundentity = NULL;
}
else if (tr.ent->movetype == MOVETYPE_FALLFLOAT)
{
if (tr.ent->touch)
{
float mass = tr.ent->mass;
tr.ent->mass *= 0.25;
tr.ent->touch(tr.ent, self, NULL, NULL);
tr.ent->mass = mass;
}
}
// ok, we hit something, damage it
if (!tr.ent->takedamage)
return false;
// do the damage
T_Damage (tr.ent, self, self, aim, tr.endpos, vec3_origin, damage, kick/2, DAMAGE_NO_KNOCKBACK, MOD_HIT);
return true;
}
void Action_Push (edict_t *ent)
{
if (ent->client->ps.gunframe == 0)
{
// play grunt sound
//gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/push/grunt.wav"), 1, ATTN_NORM, 0);
ent->client->ps.gunframe++;
}
else if (ent->client->ps.gunframe == 4)
{
vec3_t forward;
vec3_t offset;
vec3_t start;
// contact
AngleVectors(ent->client->v_angle, forward, NULL, NULL);
VectorSet(offset, 0, 0, ent->viewheight * 0.5);
VectorAdd(ent->s.origin, offset, start);
push_hit(ent, start, forward, 2, 512);
ent->client->ps.gunframe++;
}
else if (ent->client->ps.gunframe == 8)
{
// go back to old weapon
ent->client->newweapon = ent->client->pers.lastweapon;
ChangeWeapon(ent);
}
else
ent->client->ps.gunframe++;
}
#endif