From d210c9460ff58ecd8a28f6c9793d807c841341cc Mon Sep 17 00:00:00 2001 From: Alicecomma Date: Sun, 23 Feb 2020 00:41:12 +0100 Subject: [PATCH] WIP part 2 - Remove FT implementation - SpentAmmo having mass costs implemented - Magazine bulk implemented - Set Revolver to ejectsCasings = FALSE - Ported over previous "Fix-for-#1044" to new Development with PRs - ConservedMassFactorWhenFired added - ConfigError checking for Amount without backup - Loaded ammo statPart now includes several discussed things - VerbShootCE ejecting casings checks whether conservedMassFactorWhenFiring is positive --- Defs/Stats/Stats_Weapons_Ranged.xml | 10 ---- Languages/English/Keyed/BulkAndWeight.xml | 1 + Languages/English/Keyed/Keys.xml | 1 + Patches/Core/ThingDefs_Misc/Weapons_Guns.xml | 1 + Source/CombatExtended/CombatExtended.csproj | 1 - .../CombatExtended/Comps/CompAmmoUser.cs | 52 ++++++++++++------- .../Comps/CompProperties_AmmoUser.cs | 1 + .../CombatExtended/Defs/AmmoDef.cs | 1 + .../CombatExtended/Defs/AmmoLink.cs | 10 +++- .../CombatExtended/Defs/AmmoSetDef.cs | 16 ++++++ .../StatParts/StatPart_LoadedAmmo.cs | 49 ++++++++++++++--- .../StatWorker_AmmoConsumedPerShotCount.cs | 44 ---------------- .../CombatExtended/Verbs/VerbPropertiesCE.cs | 1 - .../CombatExtended/Verbs/Verb_ShootCE.cs | 5 +- 14 files changed, 105 insertions(+), 88 deletions(-) delete mode 100644 Source/CombatExtended/CombatExtended/StatWorkers/StatWorker_AmmoConsumedPerShotCount.cs diff --git a/Defs/Stats/Stats_Weapons_Ranged.xml b/Defs/Stats/Stats_Weapons_Ranged.xml index ecfad84759..9fd42d7200 100644 --- a/Defs/Stats/Stats_Weapons_Ranged.xml +++ b/Defs/Stats/Stats_Weapons_Ranged.xml @@ -97,15 +97,5 @@ true 897 - - - ammoConsumedPerShotCount - CombatExtended.StatWorker_AmmoConsumedPerShotCount - - How many units of ammunition this gun consumes for each individual shot.\n\nThis applies to guns with multiple barrels that fire simultaneously with each pull of the trigger, or exotic energy weapons that use up different amounts of energy from a power cell. - Weapon - true - 877 - diff --git a/Languages/English/Keyed/BulkAndWeight.xml b/Languages/English/Keyed/BulkAndWeight.xml index 96c348301f..a9eb33a5dc 100644 --- a/Languages/English/Keyed/BulkAndWeight.xml +++ b/Languages/English/Keyed/BulkAndWeight.xml @@ -39,5 +39,6 @@ Drop excess Pickup missing and drop excess Loaded ammunition + Spent cartridges diff --git a/Languages/English/Keyed/Keys.xml b/Languages/English/Keyed/Keys.xml index 5977c4d825..852558d0b2 100644 --- a/Languages/English/Keyed/Keys.xml +++ b/Languages/English/Keyed/Keys.xml @@ -74,6 +74,7 @@ Magazine size + Magazine bulk Reload time Caliber Fire modes diff --git a/Patches/Core/ThingDefs_Misc/Weapons_Guns.xml b/Patches/Core/ThingDefs_Misc/Weapons_Guns.xml index 7d18125b03..dbab21281e 100644 --- a/Patches/Core/ThingDefs_Misc/Weapons_Guns.xml +++ b/Patches/Core/ThingDefs_Misc/Weapons_Guns.xml @@ -115,6 +115,7 @@ Shot_Revolver GunTail_Light 9 + FALSE 6 diff --git a/Source/CombatExtended/CombatExtended.csproj b/Source/CombatExtended/CombatExtended.csproj index f605bfc2e1..3f1fc4d06a 100644 --- a/Source/CombatExtended/CombatExtended.csproj +++ b/Source/CombatExtended/CombatExtended.csproj @@ -178,7 +178,6 @@ - diff --git a/Source/CombatExtended/CombatExtended/Comps/CompAmmoUser.cs b/Source/CombatExtended/CombatExtended/Comps/CompAmmoUser.cs index 9b5c58d647..4e034c6763 100644 --- a/Source/CombatExtended/CombatExtended/Comps/CompAmmoUser.cs +++ b/Source/CombatExtended/CombatExtended/Comps/CompAmmoUser.cs @@ -14,6 +14,7 @@ public class CompAmmoUser : CompRangedGizmoGiver { #region Fields + private int lastLoadedMagCountInt = 0; private int curMagCountInt = 0; private AmmoDef currentAmmoInt = null; private AmmoDef selectedAmmo; @@ -35,6 +36,11 @@ public CompProperties_AmmoUser Props } } + bool ejectsCasings = false; + //Used by StatPart_LoadedAmmo to calculate remaining weight of e.g casings or spent batteries + public int SpentRounds => (ejectsCasings && ((CurAmmoProjectile.projectile as ProjectilePropertiesCE)?.dropsCasings ?? false)) + ? 0 : lastLoadedMagCountInt - curMagCountInt; + public int CurMagCount { get @@ -46,6 +52,8 @@ public int CurMagCount if (curMagCountInt != value && value >= 0) { curMagCountInt = value; + lastLoadedMagCountInt = Mathf.Max(lastLoadedMagCountInt, value); + if (CompInventory != null) CompInventory.UpdateInventory(); //Must be positioned after curMagCountInt is updated, because it relies on that value } } @@ -118,7 +126,11 @@ public AmmoDef CurrentAmmo return UseAmmo ? currentAmmoInt : null; } } - public ThingDef CurAmmoProjectile => Props.ammoSet?.ammoTypes?.FirstOrDefault(x => x.ammo == CurrentAmmo)?.projectile ?? parent.def.Verbs.FirstOrDefault().defaultProjectile; + public AmmoLink CurrentLink => Props.ammoSet?.ammoTypes? + .Where(x => x.ammo == CurrentAmmo && x.amount <= CurMagCount) + .MaxByWithFallback(x => x.amount); + public ThingDef CurAmmoProjectile => CurrentLink?.projectile + ?? parent.def.Verbs.FirstOrDefault().defaultProjectile; public CompInventory CompInventory { get @@ -172,6 +184,7 @@ public override void Initialize(CompProperties vprops) base.Initialize(vprops); //curMagCountInt = Props.spawnUnloaded && UseAmmo ? 0 : Props.magazineSize; + ejectsCasings = parent.def.Verbs.Select(x => x as VerbPropertiesCE).First()?.ejectsCasings ?? true; // Initialize ammo with default if none is set if (UseAmmo) @@ -196,6 +209,12 @@ public override void PostExposeData() Scribe_Values.Look(ref curMagCountInt, "count", 0); Scribe_Defs.Look(ref currentAmmoInt, "currentAmmo"); Scribe_Defs.Look(ref selectedAmmo, "selectedAmmo"); + + var val = SpentRounds; + Scribe_Values.Look(ref val, "conservedRounds", 0); + lastLoadedMagCountInt = curMagCountInt + val; + + ejectsCasings = parent.def.Verbs.Select(x => x as VerbPropertiesCE).First()?.ejectsCasings ?? true; } private void AssignJobToWielder(Job job) @@ -236,12 +255,11 @@ public bool Notify_PostShotFired() } /// - /// 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). - /// 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) + /// 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). /// - public bool TryReduceAmmoCount(int ammoConsumedPerShot = 1) + public bool TryReduceAmmoCount() { - ammoConsumedPerShot = (ammoConsumedPerShot > 0) ? ammoConsumedPerShot : 1; + var ammoConsumedPerShot = (CurrentLink?.amount ?? 1); if (Wielder == null && turret == null) { @@ -262,8 +280,12 @@ public bool TryReduceAmmoCount(int ammoConsumedPerShot = 1) currentAmmoInt = ammoToBeDeleted.def as AmmoDef; } - if (ammoToBeDeleted.stackCount > 1) - ammoToBeDeleted = ammoToBeDeleted.SplitOff(1); + //Set CurMagCount since it changes CurrentLink return value + CurMagCount = ammoToBeDeleted.stackCount; + ammoConsumedPerShot = (CurrentLink?.amount ?? 1); + + if (ammoToBeDeleted.stackCount > ammoConsumedPerShot) + ammoToBeDeleted = ammoToBeDeleted.SplitOff(ammoConsumedPerShot); } return true; } @@ -271,22 +293,11 @@ public bool TryReduceAmmoCount(int ammoConsumedPerShot = 1) if (curMagCountInt <= 0) { CurMagCount = 0; + lastLoadedMagCountInt = 0; return false; } // Reduce ammo count and update inventory - CurMagCount = (curMagCountInt - ammoConsumedPerShot < 0) ? 0 : curMagCountInt - ammoConsumedPerShot; - - - /*if (curMagCountInt - ammoConsumedPerShot < 0) - { - curMagCountInt = 0; - } else - { - curMagCountInt = curMagCountInt - ammoConsumedPerShot; - }*/ - - - // Original: curMagCountInt--; + CurMagCount -= ammoConsumedPerShot; if (curMagCountInt < 0) TryStartReload(); return true; @@ -403,6 +414,7 @@ public bool TryUnload(out Thing droppedAmmo, bool forceUnload = false) // don't forget to set the clip to empty... CurMagCount = 0; + lastLoadedMagCountInt = 0; return true; } diff --git a/Source/CombatExtended/CombatExtended/Comps/CompProperties_AmmoUser.cs b/Source/CombatExtended/CombatExtended/Comps/CompProperties_AmmoUser.cs index d4ab55a63a..959d501142 100644 --- a/Source/CombatExtended/CombatExtended/Comps/CompProperties_AmmoUser.cs +++ b/Source/CombatExtended/CombatExtended/Comps/CompProperties_AmmoUser.cs @@ -11,6 +11,7 @@ namespace CombatExtended public class CompProperties_AmmoUser : CompProperties { public int magazineSize = 0; + public float magazineBulk = 0f; public float reloadTime = 1; public bool reloadOneAtATime = false; public bool throwMote = true; diff --git a/Source/CombatExtended/CombatExtended/Defs/AmmoDef.cs b/Source/CombatExtended/CombatExtended/Defs/AmmoDef.cs index 5728845cca..97d0a3f54a 100644 --- a/Source/CombatExtended/CombatExtended/Defs/AmmoDef.cs +++ b/Source/CombatExtended/CombatExtended/Defs/AmmoDef.cs @@ -14,6 +14,7 @@ public class AmmoDef : ThingDef public int defaultAmmoCount = 1; public float cookOffSpeed = 1f; public float cookOffFlashScale = 1; + public float conservedMassFactorWhenFired = 0.1f; public ThingDef cookOffProjectile = null; public SoundDef cookOffSound = null; public SoundDef cookOffTailSound = null; diff --git a/Source/CombatExtended/CombatExtended/Defs/AmmoLink.cs b/Source/CombatExtended/CombatExtended/Defs/AmmoLink.cs index d4fb57ee19..78cd5e1cb0 100644 --- a/Source/CombatExtended/CombatExtended/Defs/AmmoLink.cs +++ b/Source/CombatExtended/CombatExtended/Defs/AmmoLink.cs @@ -13,6 +13,7 @@ public class AmmoLink { public AmmoDef ammo; public ThingDef projectile; + public int amount = 1; public AmmoLink() { } @@ -31,16 +32,21 @@ public void LoadDataFromXmlCustom(XmlNode xmlRoot) } DirectXmlCrossRefLoader.RegisterObjectWantsCrossRef(this, "ammo", xmlRoot.Name); DirectXmlCrossRefLoader.RegisterObjectWantsCrossRef(this, "projectile", (string)ParseHelper.FromString(xmlRoot.FirstChild.Value, typeof(string))); + if (xmlRoot.Attributes["Amount"] != null) + amount = (int)ParseHelper.FromString(xmlRoot.Attributes["Amount"].Value, typeof(int)); } public override string ToString() { - return string.Concat("(", ammo == null ? "null" : ammo.defName, " -> ", projectile == null ? "null" : projectile.defName, ")"); + return "(" + + (ammo == null ? "null" : ammo.defName) + + (amount > 1 ? "x" + amount + " -> " : " -> ") + + (projectile == null ? "null" : projectile.defName + ")"); } public override int GetHashCode() { - return ammo.shortHash + projectile.shortHash << 16; + return ammo.shortHash + projectile.shortHash + amount << 16; } } } diff --git a/Source/CombatExtended/CombatExtended/Defs/AmmoSetDef.cs b/Source/CombatExtended/CombatExtended/Defs/AmmoSetDef.cs index ede696a569..c9e9aca8a5 100644 --- a/Source/CombatExtended/CombatExtended/Defs/AmmoSetDef.cs +++ b/Source/CombatExtended/CombatExtended/Defs/AmmoSetDef.cs @@ -11,5 +11,21 @@ namespace CombatExtended public class AmmoSetDef : Def { public List ammoTypes; + + public override IEnumerable ConfigErrors() + { + foreach (string str in base.ConfigErrors()) + { + yield return str; + } + + var multiplesWithoutSingles = + ammoTypes.Where(x => x.amount > 1).Select(x => x.ammo) + .Except(ammoTypes.Where(x => x.amount == 1).Select(x => x.ammo)); + + if (multiplesWithoutSingles.Any()) + yield return "has multiple ammoDefs with Amount > 1, without a fallback of Amount = 1; namely " + + String.Join(",", multiplesWithoutSingles.Select(x => x.defName).ToArray()); + } } } diff --git a/Source/CombatExtended/CombatExtended/StatParts/StatPart_LoadedAmmo.cs b/Source/CombatExtended/CombatExtended/StatParts/StatPart_LoadedAmmo.cs index 47902b3110..268f9f9b63 100644 --- a/Source/CombatExtended/CombatExtended/StatParts/StatPart_LoadedAmmo.cs +++ b/Source/CombatExtended/CombatExtended/StatParts/StatPart_LoadedAmmo.cs @@ -1,12 +1,18 @@ using System; using System.Collections.Generic; -using Verse; +using System.Text; using RimWorld; +using Verse; +using UnityEngine; namespace CombatExtended { public class StatPart_LoadedAmmo : StatPart { + float cartridges = 0f; + float spentCartridges = 0f; + float magazine = 0f; + public override void TransformValue(StatRequest req, ref float val) { if (TryGetValue(req, out float num)) @@ -15,25 +21,52 @@ public override void TransformValue(StatRequest req, ref float val) public override string ExplanationPart(StatRequest req) { - return TryGetValue(req, out float num) - ? "CE_StatsReport_LoadedAmmo".Translate() + ": " + parentStat.ValueToString(num) - : null; + StringBuilder stringBuilder = new StringBuilder(); + + if (TryGetValue(req, out float _)) + { + if (cartridges != 0f) + stringBuilder.AppendLine("CE_StatsReport_LoadedAmmo".Translate() + ": " + parentStat.ValueToString(cartridges)); + + if (spentCartridges != 0f) + stringBuilder.AppendLine("CE_StatsReport_SpentAmmo".Translate() + ": " + parentStat.ValueToString(spentCartridges)); + + if (magazine != 0f) + stringBuilder.AppendLine("CE_MagazineBulk".Translate() + ": " + parentStat.ValueToString(magazine)); + + return stringBuilder.ToString().TrimEndNewlines(); + } + + return null; } public bool TryGetValue(StatRequest req, out float num) { - num = 0f; + cartridges = 0f; + spentCartridges = 0f; + magazine = 0f; + if (req.HasThing) { var ammoUser = req.Thing.TryGetComp(); if (ammoUser != null && ammoUser.CurrentAmmo != null) { - num = ammoUser.CurrentAmmo.GetStatValueAbstract(parentStat) * ammoUser.CurMagCount; + var numSingle = ammoUser.CurrentAmmo.GetStatValueAbstract(parentStat); + + cartridges = numSingle * ammoUser.CurMagCount; + + if (Controller.settings.EnableAmmoSystem && parentStat == StatDefOf.Mass) + spentCartridges = ammoUser.SpentRounds * numSingle * ammoUser.CurrentAmmo.conservedMassFactorWhenFired; + else if (parentStat == CE_StatDefOf.Bulk) + { + cartridges *= ammoUser.Props.loadedAmmoBulkFactor; - if (parentStat == CE_StatDefOf.Bulk) - num *= ammoUser.Props.loadedAmmoBulkFactor; + if (ammoUser.HasMagazine && ammoUser.CurMagCount > 0) + magazine = ammoUser.Props.magazineBulk; + } } } + num = cartridges + spentCartridges + magazine; return num != 0f; } } diff --git a/Source/CombatExtended/CombatExtended/StatWorkers/StatWorker_AmmoConsumedPerShotCount.cs b/Source/CombatExtended/CombatExtended/StatWorkers/StatWorker_AmmoConsumedPerShotCount.cs deleted file mode 100644 index 8f6d3b07bc..0000000000 --- a/Source/CombatExtended/CombatExtended/StatWorkers/StatWorker_AmmoConsumedPerShotCount.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using RimWorld; -using Verse; - -namespace CombatExtended -{ - public class StatWorker_AmmoConsumedPerShotCount : StatWorker - { - private ThingDef GunDef(StatRequest req) - { - var def = req.Def as ThingDef; - - if (def?.building?.IsTurret ?? false) - def = def.building.turretGunDef; - - return def; - } - - private Thing Gun(StatRequest req) - { - return (req.Thing as Building_TurretGunCE)?.Gun ?? req.Thing; - } - - public override bool ShouldShowFor(StatRequest req) - { - return base.ShouldShowFor(req) && (GunDef(req)?.Verbs?.Any(x => Convert.ToBoolean(((VerbPropertiesCE)x).ammoConsumedPerShotCount > 1)) ?? false); - } - - public override float GetValueUnfinalized(StatRequest req, bool applyPostProcess = true) - { - return ((VerbPropertiesCE)GunDef(req)?.Verbs?.First(x => ((VerbPropertiesCE)x).ammoConsumedPerShotCount > 1)).ammoConsumedPerShotCount; - } - - public override string GetExplanationUnfinalized(StatRequest req, ToStringNumberSense numberSense) - { - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.AppendLine(""); - return stringBuilder.ToString().TrimEndNewlines(); - } - } -} diff --git a/Source/CombatExtended/CombatExtended/Verbs/VerbPropertiesCE.cs b/Source/CombatExtended/CombatExtended/Verbs/VerbPropertiesCE.cs index f17884206a..cb01d99b0e 100644 --- a/Source/CombatExtended/CombatExtended/Verbs/VerbPropertiesCE.cs +++ b/Source/CombatExtended/CombatExtended/Verbs/VerbPropertiesCE.cs @@ -11,7 +11,6 @@ namespace CombatExtended public class VerbPropertiesCE : VerbProperties { public RecoilPattern recoilPattern = RecoilPattern.None; - public int ammoConsumedPerShotCount = 1; public float recoilAmount = 0; public float indirectFirePenalty = 0; public float circularError = 0; diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootCE.cs index ee627b51af..92fb0b8b5f 100644 --- a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootCE.cs +++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootCE.cs @@ -179,7 +179,7 @@ protected override bool TryCastShot() //Reduce ammunition if (CompAmmo != null) { - if (!CompAmmo.TryReduceAmmoCount(VerbPropsCE.ammoConsumedPerShotCount)) + if (!CompAmmo.TryReduceAmmoCount()) { return false; } @@ -192,7 +192,8 @@ protected override bool TryCastShot() ShooterPawn.records.Increment(RecordDefOf.ShotsFired); } //Drop casings - if (VerbPropsCE.ejectsCasings && projectilePropsCE.dropsCasings) + if (VerbPropsCE.ejectsCasings && projectilePropsCE.dropsCasings + && (CompAmmo?.CurrentAmmo?.conservedMassFactorWhenFired > 0f)) { CE_Utility.ThrowEmptyCasing(caster.DrawPos, caster.Map, ThingDef.Named(projectilePropsCE.casingMoteDefname)); }