Skip to content

Commit

Permalink
Only consume ammo for shots that were actually fired
Browse files Browse the repository at this point in the history
We currently check for and reduce ammo count before checking if a shot
can actually be fired. For burst weapons with interruptibleBurst = true,
this can cause extra ammo to be consumed without a corresponding shot
being made. Instead, only consume ammo after firing the shot.
  • Loading branch information
mszabo-wikia committed Nov 24, 2024
1 parent f6b2260 commit dc9272d
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 111 deletions.
104 changes: 52 additions & 52 deletions Source/CombatExtended/CombatExtended/Comps/CompAmmoUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,36 +337,12 @@ private void AssignJobToWielder(Job job)
}
}

public bool Notify_ShotFired()
{
if (ammoToBeDeleted != null)
{
ammoToBeDeleted.Destroy();
ammoToBeDeleted = null;
CompInventory.UpdateInventory();
if (!HasAmmoOrMagazine)
{
return false;
}
}
return true;
}

public bool Notify_PostShotFired()
{
if (!HasAmmoOrMagazine)
{
DoOutOfAmmoAction();
return false;
}
return true;
}

/// <summary>
/// <para>Reduces ammo count and updates inventory if necessary, call this whenever ammo is consumed by the gun (e.g. firing a shot, clearing a jam). </para>
/// <para>Has an optional argument for the amount of ammo to consume per shot, which defaults to 1; this caters for special cases such as different sci-fi weapons using up different amounts of the same energy cell ammo type per shot, or a double-barrelled shotgun that fires both cartridges at the same time (projectile treated as a single, more powerful bullet)</para>
/// </summary>
public bool TryReduceAmmoCount(int ammoConsumedPerShot = 1)
public void Notify_ShotFired(int ammoConsumedPerShot = 1)
{
ammoConsumedPerShot = (ammoConsumedPerShot > 0) ? ammoConsumedPerShot : 1;

Expand All @@ -378,48 +354,72 @@ public bool TryReduceAmmoCount(int ammoConsumedPerShot = 1)
// Mag-less weapons feed directly from inventory
if (!HasMagazine)
{
if (UseAmmo)
if (ammoToBeDeleted != null)
{
if (!TryFindAmmoInInventory(out ammoToBeDeleted))
{
return false;
}
if (ammoToBeDeleted.def != CurrentAmmo)
{
currentAmmoInt = ammoToBeDeleted.def as AmmoDef;
}

if (ammoToBeDeleted.stackCount > 1)
{
ammoToBeDeleted = ammoToBeDeleted.SplitOff(1);
}
ammoToBeDeleted.Destroy();
ammoToBeDeleted = null;
CompInventory.UpdateInventory();
}
return true;
}
// If magazine is empty, return false

if (curMagCountInt <= 0)
{
CurMagCount = 0;
return false;
Log.Error($"{parent} tried reducing its ammo count when already empty");
}
// Reduce ammo count and update inventory
CurMagCount = (curMagCountInt - ammoConsumedPerShot < 0) ? 0 : curMagCountInt - ammoConsumedPerShot;
}


/*if (curMagCountInt - ammoConsumedPerShot < 0)
public bool Notify_PostShotFired()
{
if (!HasAmmoOrMagazine)
{
curMagCountInt = 0;
} else
DoOutOfAmmoAction();
return false;
}
return true;
}

/// <summary>
/// Check whether ammo is available for firing a shot.
/// </summary>
/// <remarks>
/// For weapons without a magazine, this may update the currently selected ammo type
/// if we ran out of the currently selected ammo type but have different, compatible, types
/// available in the inventory.
/// </remarks>
/// <returns></returns>
public bool TryPrepareShot()
{
if (HasMagazine)
{
curMagCountInt = curMagCountInt - ammoConsumedPerShot;
}*/
// If magazine is empty, return false
if (curMagCountInt <= 0)
{
CurMagCount = 0;
return false;
}

return true;
}

// Original: curMagCountInt--;
if (curMagCountInt < 0)
if (UseAmmo)
{
TryStartReload();
if (!TryFindAmmoInInventory(out ammoToBeDeleted))
{
return false;
}
if (ammoToBeDeleted.def != CurrentAmmo)
{
currentAmmoInt = ammoToBeDeleted.def as AmmoDef;
}

if (ammoToBeDeleted.stackCount > 1)
{
ammoToBeDeleted = ammoToBeDeleted.SplitOff(1);
}
}

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class Verb_LaunchProjectileCE : Verb
protected float distance = 10f;

