using UnityEngine; namespace ProjectM.Client { /// /// Live-tunable knobs for the client-only COMBAT-FEEL slice (Stage E). A static bridge — mirrors /// — so values can be poked at runtime via MCP execute_code /// (e.g. ProjectM.Client.FeelConfig.HitShakeLocal = 0.4f;) WITHOUT a recompile, for interactive /// tuning. Read ONLY by client-presentation systems (, /// ) and the MonoBehaviour — all non-Burst, /// main-thread. NEVER read these from a [BurstCompile] system (managed-static + Color/enum-in-Burst /// hazards); they are presentation-only and never touch the deterministic simulation. /// /// Defaults match the values previously hardcoded in CombatFeedbackSystem so behaviour is byte-identical /// until a knob is poked. re-stamps every field on play-enter via /// [RuntimeInitializeOnLoadMethod(SubsystemRegistration)] because statics survive fast-enter-playmode /// domain reloads — without it a poked value would leak across play-enters and flash stale feel (the exact /// bug 's reset prevents). /// /// public static class FeelConfig { // ---- Feature 1: hit camera punch + (camera-only) hit-stop ---- /// Camera shake when the LOCAL player is hit (fed to PrototypeCameraRig.AddShake, clamp 0.8). public static float HitShakeLocal; /// Camera shake when a remote player / Husk is hit. public static float HitShakeRemote; /// Hit-spark particle burst count (procedural fallback path). public static int HitBurstCount; /// Hit SFX volume. public static float HitSfxVolume; /// Degrees of transient FOV "kick" on a LOCAL hit — the netcode-safe hit-stop (NEVER Time.timeScale). 0 = off. public static float HitStopFovKick; /// Milliseconds the FOV kick eases back to base. public static float HitStopDurationMs; // ---- MC-3: player-dealt-hit punch (magnitude-scaled) + deferred enemy flash ---- /// Min FOV kick (deg) when the LOCAL player lands a hit on an enemy (chip damage). public static float HitStopFovKickMin; /// Max FOV kick (deg) on a heavy player-dealt hit (delta >= HitStopRefDamage). public static float HitStopFovKickMax; /// Reserved cap for the (deferred) true freeze-frame, in fixed frames. public static int HitStopMaxFrames; /// Damage delta that saturates the player-dealt punch to HitStopFovKickMax. public static float HitStopRefDamage; /// Tint for the (deferred) enemy material hit-flash — exposed now, wired in the ShaderGraph slice. public static Color HitFlashColor; /// Duration (ms) of the deferred enemy hit-flash. public static float HitFlashDurationMs; /// Master gate for the (deferred) true freeze-frame hit-stop. FALSE for v1 (camera-punch only). public static bool HitStopFreezeEnabled; // ---- Feature 1/2: death camera punch ---- /// Camera shake on LOCAL player death (loudest event by design). public static float PlayerDeathShake; /// Camera shake on a remote player's death. public static float RemotePlayerDeathShake; /// Base death-burst particle count (player death + Husk-death base). public static int DeathBurstCount; // ---- Feature 2: kill-shot fanfare (Husk death) ---- /// Camera shake on a Husk kill (nudged above a glancing hit, kept under PlayerDeathShake). public static float KillShake; /// Multiplier on DeathBurstCount for a Husk kill (result clamped by MaxActiveVfx). public static float KillBurstScale; /// Optional FOV kick on a kill (degrees). 0 = off. public static float KillFovKick; /// Husk-death SFX volume. public static float KillSfxVolume; // ---- Feature 3: respawn shimmer / fade-in (local player recovery) ---- /// Master gate for the local-player respawn shimmer. public static bool RespawnShimmerEnabled; /// Particle burst count for the recovery shimmer. public static int RespawnShimmerBurst; /// Light camera punch on recovery so respawn reads as "reinforcing". public static float RespawnShimmerShake; // ---- Feature 4: reticle lock-on tether (cosmetic aim HINT) ---- /// Master gate for the lock-on tether. public static bool LockOnEnabled; /// Show the tether only on the Gamepad scheme (mirrors the server's gamepad-only auto-target assist). public static bool LockOnGamepadOnly; /// Max world distance from the player to a tethered Husk. public static float LockOnRange; /// Forward half-arc (degrees) around PlayerFacing within which a Husk is eligible. public static float LockOnArcDegrees; /// Tether line tint (subtle highlight, not a laser). public static Color LockOnLineColor; /// Tether line width (world units). public static float LockOnLineWidth; // ---- Feature B: enemy health bars (Slice 1) ---- /// Squared world-distance beyond which a bar is hidden when the pool cap is reached (default 400 = 20m). public static float HealthBarMaxDistSq; // ---- Feature 5 (MC-1): dash juice ---- /// Camera shake on the local player's dash start. public static float DashShake; /// Transient FOV punch (degrees) on dash start — the "lurch" read (camera punch, never Time.timeScale). public static float DashFovKick; /// Afterimage/whoosh particle burst count at dash start. public static int DashBurstCount; /// Dash whoosh SFX volume. public static float DashSfxVolume; /// Particles emitted per frame while the local i-frame window is active (the shimmer trail). public static int DashShimmerPerFrame; /// Suppress local hit-feedback during the local i-frame window (masks the prediction-reconciliation /// Health flicker on a clean dodge — the documented acceptable-not-a-bug interaction). public static bool DashHitSuppress; // ---- Combat feel pass (2026-06): connect cue, hit-flash, kill pop, footsteps, rumble, telegraph beep ---- /// Hit-flash puff density (a colored particle burst in HitFlashColor on an enemy damage edge). public static int HitFlashBurstCount; /// Extra FOV punch (deg) when the LOCAL melee cleave is confirmed to connect (vs a whiff). public static float MeleeConnectFovKick; /// Volume of the meaty melee connect "thunk". public static float MeleeConnectVolume; /// Kill-pop colored flash density on an enemy death. public static int KillFlashBurstCount; /// Soft footstep SFX volume. public static float FootstepVolume; /// Seconds between footsteps while the local player is moving. public static float FootstepIntervalSec; /// Local player speed (u/s) above which footsteps play. public static float FootstepMinSpeed; /// Master gate for gamepad rumble (no-op on KBM). public static bool RumbleEnabled; /// Rumble strength on a local hit taken / dealt. public static float RumbleHit; /// Rumble strength on a kill. public static float RumbleKill; /// Rumble strength on dash / local death (the heaviest). public static float RumbleHeavy; /// Seconds a rumble pulse lasts before it auto-stops. public static float RumbleDurationSec; // ---- Deferred-items pass (2026-06): true body hit-flash, remote co-op swings, near-impact strike beep ---- /// Master gate for the enemy material BODY hit-flash (drives URPMaterialPropertyBaseColor on render children). public static bool BodyFlashEnabled; /// Peak _BaseColor the enemy body flashes to on a hit (HDR; lerps from the baked white base and decays back). public static Color BodyFlashColor; /// Seconds the body flash decays from peak back to the baked white base. public static float BodyFlashDurationSec; /// Master gate for rendering REMOTE teammates' melee cleave arcs (co-op readability). public static bool RemoteSwingEnabled; /// Tint of a remote teammate's slash arc (cooler/friendlier than the local warm arc). public static Color RemoteSlashColor; /// Master gate for the near-impact \"dodge NOW\" strike beep on a winding-up enemy. public static bool StrikeBeepEnabled; /// Volume of the near-impact strike beep. public static float StrikeBeepVolume; /// Ticks before the strike lands that the beep fires (the dodge-reaction lead). public static int StrikeBeepLeadTicks; /// Squared world-distance from the local player beyond which the strike beep is suppressed (avoids a distant cacophony). public static float StrikeBeepMaxDistSq; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] public static void ResetDefaults() { // Feature 1 HitShakeLocal = 0.32f; HitShakeRemote = 0.10f; HitBurstCount = 10; HitSfxVolume = 0.70f; HitStopFovKick = 1.5f; HitStopDurationMs = 90f; HitStopFovKickMin = 0.6f; HitStopFovKickMax = 2.2f; HitStopMaxFrames = 3; HitStopRefDamage = 30f; HitFlashColor = new Color(1f, 0.85f, 0.55f, 1f); HitFlashDurationMs = 80f; HitStopFreezeEnabled = false; // Feature 1/2 death PlayerDeathShake = 0.50f; RemotePlayerDeathShake = 0.25f; DeathBurstCount = 28; // Feature 2 kill-shot KillShake = 0.20f; KillBurstScale = 1.5f; KillFovKick = 1.0f; KillSfxVolume = 0.75f; // Feature 3 respawn RespawnShimmerEnabled = true; RespawnShimmerBurst = 24; RespawnShimmerShake = 0.12f; // Feature 4 tether LockOnEnabled = true; LockOnGamepadOnly = true; LockOnRange = 9.0f; LockOnArcDegrees = 60f; LockOnLineColor = new Color(0.55f, 0.9f, 1f, 0.35f); LockOnLineWidth = 0.05f; // Feature B health bars (Slice 1) HealthBarMaxDistSq = 400f; // 20 m radius // Feature 5 dash (MC-1) DashShake = 0.18f; DashFovKick = 1.2f; DashBurstCount = 14; DashSfxVolume = 0.55f; DashShimmerPerFrame = 2; DashHitSuppress = true; // Combat feel pass (2026-06) HitFlashBurstCount = 16; MeleeConnectFovKick = 0.8f; MeleeConnectVolume = 0.55f; KillFlashBurstCount = 20; FootstepVolume = 0.16f; FootstepIntervalSec = 0.32f; FootstepMinSpeed = 1.5f; RumbleEnabled = true; RumbleHit = 0.25f; RumbleKill = 0.45f; RumbleHeavy = 0.6f; RumbleDurationSec = 0.12f; // Deferred-items pass (2026-06) BodyFlashEnabled = true; BodyFlashColor = new Color(3.2f, 2.8f, 2.2f, 1f); // hot near-white overdrive (multiplies the Synty atlas base map) BodyFlashDurationSec = 0.16f; RemoteSwingEnabled = true; RemoteSlashColor = new Color(1.4f, 2.2f, 2.8f, 1f); // cool teammate arc StrikeBeepEnabled = true; StrikeBeepVolume = 0.40f; StrikeBeepLeadTicks = 8; StrikeBeepMaxDistSq = 225f; // 15 m } } }