Mount authored gmCombatUI, share one press/hold/release request state machine across DAT buttons and keybindings, and recover the exact 1.0s/0.8s power timing from matching retail x86. The same timer fixes jump charge, while ready-stance, response queueing, auto-repeat, layout binding, migration, and conformance coverage keep behavior architectural rather than panel-local. Co-Authored-By: Codex <codex@openai.com>
125 lines
4.1 KiB
C#
125 lines
4.1 KiB
C#
using AcDream.Core.Combat;
|
|
using AcDream.Core.Items;
|
|
|
|
namespace AcDream.Core.Tests.Combat;
|
|
|
|
public sealed class CombatInputPlannerTests
|
|
{
|
|
[Fact]
|
|
public void GetDefaultCombatMode_MissileCombatUseSelectsMissile()
|
|
{
|
|
var bow = Equipped(EquipMask.MissileWeapon, ItemType.MissileWeapon, combatUse: 2);
|
|
|
|
Assert.Equal(
|
|
CombatMode.Missile,
|
|
CombatInputPlanner.GetDefaultCombatMode([bow]));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetDefaultCombatMode_PrimaryWeaponOrderMatchesRetailInventoryPlacement()
|
|
{
|
|
var bow = Equipped(EquipMask.MissileWeapon, ItemType.MissileWeapon, combatUse: 2);
|
|
var sword = Equipped(EquipMask.MeleeWeapon, ItemType.MeleeWeapon, combatUse: 1);
|
|
|
|
Assert.Equal(
|
|
CombatMode.Missile,
|
|
CombatInputPlanner.GetDefaultCombatMode([bow, sword]));
|
|
Assert.Equal(
|
|
CombatMode.Melee,
|
|
CombatInputPlanner.GetDefaultCombatMode([sword, bow]));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetDefaultCombatMode_HeldCasterSelectsMagic()
|
|
{
|
|
var wand = Equipped(EquipMask.Held, ItemType.Caster, combatUse: 0);
|
|
|
|
Assert.Equal(
|
|
CombatMode.Magic,
|
|
CombatInputPlanner.GetDefaultCombatMode([wand]));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetDefaultCombatMode_HeldNonCasterCannotEnterCombat()
|
|
{
|
|
var held = Equipped(EquipMask.Held, ItemType.Misc, combatUse: 0);
|
|
|
|
Assert.Equal(
|
|
CombatMode.NonCombat,
|
|
CombatInputPlanner.GetDefaultCombatMode([held]));
|
|
}
|
|
|
|
[Fact]
|
|
public void GetDefaultCombatMode_NoWeaponDefaultsToUnarmedMelee()
|
|
{
|
|
Assert.Equal(
|
|
CombatMode.Melee,
|
|
CombatInputPlanner.GetDefaultCombatMode([]));
|
|
}
|
|
|
|
[Fact]
|
|
public void ToggleMode_FromNonCombat_UsesDefaultCombatMode()
|
|
{
|
|
Assert.Equal(CombatMode.Melee, CombatInputPlanner.ToggleMode(CombatMode.NonCombat));
|
|
Assert.Equal(
|
|
CombatMode.Missile,
|
|
CombatInputPlanner.ToggleMode(CombatMode.NonCombat, CombatMode.Missile));
|
|
}
|
|
|
|
[Fact]
|
|
public void ToggleMode_FromCombat_ReturnsNonCombat()
|
|
{
|
|
Assert.Equal(CombatMode.NonCombat, CombatInputPlanner.ToggleMode(CombatMode.Melee));
|
|
Assert.Equal(CombatMode.NonCombat, CombatInputPlanner.ToggleMode(CombatMode.Magic));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(CombatAttackAction.Low, AttackHeight.Low)]
|
|
[InlineData(CombatAttackAction.Medium, AttackHeight.Medium)]
|
|
[InlineData(CombatAttackAction.High, AttackHeight.High)]
|
|
public void HeightFor_MapsRetailAttackKeys(CombatAttackAction action, AttackHeight expected)
|
|
{
|
|
Assert.Equal(expected, CombatInputPlanner.HeightFor(action));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(CombatMode.Melee, true)]
|
|
[InlineData(CombatMode.Missile, true)]
|
|
[InlineData(CombatMode.NonCombat, false)]
|
|
[InlineData(CombatMode.Magic, false)]
|
|
public void SupportsTargetedAttack_MatchesRetailExecuteAttackModes(
|
|
CombatMode mode,
|
|
bool expected)
|
|
{
|
|
Assert.Equal(expected, CombatInputPlanner.SupportsTargetedAttack(mode));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(CombatMode.Melee, 0x8000003Du, 0u, true)]
|
|
[InlineData(CombatMode.Missile, 0x8000003Fu, CombatInputPlanner.ReadyForwardCommand, true)]
|
|
[InlineData(CombatMode.Missile, 0x80000041u, CombatInputPlanner.ReadyForwardCommand, true)]
|
|
[InlineData(CombatMode.Missile, 0x8000003Du, CombatInputPlanner.ReadyForwardCommand, false)]
|
|
[InlineData(CombatMode.Missile, 0x8000003Fu, 0x41000006u, false)]
|
|
[InlineData(CombatMode.NonCombat, 0x8000003Du, CombatInputPlanner.ReadyForwardCommand, false)]
|
|
public void PlayerInReadyPositionForAttack_MatchesRetailAttackBranch(
|
|
CombatMode mode,
|
|
uint style,
|
|
uint forward,
|
|
bool expected)
|
|
{
|
|
Assert.Equal(
|
|
expected,
|
|
CombatInputPlanner.PlayerInReadyPositionForAttack(mode, style, forward));
|
|
}
|
|
|
|
private static ClientObject Equipped(
|
|
EquipMask location,
|
|
ItemType type,
|
|
byte combatUse) => new()
|
|
{
|
|
ObjectId = 1,
|
|
CurrentlyEquippedLocation = location,
|
|
Type = type,
|
|
CombatUse = combatUse,
|
|
};
|
|
}
|