quake2-rerelease-dll/rerelease/rogue/p_rogue_weapon.cpp
2023-08-07 14:48:30 -05:00

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);
}