Data-driven tuning bench: feel profiles (JSON gestalts + overlay A/B), asset-derived timing audit, server-measured combat probe

FeelProfileService: partial-apply profiles ride the authoritative SetTuning
path + FeelConfig reflection; Save-current captures hand-dialed state; 3
starter profiles (baseline-0720 / heavy-committed / snappier, DPS-held).
TuningAuditTools: contact truth derived from clip WindUp takes / state
speeds vs MeleeTiming (drift detector), coupling invariants, cadence/DPS,
reach-honesty ratio off the baked weapon mesh (now x0.93/x1.16, was >2x).
CombatProbe: dummy + injected mash chain, per-swing server-measured
start/contact/damage + cadence/DPS report. Verified live: snappier profile
apply -> measured 24/24/36 gaps, +12/+8/+15 contacts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 21:28:33 -07:00
parent 2b742f0179
commit 730bad3d74
15 changed files with 520 additions and 0 deletions
@@ -117,6 +117,20 @@ namespace ProjectM.Client
TuningRow("Cast turn deg", TuningKnob.CastTurnRateDeg, 60f, "0");
TuningRow("Move sharp", TuningKnob.MoveSharpness, 0.5f, "0.0");
// 07-20 data-driven tuning: whole-gestalt FEEL PROFILES (sim knobs + FeelConfig in one JSON) --
// A/B a feel with one click instead of dialing 30 scalars; Save captures the hand-dialed state.
GUILayout.Space(6);
GUILayout.Label("- Feel Profiles -");
foreach (var prof in FeelProfileService.ListProfiles())
if (GUILayout.Button(System.IO.Path.GetFileNameWithoutExtension(prof)))
FeelProfileService.Apply(prof);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Save current")) FeelProfileService.SaveCurrent();
if (GUILayout.Button("Rescan")) FeelProfileService.Rescan();
GUILayout.EndHorizontal();
if (GUILayout.Button(CombatProbe.Running ? "Probe running..." : "Combat probe (6 swings)") && !CombatProbe.Running)
CombatProbe.Run(6); // measured cadence/contact/DPS -> console
}