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
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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue