NSWeapon: - add "chargeTime" key

rework state machine in NSWeapon
reload timing separation in NSWeapon
This commit is contained in:
Marco Cawthorne 2024-08-05 18:06:18 -07:00
parent c3f527d5e5
commit 21b68d1938
Signed by: eukara
GPG key ID: CE2032F0A2882A22
6 changed files with 305 additions and 182 deletions

View file

@ -142,6 +142,7 @@ private:
PREDICTED_FLOAT(w_attack_next)
PREDICTED_FLOAT(w_idle_next)
PREDICTED_FLOAT(w_reload_next)
PREDICTED_FLOAT(teleport_time)
PREDICTED_FLOAT(weapontime)
PREDICTED_FLOAT(m_flStamina)

View file

@ -250,11 +250,9 @@ NSClientPlayer::ProcessInput(void)
/* weapon system */
if (input_buttons & INPUT_SECONDARY) {
m_activeWeapon._WeaponStartedFiring();
m_activeWeapon.SecondaryAttack();
m_activeWeapon._SecondaryAttack();
} else if (input_buttons & INPUT_PRIMARY) {
m_activeWeapon._WeaponStartedFiring();
m_activeWeapon.PrimaryAttack();
m_activeWeapon._PrimaryAttack();
} else if (input_buttons & INPUT_RELOAD) {
m_activeWeapon.Reload();
} else {
@ -734,6 +732,7 @@ NSClientPlayer::ReceiveEntity(float new, float flChanged)
READENTITY_FLOAT(weapontime, PLAYER_TIMINGS)
READENTITY_FLOAT(w_attack_next, PLAYER_TIMINGS)
READENTITY_FLOAT(w_idle_next, PLAYER_TIMINGS)
READENTITY_FLOAT(w_reload_next, PLAYER_TIMINGS)
READENTITY_ENTNUM(vehicle_entnum, PLAYER_VEHICLE)
READENTITY_BYTE(spec_ent, PLAYER_SPECTATE)
READENTITY_BYTE(spec_mode, PLAYER_SPECTATE)
@ -825,6 +824,7 @@ NSClientPlayer::PredictPreFrame(void)
SAVE_STATE(weapontime)
SAVE_STATE(w_attack_next)
SAVE_STATE(w_idle_next)
SAVE_STATE(w_reload_next)
SAVE_STATE(vehicle_entnum)
SAVE_STATE(spec_ent)
SAVE_STATE(spec_mode)
@ -878,6 +878,7 @@ NSClientPlayer::PredictPostFrame(void)
ROLL_BACK(weapontime)
ROLL_BACK(w_attack_next)
ROLL_BACK(w_idle_next)
ROLL_BACK(w_reload_next)
ROLL_BACK(vehicle_entnum)
ROLL_BACK(spec_ent)
ROLL_BACK(spec_mode)
@ -929,6 +930,7 @@ NSClientPlayer::Save(float handle)
SaveFloat(handle, "pmove_flags", pmove_flags);
SaveFloat(handle, "w_attack_next", w_attack_next);
SaveFloat(handle, "w_idle_next", w_idle_next);
SaveFloat(handle, "w_reload_next", w_reload_next);
SaveFloat(handle, "teleport_time", teleport_time);
SaveFloat(handle, "m_flStamina", m_flStamina);
SaveInt(handle, "weaponframe", weaponframe);
@ -1000,6 +1002,9 @@ NSClientPlayer::Restore(string strKey, string strValue)
case "w_idle_next":
w_idle_next = ReadFloat(strValue);
break;
case "w_reload_next":
w_reload_next = ReadFloat(strValue);
break;
case "teleport_time":
teleport_time = ReadFloat(strValue);
break;
@ -1255,6 +1260,7 @@ NSClientPlayer::EvaluateEntity(void)
EVALUATE_FIELD(weapontime, PLAYER_TIMINGS)
EVALUATE_FIELD(w_attack_next, PLAYER_TIMINGS)
EVALUATE_FIELD(w_idle_next, PLAYER_TIMINGS)
EVALUATE_FIELD(w_reload_next, PLAYER_TIMINGS)
EVALUATE_FIELD(vehicle, PLAYER_VEHICLE)
EVALUATE_FIELD(spec_ent, PLAYER_SPECTATE)
EVALUATE_FIELD(spec_mode, PLAYER_SPECTATE)
@ -1344,6 +1350,7 @@ NSClientPlayer::SendEntity(entity ePEnt, float flChanged)
SENDENTITY_FLOAT(weapontime, PLAYER_TIMINGS)
SENDENTITY_FLOAT(w_attack_next, PLAYER_TIMINGS)
SENDENTITY_FLOAT(w_idle_next, PLAYER_TIMINGS)
SENDENTITY_FLOAT(w_reload_next, PLAYER_TIMINGS)
SENDENTITY_ENTITY(vehicle, PLAYER_VEHICLE)
SENDENTITY_BYTE(spec_ent, PLAYER_SPECTATE)
SENDENTITY_BYTE(spec_mode, PLAYER_SPECTATE)

View file

@ -853,12 +853,15 @@ void
EFX_UpdateListener(NSView playerView)
{
static NSSoundScape lastScape;
vector vecPlayer;
vector camPos = playerView.GetCameraOrigin();
vector camAngle = playerView.GetCameraAngle();
NSSoundScape bestScape = __NULL__;
vecPlayer = playerView.GetCameraOrigin();
makevectors(playerView.GetCameraAngle());
SetListener(vecPlayer, v_forward, v_right, v_up, 12);
if (lastScape) {
SetListener(camPos, anglesToForward(camAngle), anglesToRight(camAngle), anglesToUp(camAngle), 12);
} else {
SetListener(camPos, anglesToForward(camAngle), anglesToRight(camAngle), anglesToUp(camAngle), 0);
}
if (autocvar_s_al_use_reverb == false) {
return;
@ -869,12 +872,12 @@ EFX_UpdateListener(NSView playerView)
NSSoundScape scape = (NSSoundScape)e;
other = world;
traceline(scape.origin, vecPlayer, MOVE_OTHERONLY, scape);
traceline(scape.origin, camPos, MOVE_OTHERONLY, scape);
if (trace_fraction < 1.0f) {
continue;
}
float dist = vlen(e.origin - vecPlayer);
float dist = vlen(e.origin - camPos);
if (dist > scape.m_radius) {
continue;
}

View file

@ -44,8 +44,22 @@ typedef enum
WEAPONSTATE_RELOAD_START,
WEAPONSTATE_RELOAD,
WEAPONSTATE_RELOAD_END,
WEAPONSTATE_CHARGING,
WEAPONSTATE_FIRELOOP,
WEAPONSTATE_RELEASED
} nsweapon_state_t;
string nsweapon_state_s[] =
{
"WEAPONSTATE_IDLE",
"WEAPONSTATE_RELOAD_START",
"WEAPONSTATE_RELOAD",
"WEAPONSTATE_RELOAD_END",
"WEAPONSTATE_CHARGING",
"WEAPONSTATE_FIRELOOP",
"WEAPONSTATE_RELEASED"
};
typedef enum
{
WEPEVENT_FIRED,
@ -63,9 +77,6 @@ related keys get forwarded only to items of this class.
- "targetname" : Name
- "weapon_scriptobject" : mapC progs with the weapon code within.
- "ammoType" : name of the ammo type def entry which the gun uses
- "ammoRequired" : set to 1 if we require ammo.
- "ammoPerShot" : Amount of ammo to deduct per shot.
- "clipSize" : maximum clip size
- "mtr_flashShader" : muzzleflash material to Use.
- "model_flash" : muzzleflash model/sprite to use.
@ -79,6 +90,25 @@ related keys get forwarded only to items of this class.
- "continuousSmoke" : whether the particle effect is continous
- "clipSizeDefault" : CUSTOM: Default clip size on pickup.
## Attack related keys
- "def_onFire" : Def to spawn when the weapon is fired.
- "def_onRelease" : Def to spawn when the weapon has been released.
## Ammo management related keys
- "ammoType" : name of the ammo type def entry which the weapon uses
- "ammoRequired" : set to 1 if we require ammo.
- "ammoPerShot" : Amount of ammo to deduct per successful shot.
### Charging/Overcharging
For an overcharge effect, spawn a self-destructive explosion on "def_onFire", and rely on "def_onRelease"
for delivering a lethal charge to other enemies.
- "chargeTime" : Amount of time the weapon has to charge before "def_onFire" is spawned.
### Overheating weapons
Overheating of the weapon is done when both keys are set.
- "overheatLength" : Time in which it takes for the weapon to cool down.
- "overheatPerShot" : Time added against "overheat_length" when a shot is fired.
@ingroup baseclass
*/
class
@ -111,7 +141,7 @@ public:
virtual bool IsWeapon(void);
virtual bool HasReserveAmmo(void);
/** Overridable: Called when we switch to this weapon */
/** Overridable: Called when we switch to this weapon from another. */
virtual void Draw(void);
/** Overridable: Called when we are about to switch to another weapon */
virtual void Holster(void);
@ -121,8 +151,10 @@ public:
virtual void SecondaryAttack(void);
/** Overridable: On +reload execution. */
virtual void Reload(void);
/** Overridable: When no buttons are held. */
/** Overridable: When no buttons are held, or you forcefully want to stop firing. */
virtual void Release(void);
/** Overridable: When the weapon is supposed to be doing something on its own. */
virtual void Idle(void);
/** Overridable: When the HUD is requested to be drawn. */
virtual void UpdateGUI(void);
@ -130,11 +162,18 @@ public:
nonvirtual void SetWorldModel(string);
nonvirtual void SetPlayerModel(string);
nonvirtual void SetWeaponFrame(float);
nonvirtual void PlaySound(string, bool);
/* state */
nonvirtual void SetWeaponState(nsweapon_state_t);
nonvirtual nsweapon_state_t GetWeaponState(void);
virtual void SetAttackNext(float);
virtual void SetReloadNext(float);
virtual void SetIdleNext(float);
virtual bool CanFire(void);
virtual bool CanIdle(void);
virtual bool CanReload(void);
virtual bool UseAmmo(string);
/** Overridable: Called once when the weapon started firing. */
@ -172,6 +211,8 @@ private:
nonvirtual void _CacheWeaponDefVariables(void);
nonvirtual void _WeaponStartedFiring(void);
nonvirtual void _WeaponStoppedFiring(void);
nonvirtual void _PrimaryAttack(void);
nonvirtual void _SecondaryAttack(void);
#ifdef SERVER
nonvirtual void _ReloadFinished(void);
@ -230,6 +271,7 @@ private:
bool m_fiSemiAuto;
string m_fiSndFireLoop;
float m_flReloadSpeed;
float m_fiChargeTime;
NETWORKED_INT(m_iClip)
NETWORKED_INT(m_iClipSize)

View file

@ -64,6 +64,26 @@ NSWeapon::RemovedFromInventory(void)
{
}
void
NSWeapon::SetWeaponState(nsweapon_state_t newState)
{
if (m_dState == newState) {
return;
}
m_dState = newState;
#ifdef SERVER
printf("State changed to %s\n", nsweapon_state_s[newState]);
#endif
}
nsweapon_state_t
NSWeapon::GetWeaponState(void)
{
return (m_dState);
}
void
NSWeapon::UpdateFireInfoCache(void)
{
@ -77,10 +97,14 @@ NSWeapon::UpdateFireInfoCache(void)
m_fiAmmoType = ammoNumForName(GetSubDefString(m_strLastFireInfo, "ammoType"));
m_fiAmmoRequired = GetSubDefBool(m_strLastFireInfo, "ammoRequired");
m_fiFireRate = GetSubDefFloat(m_strLastFireInfo, "fireRate");
m_fiOnFire = GetSubDefString(m_strLastFireInfo, "def_onFire");
m_fiOnRelease = GetSubDefString(m_strLastFireInfo, "def_onRelease");
m_fiSemiAuto = GetSubDefBool(m_strLastFireInfo, "semiAuto");
/* defs to spawn */
m_fiOnFire = GetSubDefString(m_strLastFireInfo, "def_onFire");
m_fiOnRelease = GetSubDefString(m_strLastFireInfo, "def_onRelease");
m_fiChargeTime = GetSubDefFloat(m_strLastFireInfo, "chargeTime");
/* when -1.0 we'll pull it from the animation. */
if (reloadSpeed != "") {
m_flReloadSpeed = stof(reloadSpeed);
} else {
@ -96,10 +120,11 @@ NSWeapon::UpdateFireInfoCache(void)
#endif
/* validate */
if (ammoPerShot != __NULL__)
if (ammoPerShot != __NULL__) {
m_fiAmmoPerShot = (int)stoi(ammoPerShot);
else
} else {
m_fiAmmoPerShot = 1i;
}
}
void
@ -184,59 +209,6 @@ NSWeapon::SpawnKey(string keyName, string setValue)
case "continuousSmoke":
m_bSmokeContinous = ReadBool(setValue);
break;
case "actAltFire":
case "actAltFireLast":
case "actDraw":
case "actDrawEmpty":
case "actFire":
case "actFireLast":
case "actHolster":
case "actHolsterEmpty":
case "actLoop":
case "actIdle":
case "actIdleEmpty":
case "actMeleeHit":
case "actMeleeMiss":
case "actPull":
case "actReload":
case "actReloadEmpty":
case "actReloadEnd":
case "actReloadStart":
case "actThrow":
case "ammoRequired":
case "ammoPerShot":
case "def_altFireInfo":
case "def_damage_inhand":
case "def_explode_inhand":
case "def_fireInfo":
case "def_melee":
case "def_plant":
case "def_projectile":
case "detonateOnFire":
case "fireRate":
case "hudSlot":
case "hudSlotPos":
case "inv_name":
case "meleeRateHit":
case "meleeRateMiss":
case "melee_distance":
case "primed_fuse":
case "punchAngle":
case "powerAmmo":
case "removeOnEmpty":
case "silent_fire":
case "snd_altfire":
case "snd_empty":
case "snd_fire":
case "snd_reload":
case "snd_reload_start":
case "snd_reload_end":
case "weight":
case "trigger_delay":
case "zoomFov":
break;
default:
super::SpawnKey(keyName, setValue);
break;
@ -585,7 +557,6 @@ NSWeapon::_CacheWeaponDefVariables(void)
muzzleModel = GetSubDefString(m_primaryFireInfo, "model_flash");
m_muzzleModelIndex = getmodelindex(muzzleModel, false);
m_flTriggerDelay = GetSubDefFloat(m_primaryFireInfo, "trigger_delay");
m_flPrimedFuse = GetDefFloat("primed_fuse");
m_flZoomFOV = GetDefFloat("zoomFov") / 90;
m_bPowerAmmo = GetDefBool("powerAmmo");
@ -748,6 +719,12 @@ NSWeapon::FiredWeaponAttack(string fireInfo)
{
NSClientPlayer ourOwner = (NSClientPlayer)GetOwner();
ourOwner.AttackByDef(fireInfo, false);
SetWeaponState(WEAPONSTATE_IDLE);
/* prevent release from firing it again */
if (m_bPowerAmmo) {
m_fiWillRelease = false;
}
}
void
@ -755,6 +732,7 @@ NSWeapon::ReleasedWeaponAttack(string fireInfo)
{
NSClientPlayer ourOwner = (NSClientPlayer)GetOwner();
ourOwner.AttackByDef(fireInfo, true);
SetWeaponState(WEAPONSTATE_RELEASED);
}
void
@ -768,7 +746,7 @@ NSWeapon::Attack(string fireInfo)
ourOwner.gflags |= GF_SEMI_TOGGLED;
}
/* satchels, pipe bombs, etc. */
/* no real attack, detonate named satchels, pipe bombs, etc. */
if (m_fiDetonateOnFire != __NULL__) {
if (DetonateDef(m_fiDetonateOnFire) == true) {
SetWeaponFrame(GetSubDefAct(fireInfo, "actDetonate"));
@ -789,6 +767,8 @@ NSWeapon::Attack(string fireInfo)
NSError("Will release.");
}
/* melee attacks go through their own routine because we have to test
against objects and walls and if we can even hit them. */
if (m_meleeDef != "") {
vector eyePos = ourOwner.GetEyePos();
vector eyeAngles = ourOwner.v_angle;
@ -818,7 +798,7 @@ NSWeapon::Attack(string fireInfo)
SetIdleNext(meleeRate + 1.0f);
#ifdef SERVER
Sound_Play(ourOwner, CHAN_WEAPON, GetSubDefString(m_meleeDef, "snd_miss"));
PlaySound(GetSubDefString(m_meleeDef, "snd_miss"), false);
if (trace_fraction >= 1.0) {
return;
@ -837,10 +817,10 @@ NSWeapon::Attack(string fireInfo)
//Damage_Apply(trace_ent, ourOwner, meleeDamage, 0, DMG_BLUNT);
if (hitEnt.iBleeds) {
Sound_Play(ourOwner, CHAN_WEAPON, GetSubDefString(m_meleeDef, "snd_flesh"));
PlaySound(GetSubDefString(m_meleeDef, "snd_flesh"), false);
}
} else {
Sound_Play(ourOwner, CHAN_WEAPON, GetSubDefString(m_meleeDef, "snd_hit"));
PlaySound(GetSubDefString(m_meleeDef, "snd_hit"), false);
DecalGroups_Place("Impact.Shot", hitLoc + (anglesToForward(eyeAngles) * -2));
}
#endif
@ -866,18 +846,19 @@ NSWeapon::Attack(string fireInfo)
return;
}
/* this weapon has a timed delay before something shoots out. */
if (m_flTriggerDelay > 0.0f) {
if (ourOwner.vv_flags & VFL_PRIMEDFUSE) {
Release();
/* this weapon has a delay before something shoots out. */
if (m_fiChargeTime > 0.0f) {
/* we haven't yet tried firing while charging */
if (GetWeaponState() != WEAPONSTATE_CHARGING) {
shotAnim = GetSubDefAct(fireInfo, "actDelay");
SetAttackNext(m_fiChargeTime);
/* mark as charging, play loop anim in Idle() next */
SetIdleNext(frameduration(m_viewModel, shotAnim));
SetWeaponFrame(shotAnim);
SetWeaponState(WEAPONSTATE_CHARGING);
return;
}
shotAnim = GetSubDefAct(fireInfo, "actDelay");
SetAttackNext(m_flTriggerDelay);
SetWeaponFrame(shotAnim);
ourOwner.vv_flags |= VFL_PRIMEDFUSE;
//ourOwner.gflags |= GF_SEMI_TOGGLED;
return;
}
ourOwner.punchangle += m_fiPunchAngle;
@ -912,10 +893,43 @@ NSWeapon::Attack(string fireInfo)
m_bFiring = true;
}
void
NSWeapon::_PrimaryAttack(void)
{
SwitchFireInfo(m_primaryFireInfo);
_WeaponStartedFiring();
PrimaryAttack();
}
void
NSWeapon::_SecondaryAttack(void)
{
SwitchFireInfo(m_secondaryFireInfo);
_WeaponStartedFiring();
SecondaryAttack();
}
void
NSWeapon::PlaySound(string soundDef, bool clientOnly)
{
#ifdef CLIENT
if (clientOnly) {
Sound_Play(owner, CHAN_WEAPON, soundDef);
}
#endif
#ifdef SERVER
if (!clientOnly) {
Sound_Play(owner, CHAN_WEAPON, soundDef);
}
#endif
}
void
NSWeapon::PrimaryAttack(void)
{
NSClientPlayer ourOwner = (NSClientPlayer)GetOwner();
if (CanFire() == false) {
if (!(ourOwner.gflags & GF_SEMI_TOGGLED)) {
Release();
@ -924,10 +938,99 @@ NSWeapon::PrimaryAttack(void)
return;
}
SwitchFireInfo(m_primaryFireInfo);
Attack(m_primaryFireInfo);
}
void
NSWeapon::Idle(void)
{
NSClientPlayer ourOwner = (NSClientPlayer)GetOwner();
float idleAnim = 0;
if (CanIdle() == false) {
return;
}
/* handle shotgun style reloads */
switch (m_dState) {
case WEAPONSTATE_RELOAD_START:
idleAnim = GetDefAct("actReloadStart");
SetWeaponState(WEAPONSTATE_RELOAD);
PlaySound(GetDefString("snd_reload_start"), false);
break;
case WEAPONSTATE_RELOAD:
float reloadTime;
idleAnim = GetDefAct("actReload");
m_iClip++;
ourOwner.UseAmmo(m_primaryAmmoType, 1);
if (HasReserveAmmo() == false || m_iClip >= m_iClipSize) {
SetWeaponState(WEAPONSTATE_RELOAD_END);
}
#ifdef SERVER
PlaySound(GetDefString("snd_reload"), false);
#endif
if (m_flReloadSpeed == -1.0f) {
reloadTime = frameduration(m_viewModel, idleAnim);
} else {
reloadTime = m_flReloadSpeed;
}
SetWeaponFrame(idleAnim);
SetIdleNext(reloadTime);
return;
break;
case WEAPONSTATE_RELOAD_END:
idleAnim = GetDefAct("actReloadEnd");
SetWeaponState(WEAPONSTATE_IDLE);
#ifdef SERVER
PlaySound(GetDefString("snd_reload_end"), false);
#endif
break;
case WEAPONSTATE_FIRELOOP:
idleAnim = GetDefAct("actLoop");
break;
case WEAPONSTATE_CHARGING:
idleAnim = GetDefAct("actLoop");
break;
case WEAPONSTATE_RELEASED:
//breakpoint();
idleAnim = GetDefAct("actRelease");
SetWeaponState(WEAPONSTATE_IDLE);
break;
case WEAPONSTATE_IDLE:
default:
if (!m_iMode) {
if (m_iClipSize > 0 && m_iClip == 0) {
idleAnim = GetDefAct("actIdleEmpty");
/* this will mess with us otherwise */
if (!idleAnim)
return;
}
if (!idleAnim)
idleAnim = GetDefAct("actIdle");
} else {
if (m_iClipSize > 0 && m_iClip == 0) {
idleAnim = GetDefAct("actAltIdleEmpty");
/* this will mess with us otherwise */
if (!idleAnim)
return;
}
if (!idleAnim)
idleAnim = GetDefAct("actAltIdle");
}
}
SetWeaponFrame(idleAnim);
SetIdleNext(frameduration(m_viewModel, idleAnim));
}
void
NSWeapon::SecondaryAttack(void)
{
@ -945,9 +1048,9 @@ NSWeapon::SecondaryAttack(void)
#ifdef SERVER
if (ourOwner.viewzoom == 1.0) {
Sound_Play(ourOwner, CHAN_WEAPON, GetDefString("snd_lower_scope"));
PlaySound(GetDefString("snd_lower_scope"), false);
} else {
Sound_Play(ourOwner, CHAN_WEAPON, GetDefString("snd_raise_scope"));
PlaySound(GetDefString("snd_raise_scope"), false);
}
#endif
@ -961,10 +1064,12 @@ NSWeapon::SecondaryAttack(void)
return;
}
if (CanFire() == false)
/* don't run any further logic */
if (CanFire() == false) {
Idle();
return;
}
SwitchFireInfo(m_secondaryFireInfo);
Attack(m_secondaryFireInfo);
}
@ -978,8 +1083,9 @@ NSWeapon::_ReloadFinished(void)
ammoToDeduct = m_iClipSize - m_iClip;
/* not enough ammo. assign whatever is left. */
if (ourOwner.HasAmmo(m_primaryAmmoType, ammoToDeduct) == false)
if (ourOwner.HasAmmo(m_primaryAmmoType, ammoToDeduct) == false) {
ammoToDeduct = ourOwner.m_iAmmoTypes[m_primaryAmmoType];
}
m_iClip = m_iClipSize;
ourOwner.UseAmmo(m_primaryAmmoType, ammoToDeduct);
@ -990,7 +1096,6 @@ void
NSWeapon::Reload(void)
{
NSClientPlayer ourOwner = (NSClientPlayer)GetOwner();
string fireInfo;
float reloadAnimation = 0;
float reloadTime;
string ammoType;
@ -1008,17 +1113,11 @@ NSWeapon::Reload(void)
return;
}
if (CanFire() == false) {
if (CanReload() == false) {
return;
}
fireInfo = m_strLastFireInfo;
/* fall back to main weapon def */
if (!fireInfo)
fireInfo = classname;
ammoType = GetSubDefString(fireInfo, "ammoType");
ammoType = GetSubDefString(m_strLastFireInfo, "ammoType");
ammoTypeID = ammoNumForName(ammoType);
if (m_iClip == m_iClipSize) {
@ -1032,7 +1131,7 @@ NSWeapon::Reload(void)
/* we have a start-reload, so this is a shotgun styled weapon reload */
if (reloadStartAct) {
m_dState = WEAPONSTATE_RELOAD_START;
SetWeaponState(WEAPONSTATE_RELOAD_START);
Release();
return;
}
@ -1061,13 +1160,9 @@ NSWeapon::Reload(void)
reloadTime = m_flReloadSpeed;
}
PlaySound(GetDefString("snd_reload"), false);
#ifdef SERVER
string soundDef = GetDefString("snd_reload");
if (soundDef) {
ourOwner.StartSoundDef(soundDef, CHAN_WEAPON, true);
}
ScheduleThink(_ReloadFinished, reloadTime - 0.1f);
#endif
@ -1106,6 +1201,19 @@ NSWeapon::_WeaponStartedFiring(void)
/* hasn't been fired yet. */
if (!(owner.vv_flags & VFL_FIRING)) {
float actFireStart = GetSubDefAct(m_strLastFireInfo, "actFireStart");
#ifdef SERVER
printf("actFireStart %d %S\n", actFireStart, m_strLastFireInfo);
#endif
if (actFireStart) {
SetWeaponFrame(actFireStart);
SetAttackNext(frameduration(m_viewModel, actFireStart));
SetIdleNext(frameduration(m_viewModel, actFireStart));
SetWeaponState(WEAPONSTATE_FIRELOOP);
}
WeaponStartedFiring();
}
@ -1121,6 +1229,19 @@ NSWeapon::_WeaponStoppedFiring(void)
/* was still registed as firing */
if (owner.vv_flags & VFL_FIRING) {
float actFireStop = GetSubDefAct(m_strLastFireInfo, "actFireStop");
#ifdef SERVER
printf("actFireStop %d %S\n", actFireStop, m_strLastFireInfo);
#endif
if (actFireStop) {
SetWeaponFrame(actFireStop);
SetAttackNext(frameduration(m_viewModel, actFireStop));
SetIdleNext(frameduration(m_viewModel, actFireStop));
SetWeaponState(WEAPONSTATE_RELEASED);
}
WeaponStoppedFiring();
}
@ -1189,10 +1310,6 @@ NSWeapon::Release(void)
}
#endif
if (CanIdle() == false) {
return;
}
m_bFiring = false;
if (ourOwner.vv_flags & VFL_REDRAW) {
@ -1201,75 +1318,7 @@ NSWeapon::Release(void)
return;
}
/* handle shotgun style reloads */
switch (m_dState) {
case WEAPONSTATE_RELOAD_START:
idleAnim = GetDefAct("actReloadStart");
m_dState = WEAPONSTATE_RELOAD;
#ifdef SERVER
Sound_Play(ourOwner, CHAN_WEAPON, GetDefString("snd_reload_start"));
#endif
break;
case WEAPONSTATE_RELOAD:
float reloadTime;
idleAnim = GetDefAct("actReload");
m_iClip++;
ourOwner.UseAmmo(m_primaryAmmoType, 1);
if (HasReserveAmmo() == false || m_iClip >= m_iClipSize) {
m_dState = WEAPONSTATE_RELOAD_END;
}
#ifdef SERVER
Sound_Play(ourOwner, CHAN_WEAPON, GetDefString("snd_reload"));
#endif
if (m_flReloadSpeed == -1.0f) {
reloadTime = frameduration(m_viewModel, idleAnim);
} else {
reloadTime = m_flReloadSpeed;
}
SetWeaponFrame(idleAnim);
SetIdleNext(reloadTime);
return;
break;
case WEAPONSTATE_RELOAD_END:
idleAnim = GetDefAct("actReloadEnd");
m_dState = WEAPONSTATE_IDLE;
#ifdef SERVER
Sound_Play(ourOwner, CHAN_WEAPON, GetDefString("snd_reload_end"));
#endif
break;
case WEAPONSTATE_IDLE:
default:
if (!m_iMode) {
if (m_iClipSize > 0 && m_iClip == 0) {
idleAnim = GetDefAct("actIdleEmpty");
/* this will mess with us otherwise */
if (!idleAnim)
return;
}
if (!idleAnim)
idleAnim = GetDefAct("actIdle");
} else {
if (m_iClipSize > 0 && m_iClip == 0) {
idleAnim = GetDefAct("actAltIdleEmpty");
/* this will mess with us otherwise */
if (!idleAnim)
return;
}
if (!idleAnim)
idleAnim = GetDefAct("actAltIdle");
}
}
SetWeaponFrame(idleAnim);
SetIdleNext(frameduration(m_viewModel, idleAnim));
Idle();
}
void
@ -1332,6 +1381,14 @@ NSWeapon::SetAttackNext(float newDelay)
{
NSClientPlayer ourOwner = (NSClientPlayer)GetOwner();
ourOwner.w_attack_next = newDelay;
SetReloadNext(newDelay);
}
void
NSWeapon::SetReloadNext(float newDelay)
{
NSClientPlayer ourOwner = (NSClientPlayer)GetOwner();
ourOwner.w_reload_next = newDelay;
}
void
@ -1369,6 +1426,18 @@ NSWeapon::CanFire(void)
return (true);
}
bool
NSWeapon::CanReload(void)
{
NSClientPlayer ourOwner = (NSClientPlayer)GetOwner();
if (ourOwner.w_reload_next > 0.0f) {
return (false);
}
return (true);
}
/* required because we might need to look it up inside the
info for the primary attack mode. */
static string

View file

@ -514,6 +514,7 @@ NSClientPlayer::Physics_InputPostMove(void)
float punch;
/* timers, these are predicted and shared across client and server */
w_attack_next = max(0, w_attack_next - input_timelength);
w_reload_next = max(0, w_reload_next - input_timelength);
w_idle_next = max(0, w_idle_next - input_timelength);
weapontime += input_timelength;