public CompCharges compCharges = null;
public CompAmmoUser compAmmo = null;

public CompFireModes compFireModes = null;
public CompChangeableProjectile compChangeable = null;
public CompApparelReloadable compReloadable = null;
Expand Down Expand Up @@ -155,26 +155,10 @@ public float ShootingAccuracy
public float SightsEfficiency => EquipmentSource?.GetStatValue(CE_StatDefOf.SightsEfficiency) ?? 1f;
public virtual float SwayAmplitude => Mathf.Max(0, (4.5f - ShootingAccuracy) * (EquipmentSource?.GetStatValue(CE_StatDefOf.SwayFactor) ?? 1f));

// Ammo variables
public virtual CompAmmoUser CompAmmo
{
get
{
if (compAmmo == null && EquipmentSource != null)
{
compAmmo = EquipmentSource.TryGetComp<CompAmmoUser>();
}
return compAmmo;
}
}
public virtual ThingDef Projectile
{
get
{
if (CompAmmo != null && CompAmmo.CurrentAmmo != null)
{
return CompAmmo.CurAmmoProjectile;
}
if (CompChangeable != null && CompChangeable.Loaded)
{
return CompChangeable.Projectile;
Expand Down Expand Up @@ -236,8 +220,6 @@ public float RecoilAmount
}
}

private bool IsAttacking => ShooterPawn?.CurJobDef == JobDefOf.AttackStatic || WarmingUp;

private LightingTracker _lightingTracker = null;
protected LightingTracker LightingTracker
{
Expand Down Expand Up @@ -302,14 +284,7 @@ public override bool Available()
}
}

// Add check for reload
if (Projectile == null || (IsAttacking && CompAmmo != null && !CompAmmo.CanBeFiredNow))
{
CompAmmo?.TryStartReload();
resetRetarget();
return false;
}
return true;
return Projectile != null;
}

