feat(anim): Phase L.1c select combat maneuvers
This commit is contained in:
parent
831392a7b2
commit
646246ba84
6 changed files with 385 additions and 33 deletions
|
|
@ -9,17 +9,20 @@ public sealed class CombatAnimationPlannerTests
|
|||
[Theory]
|
||||
[InlineData(0x10000058u, CombatAnimationKind.MeleeSwing)] // ThrustMed
|
||||
[InlineData(0x1000005Bu, CombatAnimationKind.MeleeSwing)] // SlashHigh
|
||||
[InlineData(0x10000180u, CombatAnimationKind.MeleeSwing)] // OffhandDoubleThrustMed
|
||||
[InlineData(0x1000017Du, CombatAnimationKind.MeleeSwing)] // OffhandDoubleThrustMed
|
||||
[InlineData(0x1000018Eu, CombatAnimationKind.MeleeSwing)] // PunchFastLow
|
||||
[InlineData(0x10000061u, CombatAnimationKind.MissileAttack)] // Shoot
|
||||
[InlineData(0x100000D4u, CombatAnimationKind.MissileAttack)] // Reload
|
||||
[InlineData(0x10000062u, CombatAnimationKind.CreatureAttack)] // AttackHigh1
|
||||
[InlineData(0x1000018Eu, CombatAnimationKind.CreatureAttack)] // AttackLow6
|
||||
[InlineData(0x1000018Bu, CombatAnimationKind.CreatureAttack)] // AttackLow6
|
||||
[InlineData(0x400000D3u, CombatAnimationKind.SpellCast)] // CastSpell
|
||||
[InlineData(0x400000E0u, CombatAnimationKind.SpellCast)] // UseMagicStaff
|
||||
[InlineData(0x10000051u, CombatAnimationKind.HitReaction)] // Twitch1
|
||||
[InlineData(0x10000055u, CombatAnimationKind.HitReaction)] // StaggerBackward
|
||||
[InlineData(0x40000011u, CombatAnimationKind.Death)] // Dead
|
||||
[InlineData(0x8000003Eu, CombatAnimationKind.CombatStance)] // SwordCombat
|
||||
[InlineData(0x80000043u, CombatAnimationKind.CombatStance)] // SlingCombat
|
||||
[InlineData(0x80000044u, CombatAnimationKind.CombatStance)] // 2HandedSwordCombat
|
||||
public void ClassifyMotionCommand_RecognisesRetailCombatCommands(
|
||||
uint command,
|
||||
CombatAnimationKind expected)
|
||||
|
|
@ -27,6 +30,18 @@ public sealed class CombatAnimationPlannerTests
|
|||
Assert.Equal(expected, CombatAnimationPlanner.ClassifyMotionCommand(command));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x0170, 0x10000170u)] // OffhandSlashHigh
|
||||
[InlineData(0x017D, 0x1000017Du)] // OffhandDoubleThrustMed
|
||||
[InlineData(0x018B, 0x1000018Bu)] // AttackLow6
|
||||
[InlineData(0x018E, 0x1000018Eu)] // PunchFastLow
|
||||
public void MotionCommandResolver_UsesNamedRetailLateCombatCommands(
|
||||
ushort wireCommand,
|
||||
uint expectedFullCommand)
|
||||
{
|
||||
Assert.Equal(expectedFullCommand, MotionCommandResolver.ReconstructFullCommand(wireCommand));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlanFromWireCommand_Swing_IsActionOverlay()
|
||||
{
|
||||
|
|
|
|||
155
tests/AcDream.Core.Tests/Combat/CombatManeuverSelectorTests.cs
Normal file
155
tests/AcDream.Core.Tests/Combat/CombatManeuverSelectorTests.cs
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
using AcDream.Core.Combat;
|
||||
using DatReaderWriter.DBObjs;
|
||||
using DatReaderWriter.Types;
|
||||
using DatAttackHeight = DatReaderWriter.Enums.AttackHeight;
|
||||
using DatAttackType = DatReaderWriter.Enums.AttackType;
|
||||
using DatMotionCommand = DatReaderWriter.Enums.MotionCommand;
|
||||
using DatMotionStance = DatReaderWriter.Enums.MotionStance;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Tests.Combat;
|
||||
|
||||
public sealed class CombatManeuverSelectorTests
|
||||
{
|
||||
[Fact]
|
||||
public void SelectMotion_UsesFirstEntryAtOrAboveSubdivision()
|
||||
{
|
||||
var table = MakeTable(
|
||||
Entry(DatMotionStance.SwordCombat, DatAttackHeight.Medium,
|
||||
DatAttackType.Slash, DatMotionCommand.SlashMed),
|
||||
Entry(DatMotionStance.SwordCombat, DatAttackHeight.Medium,
|
||||
DatAttackType.Slash, DatMotionCommand.BackhandMed));
|
||||
|
||||
var atThreshold = CombatManeuverSelector.SelectMotion(
|
||||
table,
|
||||
DatMotionStance.SwordCombat,
|
||||
DatAttackHeight.Medium,
|
||||
DatAttackType.Slash,
|
||||
powerLevel: CombatManeuverSelector.DefaultSubdivision);
|
||||
|
||||
var highPower = CombatManeuverSelector.SelectMotion(
|
||||
table,
|
||||
DatMotionStance.SwordCombat,
|
||||
DatAttackHeight.Medium,
|
||||
DatAttackType.Slash,
|
||||
powerLevel: 1f);
|
||||
|
||||
Assert.Equal(DatMotionCommand.SlashMed, atThreshold.Motion);
|
||||
Assert.Equal(DatMotionCommand.SlashMed, highPower.Motion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectMotion_UsesSecondEntryBelowSubdivision()
|
||||
{
|
||||
var table = MakeTable(
|
||||
Entry(DatMotionStance.SwordCombat, DatAttackHeight.Medium,
|
||||
DatAttackType.Slash, DatMotionCommand.SlashMed),
|
||||
Entry(DatMotionStance.SwordCombat, DatAttackHeight.Medium,
|
||||
DatAttackType.Slash, DatMotionCommand.BackhandMed));
|
||||
|
||||
var selection = CombatManeuverSelector.SelectMotion(
|
||||
table,
|
||||
DatMotionStance.SwordCombat,
|
||||
DatAttackHeight.Medium,
|
||||
DatAttackType.Slash,
|
||||
powerLevel: 0.2f);
|
||||
|
||||
Assert.True(selection.Found);
|
||||
Assert.Equal(DatMotionCommand.BackhandMed, selection.Motion);
|
||||
Assert.Equal(DatAttackType.Slash, selection.EffectiveAttackType);
|
||||
Assert.Equal(2, selection.Candidates.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectMotion_ThrustSlashWeaponUsesTwoThirdsSubdivision()
|
||||
{
|
||||
var table = MakeTable(
|
||||
Entry(DatMotionStance.SwordCombat, DatAttackHeight.High,
|
||||
DatAttackType.Slash, DatMotionCommand.SlashHigh),
|
||||
Entry(DatMotionStance.SwordCombat, DatAttackHeight.High,
|
||||
DatAttackType.Slash, DatMotionCommand.BackhandHigh));
|
||||
|
||||
var normal = CombatManeuverSelector.SelectMotion(
|
||||
table,
|
||||
DatMotionStance.SwordCombat,
|
||||
DatAttackHeight.High,
|
||||
DatAttackType.Slash,
|
||||
powerLevel: 0.5f);
|
||||
|
||||
var thrustSlash = CombatManeuverSelector.SelectMotion(
|
||||
table,
|
||||
DatMotionStance.SwordCombat,
|
||||
DatAttackHeight.High,
|
||||
DatAttackType.Slash,
|
||||
powerLevel: 0.5f,
|
||||
isThrustSlashWeapon: true);
|
||||
|
||||
Assert.Equal(DatMotionCommand.SlashHigh, normal.Motion);
|
||||
Assert.Equal(DatMotionCommand.BackhandHigh, thrustSlash.Motion);
|
||||
Assert.Equal(CombatManeuverSelector.ThrustSlashSubdivision, thrustSlash.Subdivision);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectMotion_MissingLookupReturnsNone()
|
||||
{
|
||||
var table = MakeTable(
|
||||
Entry(DatMotionStance.BowCombat, DatAttackHeight.High,
|
||||
DatAttackType.Punch, DatMotionCommand.Shoot));
|
||||
|
||||
var selection = CombatManeuverSelector.SelectMotion(
|
||||
table,
|
||||
DatMotionStance.SwordCombat,
|
||||
DatAttackHeight.High,
|
||||
DatAttackType.Punch,
|
||||
powerLevel: 0.5f);
|
||||
|
||||
Assert.Equal(CombatManeuverSelection.None, selection);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FindMotions_PreservesRetailTableOrder()
|
||||
{
|
||||
var table = MakeTable(
|
||||
Entry(DatMotionStance.HandCombat, DatAttackHeight.Low,
|
||||
DatAttackType.Kick, DatMotionCommand.AttackLow1),
|
||||
Entry(DatMotionStance.HandCombat, DatAttackHeight.Low,
|
||||
DatAttackType.Kick, (DatMotionCommand)0x1000018Eu),
|
||||
Entry(DatMotionStance.HandCombat, DatAttackHeight.Low,
|
||||
DatAttackType.Punch, DatMotionCommand.AttackLow2));
|
||||
|
||||
var motions = CombatManeuverSelector.FindMotions(
|
||||
table,
|
||||
DatMotionStance.HandCombat,
|
||||
DatAttackHeight.Low,
|
||||
DatAttackType.Kick);
|
||||
|
||||
Assert.Equal(new[]
|
||||
{
|
||||
DatMotionCommand.AttackLow1,
|
||||
(DatMotionCommand)0x1000018Eu,
|
||||
}, motions);
|
||||
}
|
||||
|
||||
private static CombatTable MakeTable(params CombatManeuver[] maneuvers)
|
||||
{
|
||||
var table = new CombatTable();
|
||||
table.CombatManeuvers.AddRange(maneuvers);
|
||||
return table;
|
||||
}
|
||||
|
||||
private static CombatManeuver Entry(
|
||||
DatMotionStance stance,
|
||||
DatAttackHeight height,
|
||||
DatAttackType type,
|
||||
DatMotionCommand motion)
|
||||
{
|
||||
return new CombatManeuver
|
||||
{
|
||||
Style = stance,
|
||||
AttackHeight = height,
|
||||
AttackType = type,
|
||||
MinSkillLevel = 0,
|
||||
Motion = motion,
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue