diff --git a/Defs/GameSetupSteps/SubsequentShotCurve.xml b/Defs/GameSetupSteps/SubsequentShotCurve.xml new file mode 100644 index 0000000000..d9b6b0ebb4 --- /dev/null +++ b/Defs/GameSetupSteps/SubsequentShotCurve.xml @@ -0,0 +1,20 @@ + + + + + CE_SubsequentShotCurve + 1000 + + + + +
  • (0, 0)
  • +
  • (1.5, 0.60)
  • +
  • (1.9, 0.80)
  • +
  • (13.0, 0.95)
  • +
  • (17.0, 1.0)
  • +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/Languages/English/Keyed/ModMenu.xml b/Languages/English/Keyed/ModMenu.xml index fa154a3ed4..455c2fa741 100644 --- a/Languages/English/Keyed/ModMenu.xml +++ b/Languages/English/Keyed/ModMenu.xml @@ -108,6 +108,9 @@ Controls how many wall fragments will spawn and how big they will be. Fragments from walls intensity + Controls how fast subsequent shot bonuses will accumulate. + Subsequent shot difficulty + When enabled, subsequent shots at the same, or nearby, targets are faster. Faster subsequent shots diff --git a/Source/CombatExtended/CombatExtended/GameSetupSteps/GameSetupStep_SubsequentShotCurve.cs b/Source/CombatExtended/CombatExtended/GameSetupSteps/GameSetupStep_SubsequentShotCurve.cs new file mode 100644 index 0000000000..a8e0f4184e --- /dev/null +++ b/Source/CombatExtended/CombatExtended/GameSetupSteps/GameSetupStep_SubsequentShotCurve.cs @@ -0,0 +1,20 @@ +using Verse; + +namespace CombatExtended; + +public class GameSetupStep_SubsequentShotCurve : GameSetupStep +{ + public SimpleCurve defaultSubsequentShotCurve; + + public override int SeedPart => 58224852; // unused, but required + + public override void GenerateFresh() + { + Verb_ShootCE.SubsequentShotCurve = defaultSubsequentShotCurve; + } + + public override void GenerateFromScribe() + { + Verb_ShootCE.SubsequentShotCurve = defaultSubsequentShotCurve; + } +} diff --git a/Source/CombatExtended/CombatExtended/ModSettings/Settings.cs b/Source/CombatExtended/CombatExtended/ModSettings/Settings.cs index 287a1660c3..999848ad2a 100755 --- a/Source/CombatExtended/CombatExtended/ModSettings/Settings.cs +++ b/Source/CombatExtended/CombatExtended/ModSettings/Settings.cs @@ -41,6 +41,7 @@ public class Settings : ModSettings, ISettingsCE private bool midBurstRetarget = true; private bool fasterRepeatShots = true; + private float fasterRepeatShotsRecoilMult = 1.0f; private float explosionPenMultiplier = 1.0f; private float explosionFalloffFactor = 1.0f; @@ -121,6 +122,7 @@ public class Settings : ModSettings, ISettingsCE private bool debugDisplayCellCoverRating = false; private bool debugDisplayAttritionInfo = false; private bool debugWorldShellingDamageRandomness = false; + private bool debugSubsequentShotLogging = false; public bool DebuggingMode => debuggingMode; public bool DebugVerbose => debugVerbose; @@ -136,6 +138,7 @@ public class Settings : ModSettings, ISettingsCE public bool DebugDisplayCellCoverRating => debugDisplayCellCoverRating && debuggingMode; public bool DebugDisplayAttritionInfo => debugDisplayAttritionInfo && debuggingMode; public bool DebugWorldShellingDamageRandomness => debugWorldShellingDamageRandomness && debuggingMode; + public bool DebugSubsequentShotLogging => debugSubsequentShotLogging && debuggingMode; #endregion #region Autopatcher @@ -160,6 +163,7 @@ public class Settings : ModSettings, ISettingsCE public float FragmentsFromWallsIntensity => fragmentsFromWallsIntensity; public bool FasterRepeatShots => fasterRepeatShots; + public float FasterRepeatShotsRecoilMult => fasterRepeatShotsRecoilMult; public bool MidBurstRetarget => midBurstRetarget; public float ExplosionPenMultiplier => explosionPenMultiplier; @@ -220,6 +224,7 @@ public override void ExposeData() Scribe_Values.Look(ref debugDisplayCellCoverRating, "debugDisplayCellCoverRating", false); Scribe_Values.Look(ref debugDisplayAttritionInfo, "debugDisplayAttritionInfo", false); Scribe_Values.Look(ref debugWorldShellingDamageRandomness, "debugWorldShellingDamageRandomness", false); + Scribe_Values.Look(ref debugSubsequentShotLogging, "debugSubsequentShotLogging", false); Scribe_Values.Look(ref debugGenClosetPawn, "debugGenClosetPawn", false); Scribe_Values.Look(ref debugVerbose, "debugVerbose", false); Scribe_Values.Look(ref debugMuzzleFlash, "debugMuzzleFlash", false); @@ -256,6 +261,7 @@ public override void ExposeData() Scribe_Values.Look(ref fragmentsFromWallsReflected, "fragmentsFromWallsReflected", false); Scribe_Values.Look(ref fragmentsFromWallsIntensity, "fragmentsFromWallsIntensity", 1.0f); Scribe_Values.Look(ref fasterRepeatShots, "fasterRepeatShots", false); + Scribe_Values.Look(ref fasterRepeatShotsRecoilMult, "fasterRepeatShotsRecoilMult", 1.0f); Scribe_Values.Look(ref midBurstRetarget, "midBurstRetarget", true); Scribe_Values.Look(ref explosionPenMultiplier, "explosionPenMultiplier", 1.0f); Scribe_Values.Look(ref explosionFalloffFactor, "explosionFalloffFactor", 1.0f); @@ -305,7 +311,6 @@ private void DoSettingsWindowContents_Mechanics(Listing_Standard list) left.CheckboxLabeled("CE_Settings_AllowMeleeHunting_Title".Translate(), ref allowMeleeHunting, "CE_Settings_AllowMeleeHunting_Desc".Translate()); left.CheckboxLabeled("CE_Settings_SmokeEffects_Title".Translate(), ref smokeEffects, "CE_Settings_SmokeEffects_Desc".Translate()); left.CheckboxLabeled("CE_Settings_TurretsBreakShields_Title".Translate(), ref turretsBreakShields, "CE_Settings_TurretsBreakShields_Desc".Translate()); - left.CheckboxLabeled("CE_Settings_FasterRepeatShots_Title".Translate(), ref fasterRepeatShots, "CE_Settings_FasterRepeatShots_Desc".Translate()); left.CheckboxLabeled("CE_Settings_MidBurstRetarget_Title".Translate(), ref midBurstRetarget, "CE_Settings_MidBurstRetarget_Desc".Translate()); left.CheckboxLabeled("CE_Settings_EnableArcOfFire_Title".Translate(), ref enableArcOfFire, "CE_Settings_EnableArcOfFire_Desc".Translate()); left.CheckboxLabeled("CE_Settings_EnableCIWS".Translate(), ref enableCIWS, "CE_Settings_EnableCIWS_Desc".Translate()); @@ -333,6 +338,9 @@ private void DoSettingsWindowContents_Mechanics(Listing_Standard list) right.Label("CE_Settings_StabilizationSettings".Translate()); right.Gap(); medicineSearchRadius = right.SliderLabeled("CE_Settings_MedicineSearchRadius_Title".Translate() + ": " + medicineSearchRadius.ToString("F0"), medicineSearchRadius, 1f, 100f, tooltip: "CE_Settings_MedicineSearchRadius_Desc".Translate(), labelPct: 0.6f); + right.GapLine(); + left.CheckboxLabeled("CE_Settings_FasterRepeatShots_Title".Translate(), ref fasterRepeatShots, "CE_Settings_FasterRepeatShots_Desc".Translate()); + fasterRepeatShotsRecoilMult = left.SliderLabeled("CE_Settings_FasterRepeatShotsMultiplier_Title".Translate() + ": " + fasterRepeatShotsRecoilMult.ToString("F1"), fasterRepeatShotsRecoilMult, 0.1f, 3f, tooltip: "CE_Settings_FasterRepeatShotsMultiplier_Desc".Translate(), labelPct: 0.6f); right.End(); } @@ -435,6 +443,7 @@ private void DoSettingsWindowContents_Misc(Listing_Standard list) list.CheckboxLabeled("Display danger buildup within cells", ref debugDisplayDangerBuildup); list.CheckboxLabeled("Display cover rating of cells of suppressed pawns", ref debugDisplayCellCoverRating); list.CheckboxLabeled("Disable randomness in world shelling", ref debugWorldShellingDamageRandomness); + list.CheckboxLabeled("Log subsequent shot calculations", ref debugSubsequentShotLogging); } #endif list.Gap(); @@ -499,6 +508,7 @@ private void ResetToDefault_Mechanics() smokeEffects = true; turretsBreakShields = true; fasterRepeatShots = true; + fasterRepeatShotsRecoilMult = 1.0f; midBurstRetarget = true; enableCIWS = true; fragmentsFromWalls = false; @@ -567,6 +577,7 @@ private void ResetToDefault_Misc() debugDisplayDangerBuildup = false; debugDisplayCellCoverRating = false; debugWorldShellingDamageRandomness = false; + debugSubsequentShotLogging = false; #endif } #endregion diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs index 94cc616319..9f99f8abda 100644 --- a/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs +++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_LaunchProjectileCE.cs @@ -620,7 +620,7 @@ private void GetRecoilVec(ref float rotation, ref float angle) rotation += recoilMagnitude * Rand.Range(minX, maxX); var trd = Rand.Range(minY, maxY); angle += recoilMagnitude * Mathf.Deg2Rad * trd; - lastRecoilDeg += nextRecoilMagnitude * trd; + lastRecoilDeg += nextRecoilMagnitude * recoil; //always store non-randomized max recoil for subsequent shot purposes } /// @@ -1083,7 +1083,7 @@ public override bool TryCastShot() didRetarget = Retarget(); repeating = true; doRetarget = true; - storedShotReduction = null; + var props = VerbPropsCE; var midBurst = numShotsFired > 0; var suppressing = CompFireModes?.CurrentAimMode == AimMode.SuppressFire; diff --git a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootCE.cs b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootCE.cs index c964e2486c..650476f968 100755 --- a/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootCE.cs +++ b/Source/CombatExtended/CombatExtended/Verbs/Verb_ShootCE.cs @@ -30,6 +30,8 @@ public class Verb_ShootCE : Verb_LaunchProjectileCE public Vector3 drawPos; + internal static SimpleCurve SubsequentShotCurve = new SimpleCurve(); // Populated on game start from a SetupStepDef + #endregion #region Properties @@ -327,13 +329,28 @@ public override void RecalculateWarmupTicks() var d = v - u; var newShotRotation = (-90 + Mathf.Rad2Deg * Mathf.Atan2(d.z, d.x)) % 360; - var delta = Mathf.Abs(newShotRotation - lastShotRotation) + lastRecoilDeg; + var delta = Mathf.Abs(newShotRotation - lastShotRotation); + + //max possible reduction for this weapon + float maxReduction = CompFireModes?.CurrentAimMode == AimMode.SuppressFire ? 0.1f : _isAiming ? 0.5f : 0.25f; + maxReduction += RecoilAmount * RecoilAmount * 0.1f; + + //current reduction after pawn stats + float reduction = (delta / 45f) + (SubsequentShotCurve.Evaluate(lastRecoilDeg) * Controller.settings.FasterRepeatShotsRecoilMult); + + if (storedShotReduction != null) + { + reduction *= (float)storedShotReduction; + } + + reduction = Mathf.Max(maxReduction, reduction); + if (Controller.settings.DebugSubsequentShotLogging) + { + Log.Message($"{caster?.LabelShort} ({EquipmentSource?.LabelShort}) subsequent shot:\nreduction {reduction}; storedShotReduction {storedShotReduction}; maxReduction {maxReduction}; lastRecoilDeg {lastRecoilDeg}; delta {delta}; delta/45 {delta / 45f}"); + } + lastRecoilDeg = 0; - var maxReduction = storedShotReduction ?? (CompFireModes?.CurrentAimMode == AimMode.SuppressFire ? - 0.1f : - (_isAiming ? 0.5f : 0.25f)); - var reduction = Mathf.Max(maxReduction, delta / 45f); - storedShotReduction = reduction; + storedShotReduction = Mathf.Clamp01(reduction); if (reduction < 1.0f) {