66 lines
2.2 KiB
C#
66 lines
2.2 KiB
C#
namespace NeonSprawl.Server.Game.Combat;
|
|
|
|
/// <summary>
|
|
/// Orchestrates deterministic combat resolution: ability catalog damage + prototype target HP (NEO-81).
|
|
/// Cast HTTP wiring: <see cref="AbilityInput.AbilityCastApi"/> (NEO-82).
|
|
/// </summary>
|
|
public static class CombatOperations
|
|
{
|
|
/// <summary>
|
|
/// Resolves one ability against one prototype combat target using catalog damage and the health store.
|
|
/// Defeated targets deny before damage application; zero-damage abilities succeed without mutating HP.
|
|
/// </summary>
|
|
public static CombatResult TryResolve(
|
|
string abilityId,
|
|
string targetId,
|
|
IAbilityDefinitionRegistry abilityRegistry,
|
|
ICombatEntityHealthStore healthStore)
|
|
{
|
|
if (!abilityRegistry.TryNormalizeKnown(abilityId, out var normalizedAbility) ||
|
|
!abilityRegistry.TryGetDefinition(normalizedAbility, out var definition))
|
|
{
|
|
return Deny(CombatReasonCodes.UnknownAbility);
|
|
}
|
|
|
|
if (!healthStore.TryGet(targetId, out var snapshot))
|
|
{
|
|
return Deny(CombatReasonCodes.UnknownTarget);
|
|
}
|
|
|
|
if (snapshot.Defeated)
|
|
{
|
|
return Deny(CombatReasonCodes.TargetDefeated, snapshot.CurrentHp);
|
|
}
|
|
|
|
if (definition.BaseDamage == 0)
|
|
{
|
|
return new CombatResult(
|
|
Success: true,
|
|
ReasonCode: null,
|
|
DamageDealt: 0,
|
|
TargetRemainingHp: snapshot.CurrentHp,
|
|
TargetDefeated: snapshot.Defeated);
|
|
}
|
|
|
|
if (!healthStore.TryApplyDamage(targetId, definition.BaseDamage, out var after))
|
|
{
|
|
return Deny(CombatReasonCodes.UnknownTarget);
|
|
}
|
|
|
|
return new CombatResult(
|
|
Success: true,
|
|
ReasonCode: null,
|
|
DamageDealt: definition.BaseDamage,
|
|
TargetRemainingHp: after.CurrentHp,
|
|
TargetDefeated: after.Defeated);
|
|
}
|
|
|
|
private static CombatResult Deny(string reasonCode, int? targetRemainingHp = null) =>
|
|
new(
|
|
Success: false,
|
|
ReasonCode: reasonCode,
|
|
DamageDealt: 0,
|
|
TargetRemainingHp: targetRemainingHp,
|
|
TargetDefeated: false);
|
|
}
|