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
|
|
@ -53,9 +53,12 @@ public static class CombatAnimationPlanner
|
|||
or CombatAnimationMotionCommands.SwordCombat
|
||||
or CombatAnimationMotionCommands.SwordShieldCombat
|
||||
or CombatAnimationMotionCommands.TwoHandedSwordCombat
|
||||
or CombatAnimationMotionCommands.TwoHandedStaffCombat
|
||||
or CombatAnimationMotionCommands.BowCombat
|
||||
or CombatAnimationMotionCommands.CrossbowCombat
|
||||
or CombatAnimationMotionCommands.SlingCombat
|
||||
or CombatAnimationMotionCommands.DualWieldCombat
|
||||
or CombatAnimationMotionCommands.ThrownWeaponCombat
|
||||
or CombatAnimationMotionCommands.AtlatlCombat
|
||||
or CombatAnimationMotionCommands.ThrownShieldCombat
|
||||
or CombatAnimationMotionCommands.Magic => CombatAnimationKind.CombatStance,
|
||||
|
|
@ -99,7 +102,19 @@ public static class CombatAnimationPlanner
|
|||
or CombatAnimationMotionCommands.OffhandTripleThrustLow
|
||||
or CombatAnimationMotionCommands.OffhandTripleThrustMed
|
||||
or CombatAnimationMotionCommands.OffhandTripleThrustHigh
|
||||
or CombatAnimationMotionCommands.OffhandKick => CombatAnimationKind.MeleeSwing,
|
||||
or CombatAnimationMotionCommands.OffhandKick
|
||||
or CombatAnimationMotionCommands.PunchFastHigh
|
||||
or CombatAnimationMotionCommands.PunchFastMed
|
||||
or CombatAnimationMotionCommands.PunchFastLow
|
||||
or CombatAnimationMotionCommands.PunchSlowHigh
|
||||
or CombatAnimationMotionCommands.PunchSlowMed
|
||||
or CombatAnimationMotionCommands.PunchSlowLow
|
||||
or CombatAnimationMotionCommands.OffhandPunchFastHigh
|
||||
or CombatAnimationMotionCommands.OffhandPunchFastMed
|
||||
or CombatAnimationMotionCommands.OffhandPunchFastLow
|
||||
or CombatAnimationMotionCommands.OffhandPunchSlowHigh
|
||||
or CombatAnimationMotionCommands.OffhandPunchSlowMed
|
||||
or CombatAnimationMotionCommands.OffhandPunchSlowLow => CombatAnimationKind.MeleeSwing,
|
||||
|
||||
CombatAnimationMotionCommands.Shoot
|
||||
or CombatAnimationMotionCommands.MissileAttack1
|
||||
|
|
@ -192,8 +207,11 @@ internal static class CombatAnimationMotionCommands
|
|||
public const uint BowCombat = 0x8000003Fu;
|
||||
public const uint SwordShieldCombat = 0x80000040u;
|
||||
public const uint CrossbowCombat = 0x80000041u;
|
||||
public const uint TwoHandedSwordCombat = 0x80000046u;
|
||||
public const uint SlingCombat = 0x80000047u;
|
||||
public const uint SlingCombat = 0x80000043u;
|
||||
public const uint TwoHandedSwordCombat = 0x80000044u;
|
||||
public const uint TwoHandedStaffCombat = 0x80000045u;
|
||||
public const uint DualWieldCombat = 0x80000046u;
|
||||
public const uint ThrownWeaponCombat = 0x80000047u;
|
||||
public const uint Magic = 0x80000049u;
|
||||
public const uint AtlatlCombat = 0x8000013Bu;
|
||||
public const uint ThrownShieldCombat = 0x8000013Cu;
|
||||
|
|
@ -247,32 +265,44 @@ internal static class CombatAnimationMotionCommands
|
|||
public const uint TripleThrustMed = 0x10000129u;
|
||||
public const uint TripleThrustHigh = 0x1000012Au;
|
||||
|
||||
public const uint OffhandSlashHigh = 0x10000173u;
|
||||
public const uint OffhandSlashMed = 0x10000174u;
|
||||
public const uint OffhandSlashLow = 0x10000175u;
|
||||
public const uint OffhandThrustHigh = 0x10000176u;
|
||||
public const uint OffhandThrustMed = 0x10000177u;
|
||||
public const uint OffhandThrustLow = 0x10000178u;
|
||||
public const uint OffhandDoubleSlashLow = 0x10000179u;
|
||||
public const uint OffhandDoubleSlashMed = 0x1000017Au;
|
||||
public const uint OffhandDoubleSlashHigh = 0x1000017Bu;
|
||||
public const uint OffhandTripleSlashLow = 0x1000017Cu;
|
||||
public const uint OffhandTripleSlashMed = 0x1000017Du;
|
||||
public const uint OffhandTripleSlashHigh = 0x1000017Eu;
|
||||
public const uint OffhandDoubleThrustLow = 0x1000017Fu;
|
||||
public const uint OffhandDoubleThrustMed = 0x10000180u;
|
||||
public const uint OffhandDoubleThrustHigh = 0x10000181u;
|
||||
public const uint OffhandTripleThrustLow = 0x10000182u;
|
||||
public const uint OffhandTripleThrustMed = 0x10000183u;
|
||||
public const uint OffhandTripleThrustHigh = 0x10000184u;
|
||||
public const uint OffhandKick = 0x10000185u;
|
||||
public const uint AttackHigh4 = 0x10000186u;
|
||||
public const uint AttackMed4 = 0x10000187u;
|
||||
public const uint AttackLow4 = 0x10000188u;
|
||||
public const uint AttackHigh5 = 0x10000189u;
|
||||
public const uint AttackMed5 = 0x1000018Au;
|
||||
public const uint AttackLow5 = 0x1000018Bu;
|
||||
public const uint AttackHigh6 = 0x1000018Cu;
|
||||
public const uint AttackMed6 = 0x1000018Du;
|
||||
public const uint AttackLow6 = 0x1000018Eu;
|
||||
public const uint OffhandSlashHigh = 0x10000170u;
|
||||
public const uint OffhandSlashMed = 0x10000171u;
|
||||
public const uint OffhandSlashLow = 0x10000172u;
|
||||
public const uint OffhandThrustHigh = 0x10000173u;
|
||||
public const uint OffhandThrustMed = 0x10000174u;
|
||||
public const uint OffhandThrustLow = 0x10000175u;
|
||||
public const uint OffhandDoubleSlashLow = 0x10000176u;
|
||||
public const uint OffhandDoubleSlashMed = 0x10000177u;
|
||||
public const uint OffhandDoubleSlashHigh = 0x10000178u;
|
||||
public const uint OffhandTripleSlashLow = 0x10000179u;
|
||||
public const uint OffhandTripleSlashMed = 0x1000017Au;
|
||||
public const uint OffhandTripleSlashHigh = 0x1000017Bu;
|
||||
public const uint OffhandDoubleThrustLow = 0x1000017Cu;
|
||||
public const uint OffhandDoubleThrustMed = 0x1000017Du;
|
||||
public const uint OffhandDoubleThrustHigh = 0x1000017Eu;
|
||||
public const uint OffhandTripleThrustLow = 0x1000017Fu;
|
||||
public const uint OffhandTripleThrustMed = 0x10000180u;
|
||||
public const uint OffhandTripleThrustHigh = 0x10000181u;
|
||||
public const uint OffhandKick = 0x10000182u;
|
||||
public const uint AttackHigh4 = 0x10000183u;
|
||||
public const uint AttackMed4 = 0x10000184u;
|
||||
public const uint AttackLow4 = 0x10000185u;
|
||||
public const uint AttackHigh5 = 0x10000186u;
|
||||
public const uint AttackMed5 = 0x10000187u;
|
||||
public const uint AttackLow5 = 0x10000188u;
|
||||
public const uint AttackHigh6 = 0x10000189u;
|
||||
public const uint AttackMed6 = 0x1000018Au;
|
||||
public const uint AttackLow6 = 0x1000018Bu;
|
||||
public const uint PunchFastHigh = 0x1000018Cu;
|
||||
public const uint PunchFastMed = 0x1000018Du;
|
||||
public const uint PunchFastLow = 0x1000018Eu;
|
||||
public const uint PunchSlowHigh = 0x1000018Fu;
|
||||
public const uint PunchSlowMed = 0x10000190u;
|
||||
public const uint PunchSlowLow = 0x10000191u;
|
||||
public const uint OffhandPunchFastHigh = 0x10000192u;
|
||||
public const uint OffhandPunchFastMed = 0x10000193u;
|
||||
public const uint OffhandPunchFastLow = 0x10000194u;
|
||||
public const uint OffhandPunchSlowHigh = 0x10000195u;
|
||||
public const uint OffhandPunchSlowMed = 0x10000196u;
|
||||
public const uint OffhandPunchSlowLow = 0x10000197u;
|
||||
}
|
||||
|
|
|
|||
89
src/AcDream.Core/Combat/CombatManeuverSelector.cs
Normal file
89
src/AcDream.Core/Combat/CombatManeuverSelector.cs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
using DatReaderWriter.DBObjs;
|
||||
using DatMotionCommand = DatReaderWriter.Enums.MotionCommand;
|
||||
using DatMotionStance = DatReaderWriter.Enums.MotionStance;
|
||||
using DatAttackHeight = DatReaderWriter.Enums.AttackHeight;
|
||||
using DatAttackType = DatReaderWriter.Enums.AttackType;
|
||||
|
||||
namespace AcDream.Core.Combat;
|
||||
|
||||
/// <summary>
|
||||
/// Selects combat swing motions from the retail <c>CombatTable</c> DBObj.
|
||||
///
|
||||
/// Retail evidence:
|
||||
/// - <c>CombatManeuverTable::Get</c> (0x0056AB60) loads DB type
|
||||
/// <c>0x1000000D</c> for a 0x30xxxxxx combat table id.
|
||||
/// - ACE <c>CombatManeuverTable.GetMotion</c> indexes maneuvers by
|
||||
/// stance, attack height, and attack type, returning all matching motions.
|
||||
/// - ACE <c>Player_Melee.GetSwingAnimation</c> then chooses
|
||||
/// <c>motions[1]</c> when more than one motion exists and power is below
|
||||
/// the subdivision threshold; otherwise it uses <c>motions[0]</c>.
|
||||
/// </summary>
|
||||
public static class CombatManeuverSelector
|
||||
{
|
||||
public const float DefaultSubdivision = 0.33f;
|
||||
public const float ThrustSlashSubdivision = 0.66f;
|
||||
|
||||
public static CombatManeuverSelection SelectMotion(
|
||||
CombatTable table,
|
||||
DatMotionStance stance,
|
||||
DatAttackHeight attackHeight,
|
||||
DatAttackType attackType,
|
||||
float powerLevel,
|
||||
bool isThrustSlashWeapon = false)
|
||||
{
|
||||
var motions = FindMotions(table, stance, attackHeight, attackType);
|
||||
if (motions.Count == 0)
|
||||
return CombatManeuverSelection.None;
|
||||
|
||||
float subdivision = isThrustSlashWeapon
|
||||
? ThrustSlashSubdivision
|
||||
: DefaultSubdivision;
|
||||
|
||||
var motion = motions.Count > 1 && powerLevel < subdivision
|
||||
? motions[1]
|
||||
: motions[0];
|
||||
|
||||
return new CombatManeuverSelection(
|
||||
Found: true,
|
||||
Motion: motion,
|
||||
Candidates: motions,
|
||||
EffectiveAttackType: attackType,
|
||||
Subdivision: subdivision);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<DatMotionCommand> FindMotions(
|
||||
CombatTable table,
|
||||
DatMotionStance stance,
|
||||
DatAttackHeight attackHeight,
|
||||
DatAttackType attackType)
|
||||
{
|
||||
var result = new List<DatMotionCommand>();
|
||||
|
||||
foreach (var maneuver in table.CombatManeuvers)
|
||||
{
|
||||
if (maneuver.Style == stance
|
||||
&& maneuver.AttackHeight == attackHeight
|
||||
&& maneuver.AttackType == attackType)
|
||||
{
|
||||
result.Add(maneuver.Motion);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly record struct CombatManeuverSelection(
|
||||
bool Found,
|
||||
DatMotionCommand Motion,
|
||||
IReadOnlyList<DatMotionCommand> Candidates,
|
||||
DatAttackType EffectiveAttackType,
|
||||
float Subdivision)
|
||||
{
|
||||
public static CombatManeuverSelection None { get; } = new(
|
||||
Found: false,
|
||||
Motion: DatMotionCommand.Invalid,
|
||||
Candidates: Array.Empty<DatMotionCommand>(),
|
||||
EffectiveAttackType: DatAttackType.Undef,
|
||||
Subdivision: 0f);
|
||||
}
|
||||
|
|
@ -84,6 +84,24 @@ public static class MotionCommandResolver
|
|||
result[lo] = full;
|
||||
}
|
||||
}
|
||||
|
||||
ApplyNamedRetailOverrides(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void ApplyNamedRetailOverrides(Dictionary<ushort, uint> result)
|
||||
{
|
||||
// The generated DRW enum is shifted by three entries starting at
|
||||
// AllegianceHometownRecall. The named Sept 2013 retail command_ids
|
||||
// table is authoritative here:
|
||||
// named-retail/acclient_2013_pseudo_c.txt lines 1017626-1017658
|
||||
// and command-name table lines 1068272-1068313.
|
||||
//
|
||||
// These values cover recall, offhand, attack 4-6, and fast/slow punch
|
||||
// actions. Without the override, wire command 0x0170 reconstructs to
|
||||
// IssueSlashCommand instead of OffhandSlashHigh, so offhand swing
|
||||
// animations route as UI commands and never play.
|
||||
for (ushort lo = 0x016E; lo <= 0x0197; lo++)
|
||||
result[lo] = 0x10000000u | lo;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue