feat: port retail magic lifecycle and retained spell UI
Complete the retail cast-intent, target, component, enchantment, and busy-state paths; mount the DAT-authored spell bar, spellbook, component book, effects panels, and shared panel lifecycle; and add scoped input plus conformance coverage. Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
parent
7b7ffcd278
commit
07be994d97
84 changed files with 17822 additions and 1051 deletions
|
|
@ -26,7 +26,7 @@ namespace AcDream.UI.Abstractions.Input;
|
|||
/// </summary>
|
||||
public sealed class KeyBindings
|
||||
{
|
||||
private const int CurrentSchemaVersion = 3;
|
||||
private const int CurrentSchemaVersion = 4;
|
||||
|
||||
private readonly List<Binding> _bindings = new();
|
||||
|
||||
|
|
@ -47,11 +47,22 @@ public sealed class KeyBindings
|
|||
/// activation type matches the requested phase. Null if none.
|
||||
/// </summary>
|
||||
public Binding? Find(KeyChord chord, ActivationType activation)
|
||||
{
|
||||
for (int i = 0; i < _bindings.Count; i++)
|
||||
{
|
||||
Binding binding = _bindings[i];
|
||||
if (binding.Chord == chord && binding.Activation == activation)
|
||||
return binding;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Binding? Find(KeyChord chord, ActivationType activation, InputScope scope)
|
||||
{
|
||||
for (int i = 0; i < _bindings.Count; i++)
|
||||
{
|
||||
var b = _bindings[i];
|
||||
if (b.Chord == chord && b.Activation == activation) return b;
|
||||
if (b.Chord == chord && b.Activation == activation && b.Scope == scope) return b;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -242,37 +253,37 @@ public sealed class KeyBindings
|
|||
// ── Combat (mode-dependent — dormant in K, lights up in Phase L) ──
|
||||
b.Add(new(new KeyChord(Key.GraveAccent, ModifierMask.None), InputAction.CombatToggleCombat));
|
||||
// Melee mode (active when MeleeCombat scope pushed).
|
||||
b.Add(new(new KeyChord(Key.Insert, ModifierMask.None), InputAction.CombatDecreaseAttackPower));
|
||||
b.Add(new(new KeyChord(Key.PageUp, ModifierMask.None), InputAction.CombatIncreaseAttackPower));
|
||||
b.Add(new(new KeyChord(Key.Insert, ModifierMask.None), InputAction.CombatDecreaseAttackPower, Scope: InputScope.MeleeCombat));
|
||||
b.Add(new(new KeyChord(Key.PageUp, ModifierMask.None), InputAction.CombatIncreaseAttackPower, Scope: InputScope.MeleeCombat));
|
||||
// Retail HandleCombatAction consumes key-down AND key-up for attack
|
||||
// height actions: down starts charging, up ends the request. Hold is
|
||||
// our binding representation for that transition pair.
|
||||
b.Add(new(new KeyChord(Key.Delete, ModifierMask.None), InputAction.CombatLowAttack, ActivationType.Hold));
|
||||
b.Add(new(new KeyChord(Key.End, ModifierMask.None), InputAction.CombatMediumAttack, ActivationType.Hold));
|
||||
b.Add(new(new KeyChord(Key.PageDown, ModifierMask.None), InputAction.CombatHighAttack, ActivationType.Hold));
|
||||
b.Add(new(new KeyChord(Key.Delete, ModifierMask.None), InputAction.CombatLowAttack, ActivationType.Hold, InputScope.MeleeCombat));
|
||||
b.Add(new(new KeyChord(Key.End, ModifierMask.None), InputAction.CombatMediumAttack, ActivationType.Hold, InputScope.MeleeCombat));
|
||||
b.Add(new(new KeyChord(Key.PageDown, ModifierMask.None), InputAction.CombatHighAttack, ActivationType.Hold, InputScope.MeleeCombat));
|
||||
// Missile + Magic + Spell-tab — same chords; resolved by scope at
|
||||
// runtime per InputDispatcher's stack lookup. Add the bindings;
|
||||
// subscribers arrive in Phase L when CombatState.CurrentMode is
|
||||
// wired.
|
||||
b.Add(new(new KeyChord(Key.Insert, ModifierMask.None), InputAction.CombatDecreaseMissileAccuracy));
|
||||
b.Add(new(new KeyChord(Key.PageUp, ModifierMask.None), InputAction.CombatIncreaseMissileAccuracy));
|
||||
b.Add(new(new KeyChord(Key.Delete, ModifierMask.None), InputAction.CombatAimLow));
|
||||
b.Add(new(new KeyChord(Key.End, ModifierMask.None), InputAction.CombatAimMedium));
|
||||
b.Add(new(new KeyChord(Key.PageDown, ModifierMask.None), InputAction.CombatAimHigh));
|
||||
b.Add(new(new KeyChord(Key.Insert, ModifierMask.None), InputAction.CombatPrevSpellTab));
|
||||
b.Add(new(new KeyChord(Key.PageUp, ModifierMask.None), InputAction.CombatNextSpellTab));
|
||||
b.Add(new(new KeyChord(Key.Delete, ModifierMask.None), InputAction.CombatPrevSpell));
|
||||
b.Add(new(new KeyChord(Key.End, ModifierMask.None), InputAction.CombatCastCurrentSpell));
|
||||
b.Add(new(new KeyChord(Key.PageDown, ModifierMask.None), InputAction.CombatNextSpell));
|
||||
b.Add(new(new KeyChord(Key.Insert, ModifierMask.Ctrl), InputAction.CombatFirstSpellTab));
|
||||
b.Add(new(new KeyChord(Key.PageUp, ModifierMask.Ctrl), InputAction.CombatLastSpellTab));
|
||||
b.Add(new(new KeyChord(Key.Delete, ModifierMask.Ctrl), InputAction.CombatFirstSpell));
|
||||
b.Add(new(new KeyChord(Key.PageDown, ModifierMask.Ctrl), InputAction.CombatLastSpell));
|
||||
b.Add(new(new KeyChord(Key.Insert, ModifierMask.None), InputAction.CombatDecreaseMissileAccuracy, Scope: InputScope.MissileCombat));
|
||||
b.Add(new(new KeyChord(Key.PageUp, ModifierMask.None), InputAction.CombatIncreaseMissileAccuracy, Scope: InputScope.MissileCombat));
|
||||
b.Add(new(new KeyChord(Key.Delete, ModifierMask.None), InputAction.CombatAimLow, Scope: InputScope.MissileCombat));
|
||||
b.Add(new(new KeyChord(Key.End, ModifierMask.None), InputAction.CombatAimMedium, Scope: InputScope.MissileCombat));
|
||||
b.Add(new(new KeyChord(Key.PageDown, ModifierMask.None), InputAction.CombatAimHigh, Scope: InputScope.MissileCombat));
|
||||
b.Add(new(new KeyChord(Key.Insert, ModifierMask.None), InputAction.CombatPrevSpellTab, Scope: InputScope.MagicCombat));
|
||||
b.Add(new(new KeyChord(Key.PageUp, ModifierMask.None), InputAction.CombatNextSpellTab, Scope: InputScope.MagicCombat));
|
||||
b.Add(new(new KeyChord(Key.Delete, ModifierMask.None), InputAction.CombatPrevSpell, Scope: InputScope.MagicCombat));
|
||||
b.Add(new(new KeyChord(Key.End, ModifierMask.None), InputAction.CombatCastCurrentSpell, Scope: InputScope.MagicCombat));
|
||||
b.Add(new(new KeyChord(Key.PageDown, ModifierMask.None), InputAction.CombatNextSpell, Scope: InputScope.MagicCombat));
|
||||
b.Add(new(new KeyChord(Key.Insert, ModifierMask.Ctrl), InputAction.CombatFirstSpellTab, Scope: InputScope.MagicCombat));
|
||||
b.Add(new(new KeyChord(Key.PageUp, ModifierMask.Ctrl), InputAction.CombatLastSpellTab, Scope: InputScope.MagicCombat));
|
||||
b.Add(new(new KeyChord(Key.Delete, ModifierMask.Ctrl), InputAction.CombatFirstSpell, Scope: InputScope.MagicCombat));
|
||||
b.Add(new(new KeyChord(Key.PageDown, ModifierMask.Ctrl), InputAction.CombatLastSpell, Scope: InputScope.MagicCombat));
|
||||
for (int i = 1; i <= 9; i++)
|
||||
{
|
||||
var k = (Key)((int)Key.Number0 + i);
|
||||
var action = (InputAction)((int)InputAction.UseSpellSlot_1 + i - 1);
|
||||
b.Add(new(new KeyChord(k, ModifierMask.None), action));
|
||||
b.Add(new(new KeyChord(k, ModifierMask.None), action, Scope: InputScope.MagicCombat));
|
||||
}
|
||||
|
||||
// ── Emotes ──────────────────────────────────────────────
|
||||
|
|
@ -424,7 +435,17 @@ public sealed class KeyBindings
|
|||
var chord = new KeyChord(silkKey, mods, device);
|
||||
action = MigrateLegacyQuickSlotIntent(version, action, chord, activation);
|
||||
activation = MigrateCombatAttackActivation(version, action, activation);
|
||||
loaded.Add(new(chord, action, activation));
|
||||
InputScope scope = defaults.ForAction(action)
|
||||
.Select(binding => binding.Scope)
|
||||
.DefaultIfEmpty(InputScope.Game)
|
||||
.First();
|
||||
if (bindingEl.TryGetProperty("scope", out var scopeEl)
|
||||
&& scopeEl.ValueKind == JsonValueKind.String
|
||||
&& Enum.TryParse(scopeEl.GetString(), out InputScope parsedScope))
|
||||
{
|
||||
scope = parsedScope;
|
||||
}
|
||||
loaded.Add(new(chord, action, activation, scope));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -480,6 +501,8 @@ public sealed class KeyBindings
|
|||
entry["device"] = (int)binding.Chord.Device;
|
||||
if (binding.Activation != ActivationType.Press)
|
||||
entry["activation"] = binding.Activation.ToString();
|
||||
if (binding.Scope != InputScope.Game)
|
||||
entry["scope"] = binding.Scope.ToString();
|
||||
list.Add(entry);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue