mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
0d4e872ce9
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.
1647 lines
No EOL
41 KiB
C
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
|