/// <summary>
Expand Down Expand Up @@ -487,19 +462,13 @@ public virtual void ShiftTarget(ShiftVecReport report, bool calculateMechanicalO
Apparel LegArmor = LegArmors.MaxByWithFallback(funcArmor);
#endregion

#region get CompAmmo's Current ammo projectile

var ProjCE = (ProjectilePropertiesCE)compAmmo?.CurAmmoProjectile?.projectile ?? null;

#endregion

#region checks for whether the pawn can penetrate armor, which armor is stronger, etc

var TargetedBodyPartArmor = TorsoArmor;

bool flagTorsoArmor = ((TorsoArmor?.GetStatValue(StatDefOf.ArmorRating_Sharp) ?? 0.1f) >= (Helmet?.GetStatValue(StatDefOf.ArmorRating_Sharp) ?? 0f));

bool flag2 = ((ProjCE?.armorPenetrationSharp ?? 0f) >= (TorsoArmor?.GetStatValue(StatDefOf.ArmorRating_Sharp) ?? 0.1f));
bool flag2 = (projectilePropsCE.armorPenetrationSharp >= (TorsoArmor?.GetStatValue(StatDefOf.ArmorRating_Sharp) ?? 0.1f));
//Headshots do too little damage too often, so if the pawn can penetrate torso armor, they should aim at it
if ((flagTorsoArmor && !flag2))
{
Expand All @@ -509,7 +478,7 @@ public virtual void ShiftTarget(ShiftVecReport report, bool calculateMechanicalO
bool flag3 = (TargetedBodyPartArmor?.GetStatValue(StatDefOf.ArmorRating_Sharp) ?? 0f) >= ((LegArmor?.GetStatValue(StatDefOf.ArmorRating_Sharp) ?? 0f) + 4f);

//bool for whether the pawn can penetrate helmet
bool flag4 = ((ProjCE?.armorPenetrationSharp ?? 0f) >= (Helmet?.GetStatValue(StatDefOf.ArmorRating_Sharp) ?? 0.1f));
bool flag4 = (projectilePropsCE.armorPenetrationSharp >= (Helmet?.GetStatValue(StatDefOf.ArmorRating_Sharp) ?? 0.1f));

//if the pawn can penetrate the helmet or torso armor there's no need to aim for legs
if (flag3 && (!flag4) && (!flag2))
Expand Down Expand Up @@ -1132,11 +1101,6 @@ public override bool TryCastShot()
numShotsFired++;
if (ShooterPawn != null)
{
if (CompAmmo != null && !CompAmmo.CanBeFiredNow)
{
CompAmmo?.TryStartReload();
resetRetarget();
}
if (CompReloadable != null)
{
CompReloadable.UsedOnce();
Expand Down
59 changes: 51 additions & 8 deletions Source/CombatExtended/CombatExtended/Verbs/Verb_ShootCE.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class Verb_ShootCE : Verb_LaunchProjectileCE

public Vector3 drawPos;

private CompAmmoUser compAmmo;

#endregion

#region Properties
Expand Down Expand Up @@ -134,6 +136,17 @@ public float SpreadDegrees
// Whether our shooter is currently under suppressive fire
private bool IsSuppressed => ShooterPawn?.TryGetComp<CompSuppressable>()?.isSuppressed ?? false;

public CompAmmoUser CompAmmo
{
get
{
compAmmo ??= EquipmentSource?.TryGetComp<CompAmmoUser>();
return compAmmo;
}
}

public override ThingDef Projectile => CompAmmo?.CurrentAmmo != null ? CompAmmo.CurAmmoProjectile : base.Projectile;

#endregion

#region Methods
Expand Down Expand Up @@ -248,6 +261,25 @@ public override void WarmupComplete()
}
}

public override bool Available()
{
if (!base.Available())
{
return false;
}

// Add check for reload
bool isAttacking = ShooterPawn?.CurJobDef == JobDefOf.AttackStatic || WarmingUp;
if (isAttacking && !(CompAmmo?.CanBeFiredNow ?? true))
{
CompAmmo?.TryStartReload();
resetRetarget();
return false;
}

return true;
}

public override void VerbTickCE()
{
if (_isAiming)
Expand Down Expand Up @@ -379,13 +411,9 @@ public void ExternalCallDropCasing(int randomSeedOffset = -1)

public override bool TryCastShot()
{
//Reduce ammunition
if (CompAmmo != null)
if (!CompAmmo?.TryPrepareShot() ?? false)
{
if (!CompAmmo.TryReduceAmmoCount(((CompAmmo.Props.ammoSet != null) ? CompAmmo.Props.ammoSet.ammoConsumedPerShot : 1) * VerbPropsCE.ammoConsumedPerShotCount))
{
return false;
}
return false;
}
if (base.TryCastShot())
{
Expand All @@ -409,10 +437,25 @@ protected virtual bool OnCastSuccessful()
{
CE_Utility.GenerateAmmoCasings(projectilePropsCE, fromPawn ? drawPos : caster.DrawPos, caster.Map, AimAngle, VerbPropsCE.recoilAmount, fromPawn: fromPawn, extension: ext);
}

if (CompAmmo == null)
{
return true;
}

int ammoConsumedPerShot = (CompAmmo.Props.ammoSet?.ammoConsumedPerShot ?? 1) * VerbPropsCE.ammoConsumedPerShotCount;
CompAmmo.Notify_ShotFired(ammoConsumedPerShot);

if (ShooterPawn != null && !CompAmmo.CanBeFiredNow)
{
CompAmmo.TryStartReload();
resetRetarget();
}

// This needs to here for weapons without magazine to ensure their last shot plays sounds
if (CompAmmo != null && !CompAmmo.HasMagazine && CompAmmo.UseAmmo)
if (!CompAmmo.HasMagazine && CompAmmo.UseAmmo)
{
if (!CompAmmo.Notify_ShotFired())
if (!CompAmmo.HasAmmoOrMagazine)
{
if (VerbPropsCE.muzzleFlashScale > 0.01f)
{
Expand Down
12 changes: 1 addition & 11 deletions Source/CombatExtended/CombatExtended/Verbs/Verb_ShootMortarCE.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,6 @@ public virtual bool TryCastGlobalShot()
numShotsFired++;
if (ShooterPawn != null)
{
if (CompAmmo != null && !CompAmmo.CanBeFiredNow)
{
CompAmmo?.TryStartReload();
}
if (CompReloadable != null)
{
CompReloadable.UsedOnce();
Expand All @@ -225,13 +221,7 @@ public override bool TryCastShot()
{
return base.TryCastShot();
}
if (CompAmmo != null)
{
if (!CompAmmo.TryReduceAmmoCount(CompAmmo.Props.ammoSet.ammoConsumedPerShot * VerbPropsCE.ammoConsumedPerShotCount))
{
return false;
}
}

if (this.TryCastGlobalShot())
{
return this.OnCastSuccessful();
Expand Down

0 comments on commit dc9272d

Please sign in to comment.