mirror of
https://github.com/id-Software/quake2-rerelease-dll.git
synced 2025-02-22 11:21:22 +00:00
446 lines
No EOL
11 KiB
C++
446 lines
No EOL
11 KiB
C++
// Copyright (c) ZeniMax Media Inc.
|
|
// Licensed under the GNU General Public License 2.0.
|
|
|
|
#include "../g_local.h"
|
|
#include "../m_player.h"
|
|
|
|
void weapon_prox_fire(edict_t *ent)
|
|
{
|
|
vec3_t start, dir;
|
|
// Paril: kill sideways angle on grenades
|
|
// limit upwards angle so you don't fire behind you
|
|
P_ProjectSource(ent, { max(-62.5f, ent->client->v_angle[0]), ent->client->v_angle[1], ent->client->v_angle[2] }, { 8, 0, -8 }, start, dir);
|
|
|
|
P_AddWeaponKick(ent, ent->client->v_forward * -2, { -1.f, 0.f, 0.f });
|
|
|
|
fire_prox(ent, start, dir, damage_multiplier, 600);
|
|
|
|
gi.WriteByte(svc_muzzleflash);
|
|
gi.WriteEntity(ent);
|
|
gi.WriteByte(MZ_PROX | is_silenced);
|
|
gi.multicast(ent->s.origin, MULTICAST_PVS, false);
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
G_RemoveAmmo(ent);
|
|
}
|
|
|
|
void Weapon_ProxLauncher(edict_t *ent)
|
|
{
|
|
constexpr int pause_frames[] = { 34, 51, 59, 0 };
|
|
constexpr int fire_frames[] = { 6, 0 };
|
|
|
|
Weapon_Generic(ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_prox_fire);
|
|
}
|
|
|
|
void weapon_tesla_fire(edict_t *ent, bool held)
|
|
{
|
|
vec3_t start, dir;
|
|
// Paril: kill sideways angle on grenades
|
|
// limit upwards angle so you don't throw behind you
|
|
P_ProjectSource(ent, { max(-62.5f, ent->client->v_angle[0]), ent->client->v_angle[1], ent->client->v_angle[2] }, { 0, 0, -22 }, start, dir);
|
|
|
|
gtime_t timer = ent->client->grenade_time - level.time;
|
|
int speed = (int) (ent->health <= 0 ? GRENADE_MINSPEED : min(GRENADE_MINSPEED + (GRENADE_TIMER - timer).seconds() * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER.seconds()), GRENADE_MAXSPEED));
|
|
|
|
ent->client->grenade_time = 0_ms;
|
|
|
|
fire_tesla(ent, start, dir, damage_multiplier, speed);
|
|
|
|
G_RemoveAmmo(ent, 1);
|
|
}
|
|
|
|
void Weapon_Tesla(edict_t *ent)
|
|
{
|
|
constexpr int pause_frames[] = { 21, 0 };
|
|
|
|
Throw_Generic(ent, 8, 32, -1, nullptr, 1, 2, pause_frames, false, nullptr, weapon_tesla_fire, false);
|
|
}
|
|
|
|
//======================================================================
|
|
// ROGUE MODS BELOW
|
|
//======================================================================
|
|
|
|
//
|
|
// CHAINFIST
|
|
//
|
|
constexpr int32_t CHAINFIST_REACH = 24;
|
|
|
|
void weapon_chainfist_fire(edict_t *ent)
|
|
{
|
|
if (!(ent->client->buttons & BUTTON_ATTACK))
|
|
{
|
|
if (ent->client->ps.gunframe == 13 ||
|
|
ent->client->ps.gunframe == 23 ||
|
|
ent->client->ps.gunframe >= 32)
|
|
{
|
|
ent->client->ps.gunframe = 33;
|
|
return;
|
|
}
|
|
}
|
|
|
|
int damage = 7;
|
|
|
|
if (deathmatch->integer)
|
|
damage = 15;
|
|
|
|
if (is_quad)
|
|
damage *= damage_multiplier;
|
|
|
|
// set start point
|
|
vec3_t start, dir;
|
|
|
|
P_ProjectSource(ent, ent->client->v_angle, { 0, 0, -4 }, start, dir);
|
|
|
|
if (fire_player_melee(ent, start, dir, CHAINFIST_REACH, damage, 100, MOD_CHAINFIST))
|
|
{
|
|
if (ent->client->empty_click_sound < level.time)
|
|
{
|
|
ent->client->empty_click_sound = level.time + 500_ms;
|
|
gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/sawslice.wav"), 1.f, ATTN_NORM, 0.f);
|
|
}
|
|
}
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
ent->client->ps.gunframe++;
|
|
|
|
if (ent->client->buttons & BUTTON_ATTACK)
|
|
{
|
|
if (ent->client->ps.gunframe == 12)
|
|
ent->client->ps.gunframe = 14;
|
|
else if (ent->client->ps.gunframe == 22)
|
|
ent->client->ps.gunframe = 24;
|
|
else if (ent->client->ps.gunframe >= 32)
|
|
ent->client->ps.gunframe = 7;
|
|
}
|
|
|
|
// start the animation
|
|
if (ent->client->anim_priority != ANIM_ATTACK || frandom() < 0.25f)
|
|
{
|
|
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;
|
|
}
|
|
ent->client->anim_time = 0_ms;
|
|
}
|
|
}
|
|
|
|
// this spits out some smoke from the motor. it's a two-stroke, you know.
|
|
void chainfist_smoke(edict_t *ent)
|
|
{
|
|
vec3_t tempVec, dir;
|
|
P_ProjectSource(ent, ent->client->v_angle, { 8, 8, -4 }, tempVec, dir);
|
|
|
|
gi.WriteByte(svc_temp_entity);
|
|
gi.WriteByte(TE_CHAINFIST_SMOKE);
|
|
gi.WritePosition(tempVec);
|
|
gi.unicast(ent, 0);
|
|
}
|
|
|
|
void Weapon_ChainFist(edict_t *ent)
|
|
{
|
|
constexpr int pause_frames[] = { 0 };
|
|
|
|
Weapon_Repeating(ent, 4, 32, 57, 60, pause_frames, weapon_chainfist_fire);
|
|
|
|
// smoke on idle sequence
|
|
if (ent->client->ps.gunframe == 42 && irandom(8))
|
|
{
|
|
if ((ent->client->pers.hand != CENTER_HANDED) && frandom() < 0.4f)
|
|
chainfist_smoke(ent);
|
|
}
|
|
else if (ent->client->ps.gunframe == 51 && irandom(8))
|
|
{
|
|
if ((ent->client->pers.hand != CENTER_HANDED) && frandom() < 0.4f)
|
|
chainfist_smoke(ent);
|
|
}
|
|
|
|
// set the appropriate weapon sound.
|
|
if (ent->client->weaponstate == WEAPON_FIRING)
|
|
ent->client->weapon_sound = gi.soundindex("weapons/sawhit.wav");
|
|
else if (ent->client->weaponstate == WEAPON_DROPPING)
|
|
ent->client->weapon_sound = 0;
|
|
else if (ent->client->pers.weapon->id == IT_WEAPON_CHAINFIST)
|
|
ent->client->weapon_sound = gi.soundindex("weapons/sawidle.wav");
|
|
}
|
|
|
|
//
|
|
// Disintegrator
|
|
//
|
|
|
|
void weapon_tracker_fire(edict_t *self)
|
|
{
|
|
vec3_t end;
|
|
edict_t *enemy;
|
|
trace_t tr;
|
|
int damage;
|
|
vec3_t mins, maxs;
|
|
|
|
// PMM - felt a little high at 25
|
|
if (deathmatch->integer)
|
|
damage = 45;
|
|
else
|
|
damage = 135;
|
|
|
|
if (is_quad)
|
|
damage *= damage_multiplier; // pgm
|
|
|
|
mins = { -16, -16, -16 };
|
|
maxs = { 16, 16, 16 };
|
|
|
|
vec3_t start, dir;
|
|
P_ProjectSource(self, self->client->v_angle, { 24, 8, -8 }, start, dir);
|
|
|
|
end = start + (dir * 8192);
|
|
enemy = nullptr;
|
|
// PMM - doing two traces .. one point and one box.
|
|
contents_t mask = MASK_PROJECTILE;
|
|
|
|
// [Paril-KEX]
|
|
if (!G_ShouldPlayersCollide(true))
|
|
mask &= ~CONTENTS_PLAYER;
|
|
|
|
G_LagCompensate(self, start, dir);
|
|
tr = gi.traceline(start, end, self, mask);
|
|
G_UnLagCompensate();
|
|
if (tr.ent != world)
|
|
{
|
|
if ((tr.ent->svflags & SVF_MONSTER) || tr.ent->client || (tr.ent->flags & FL_DAMAGEABLE))
|
|
{
|
|
if (tr.ent->health > 0)
|
|
enemy = tr.ent;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tr = gi.trace(start, mins, maxs, end, self, mask);
|
|
if (tr.ent != world)
|
|
{
|
|
if ((tr.ent->svflags & SVF_MONSTER) || tr.ent->client || (tr.ent->flags & FL_DAMAGEABLE))
|
|
{
|
|
if (tr.ent->health > 0)
|
|
enemy = tr.ent;
|
|
}
|
|
}
|
|
}
|
|
|
|
P_AddWeaponKick(self, self->client->v_forward * -2, { -1.f, 0.f, 0.f });
|
|
|
|
fire_tracker(self, start, dir, damage, 1000, enemy);
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte(svc_muzzleflash);
|
|
gi.WriteEntity(self);
|
|
gi.WriteByte(MZ_TRACKER | is_silenced);
|
|
gi.multicast(self->s.origin, MULTICAST_PVS, false);
|
|
|
|
PlayerNoise(self, start, PNOISE_WEAPON);
|
|
|
|
G_RemoveAmmo(self);
|
|
}
|
|
|
|
void Weapon_Disintegrator(edict_t *ent)
|
|
{
|
|
constexpr int pause_frames[] = { 14, 19, 23, 0 };
|
|
constexpr int fire_frames[] = { 5, 0 };
|
|
|
|
Weapon_Generic(ent, 4, 9, 29, 34, pause_frames, fire_frames, weapon_tracker_fire);
|
|
}
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
ETF RIFLE
|
|
|
|
======================================================================
|
|
*/
|
|
void weapon_etf_rifle_fire(edict_t *ent)
|
|
{
|
|
int damage;
|
|
int kick = 3;
|
|
int i;
|
|
vec3_t offset;
|
|
|
|
if (deathmatch->integer)
|
|
damage = 10;
|
|
else
|
|
damage = 10;
|
|
|
|
if (!(ent->client->buttons & BUTTON_ATTACK))
|
|
{
|
|
ent->client->ps.gunframe = 8;
|
|
return;
|
|
}
|
|
|
|
if (ent->client->ps.gunframe == 6)
|
|
ent->client->ps.gunframe = 7;
|
|
else
|
|
ent->client->ps.gunframe = 6;
|
|
|
|
// PGM - adjusted to use the quantity entry in the weapon structure.
|
|
if (ent->client->pers.inventory[ent->client->pers.weapon->ammo] < ent->client->pers.weapon->quantity)
|
|
{
|
|
ent->client->ps.gunframe = 8;
|
|
NoAmmoWeaponChange(ent, true);
|
|
return;
|
|
}
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= damage_multiplier;
|
|
kick *= damage_multiplier;
|
|
}
|
|
|
|
vec3_t kick_origin {}, kick_angles {};
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
kick_origin[i] = crandom() * 0.85f;
|
|
kick_angles[i] = crandom() * 0.85f;
|
|
}
|
|
P_AddWeaponKick(ent, kick_origin, kick_angles);
|
|
|
|
// get start / end positions
|
|
if (ent->client->ps.gunframe == 6)
|
|
offset = { 15, 8, -8 };
|
|
else
|
|
offset = { 15, 6, -8 };
|
|
|
|
vec3_t start, dir;
|
|
P_ProjectSource(ent, ent->client->v_angle + kick_angles, offset, start, dir);
|
|
fire_flechette(ent, start, dir, damage, 1150, kick);
|
|
Weapon_PowerupSound(ent);
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte(svc_muzzleflash);
|
|
gi.WriteEntity(ent);
|
|
gi.WriteByte((ent->client->ps.gunframe == 6 ? MZ_ETF_RIFLE : MZ_ETF_RIFLE_2) | is_silenced);
|
|
gi.multicast(ent->s.origin, MULTICAST_PVS, false);
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
G_RemoveAmmo(ent);
|
|
|
|
ent->client->anim_priority = ANIM_ATTACK;
|
|
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
|
{
|
|
ent->s.frame = FRAME_crattak1 - (int) (frandom() + 0.25f);
|
|
ent->client->anim_end = FRAME_crattak9;
|
|
}
|
|
else
|
|
{
|
|
ent->s.frame = FRAME_attack1 - (int) (frandom() + 0.25f);
|
|
ent->client->anim_end = FRAME_attack8;
|
|
}
|
|
ent->client->anim_time = 0_ms;
|
|
}
|
|
|
|
void Weapon_ETF_Rifle(edict_t *ent)
|
|
{
|
|
constexpr int pause_frames[] = { 18, 28, 0 };
|
|
|
|
Weapon_Repeating(ent, 4, 7, 37, 41, pause_frames, weapon_etf_rifle_fire);
|
|
}
|
|
|
|
constexpr int32_t HEATBEAM_DM_DMG = 15;
|
|
constexpr int32_t HEATBEAM_SP_DMG = 15;
|
|
|
|
void Heatbeam_Fire(edict_t *ent)
|
|
{
|
|
bool firing = (ent->client->buttons & BUTTON_ATTACK);
|
|
bool has_ammo = ent->client->pers.inventory[ent->client->pers.weapon->ammo] >= ent->client->pers.weapon->quantity;
|
|
|
|
if (!firing || !has_ammo)
|
|
{
|
|
ent->client->ps.gunframe = 13;
|
|
ent->client->weapon_sound = 0;
|
|
ent->client->ps.gunskin = 0;
|
|
|
|
if (firing && !has_ammo)
|
|
NoAmmoWeaponChange(ent, true);
|
|
return;
|
|
}
|
|
|
|
// start on frame 8
|
|
if (ent->client->ps.gunframe > 12)
|
|
ent->client->ps.gunframe = 8;
|
|
else
|
|
ent->client->ps.gunframe++;
|
|
|
|
if (ent->client->ps.gunframe == 12)
|
|
ent->client->ps.gunframe = 8;
|
|
|
|
// play weapon sound for firing
|
|
ent->client->weapon_sound = gi.soundindex("weapons/bfg__l1a.wav");
|
|
ent->client->ps.gunskin = 1;
|
|
|
|
int damage;
|
|
int kick;
|
|
|
|
// for comparison, the hyperblaster is 15/20
|
|
// jim requested more damage, so try 15/15 --- PGM 07/23/98
|
|
if (deathmatch->integer)
|
|
damage = HEATBEAM_DM_DMG;
|
|
else
|
|
damage = HEATBEAM_SP_DMG;
|
|
|
|
if (deathmatch->integer) // really knock 'em around in deathmatch
|
|
kick = 75;
|
|
else
|
|
kick = 30;
|
|
|
|
if (is_quad)
|
|
{
|
|
damage *= damage_multiplier;
|
|
kick *= damage_multiplier;
|
|
}
|
|
|
|
ent->client->kick.time = 0_ms;
|
|
|
|
// This offset is the "view" offset for the beam start (used by trace)
|
|
vec3_t start, dir;
|
|
P_ProjectSource(ent, ent->client->v_angle, { 7, 2, -3 }, start, dir);
|
|
|
|
// This offset is the entity offset
|
|
G_LagCompensate(ent, start, dir);
|
|
fire_heatbeam(ent, start, dir, { 2, 7, -3 }, damage, kick, false);
|
|
G_UnLagCompensate();
|
|
Weapon_PowerupSound(ent);
|
|
|
|
// send muzzle flash
|
|
gi.WriteByte(svc_muzzleflash);
|
|
gi.WriteEntity(ent);
|
|
gi.WriteByte(MZ_HEATBEAM | is_silenced);
|
|
gi.multicast(ent->s.origin, MULTICAST_PVS, false);
|
|
|
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
|
|
|
G_RemoveAmmo(ent);
|
|
|
|
ent->client->anim_priority = ANIM_ATTACK;
|
|
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
|
{
|
|
ent->s.frame = FRAME_crattak1 - (int) (frandom() + 0.25f);
|
|
ent->client->anim_end = FRAME_crattak9;
|
|
}
|
|
else
|
|
{
|
|
ent->s.frame = FRAME_attack1 - (int) (frandom() + 0.25f);
|
|
ent->client->anim_end = FRAME_attack8;
|
|
}
|
|
ent->client->anim_time = 0_ms;
|
|
}
|
|
|
|
void Weapon_Heatbeam(edict_t *ent)
|
|
{
|
|
constexpr int pause_frames[] = { 35, 0 };
|
|
|
|
Weapon_Repeating(ent, 8, 12, 42, 47, pause_frames, Heatbeam_Fire);
|
|
} |