diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs index ca34f5bdbe..6bfe61b008 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs @@ -881,7 +881,25 @@ public bool IsDualWieldingRangedWeapons() return false; } - + + public bool IsDualWieldingMeleeWeapons() + { + int meleeItemCount = 0; + foreach (var item in HeldItems) + { + if (item.GetComponent() != null) + { + meleeItemCount++; + } + + if (meleeItemCount > 1) + { + return true; + } + } + + return false; + } private float lowPassMultiplier; public float LowPassMultiplier { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs index 0fa4f918f3..13e3722d4a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/MeleeWeapon.cs @@ -54,8 +54,15 @@ public bool HitOnlyCharacters { get; set; + } + + [Serialize(defaultValue: 1f, IsPropertySaveable.Yes, description: "Penalty multiplier to reload time when dual-wielding.")] + public float DualWieldReloadTimePenaltyMultiplier + { + get; + private set; } - + [Editable, Serialize(true, IsPropertySaveable.No)] public bool Swing { get; set; } @@ -88,11 +95,28 @@ public MeleeWeapon(Item item, ContentXElement element) PreferredContainedItems = element.GetAttributeIdentifierArray("preferredcontaineditems", Array.Empty()).ToImmutableHashSet(); } + /// + /// Lerps between the original penalty and a neutral value, which should be 1 for multipliers and 0 for additive penalties. + /// + /// The character to get stat values from + /// The original penalty value + /// Neutral value to lerp towards. Should be 1 for multipliers and 0 for additives. + /// + private static float ApplyDualWieldPenaltyReduction(Character character, float originalPenalty, float neutralValue) + { + float statAdjustmentPrc = character.GetStatValue(StatTypes.DualWieldingPenaltyReduction); + statAdjustmentPrc = MathHelper.Clamp(statAdjustmentPrc, 0f, 1f); + float reducedPenaltyMultiplier = MathHelper.Lerp(originalPenalty, neutralValue, statAdjustmentPrc); + return reducedPenaltyMultiplier; + } + + public override void Equip(Character character) { base.Equip(character); //force a wait of at least 1 second when equipping the weapon, so you can't "rapid-fire" by swapping between weapons const float forcedDelayOnEquip = 1.0f; + reloadTimer = Math.Max(Math.Min(reload, forcedDelayOnEquip), reloadTimer); IsActive = true; } @@ -117,6 +141,12 @@ public override bool Use(float deltaTime, Character character = null) if (Item.RequireAimToUse && hitPos < MathHelper.PiOver4) { return false; } + + if (character.IsDualWieldingMeleeWeapons()) + { + baseReloadTime *= Math.Max(1f, ApplyDualWieldPenaltyReduction(character, DualWieldReloadTimePenaltyMultiplier, neutralValue: 1f)); + } + ActivateNearbySleepingCharacters(); reloadTimer = reload; reloadTimer /= 1f + character.GetStatValue(StatTypes.MeleeAttackSpeed);