89 lines
2.9 KiB
C#
89 lines
2.9 KiB
C#
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);
|
|
}
|