Files
Cielonos/Assets/Scripts/MainGame/AttackArea/AttackInfo/AttackResult.cs

260 lines
8.0 KiB
C#
Raw Normal View History

2026-07-18 03:16:20 -04:00
using System.Collections.Generic;
using Cielonos.MainGame.Characters;
using UnityEngine;
namespace Cielonos.MainGame
{
public static partial class Attack
{
/// <summary>
/// 攻击结果:伤害结算后的数据载体,包含攻击事实、最终数值和对原始上下文的引用。
/// </summary>
public class Result
{
#region References
/// <summary>
/// 产生本次结算的攻击区域。Buff、持续伤害等非 AttackArea 路径保持为 null。
/// </summary>
public AttackAreaBase sourceArea;
/// <summary>
/// 原始攻击上下文包含攻击者、目标、命中点、spamGroupID 和可修改攻击值。
/// </summary>
public Context context;
public CharacterBase attacker => context.attacker;
public CharacterBase target => context.target;
public Vector3 hitPosition => context.hitPosition;
public string spamGroupID => context.spamGroupID;
public Value value
{
get => context.value;
set => context.value = value;
}
#endregion
#region Final Values
public float shieldBlockedDamage;
public float finalDamage;
public float finalStanceDepletion;
public float hitRecoveryTime;
#endregion
#region Constructors
/// <summary>
/// 从已有上下文构建结果。用于标准攻击流程Context 阶段完成后包装为 Result。
/// </summary>
public Result(Context context)
{
this.context = context;
}
/// <summary>
/// 从攻击区域构建结算结果,并保留本次攻击的来源。
/// </summary>
public Result(Context context, AttackAreaBase sourceArea) : this(context)
{
this.sourceArea = sourceArea;
}
/// <summary>
/// 直接从参数构建结果,用于 Buff 直接附伤等流程。会自动构建 Context 并关联 Value。
/// </summary>
public Result(Context context, Value value)
{
this.context = context;
this.value = value;
}
#endregion
#region Outcomes
public readonly HashSet<Outcome> outcomes = new HashSet<Outcome>();
public bool HasOutcome(Outcome outcome)
{
return outcomes.Contains(outcome);
}
public bool HasAnyOutcome(params Outcome[] queryOutcomes)
{
if (queryOutcomes == null) return false;
foreach (Outcome outcome in queryOutcomes)
{
if (outcomes.Contains(outcome)) return true;
}
return false;
}
public bool HasAllOutcomes(params Outcome[] queryOutcomes)
{
if (queryOutcomes == null || queryOutcomes.Length == 0) return false;
foreach (Outcome outcome in queryOutcomes)
{
if (!outcomes.Contains(outcome)) return false;
}
return true;
}
public void AddOutcome(Outcome outcome)
{
outcomes.Add(outcome);
}
public void RemoveOutcome(Outcome outcome)
{
outcomes.Remove(outcome);
}
public void SetOutcome(Outcome outcome, bool isActive)
{
if (isActive)
{
AddOutcome(outcome);
}
else
{
RemoveOutcome(outcome);
}
}
public bool isBlocked
{
get => HasOutcome(Outcome.Blocked);
set => SetOutcome(Outcome.Blocked, value);
}
public bool isDodged
{
get => HasOutcome(Outcome.Dodged);
set => SetOutcome(Outcome.Dodged, value);
}
public bool isReflected
{
get => HasOutcome(Outcome.Reflected);
set => SetOutcome(Outcome.Reflected, value);
}
public bool isMissed
{
get => HasOutcome(Outcome.Missed);
set => SetOutcome(Outcome.Missed, value);
}
public bool isEvaded
{
get => HasOutcome(Outcome.Evaded);
set => SetOutcome(Outcome.Evaded, value);
}
public bool causedDeath
{
get => HasOutcome(Outcome.Killed);
set => SetOutcome(Outcome.Killed, value);
}
public bool isBreakthrough
{
get => HasOutcome(Outcome.Breakthrough);
set => SetOutcome(Outcome.Breakthrough, value);
}
public bool isDisrupted
{
get => HasOutcome(Outcome.Disrupted);
set => SetOutcome(Outcome.Disrupted, value);
}
public bool causedStanceBreak
{
get => HasOutcome(Outcome.StanceBreak);
set => SetOutcome(Outcome.StanceBreak, value);
}
public bool isReactionOnlyCheck
{
get => HasOutcome(Outcome.ReactionOnlyCheck);
set => SetOutcome(Outcome.ReactionOnlyCheck, value);
}
public bool isInvalidAttack
{
get => HasOutcome(Outcome.InvalidAttack);
set => SetOutcome(Outcome.InvalidAttack, value);
}
public bool hitEventsInvoked
{
get => HasOutcome(Outcome.HitEventsInvoked);
set => SetOutcome(Outcome.HitEventsInvoked, value);
}
public bool damageEventsInvoked
{
get => HasOutcome(Outcome.DamageEventsInvoked);
set => SetOutcome(Outcome.DamageEventsInvoked, value);
}
public bool isDenied
{
get => context?.isDenied ?? false;
set
{
if (context != null)
{
context.isDenied = value;
}
SetOutcome(Outcome.Denied, value);
}
}
public bool isDamageNegated
{
get => value?.isDamageNegated ?? false;
set
{
if (this.value != null)
{
this.value.isDamageNegated = value;
}
SetOutcome(Outcome.DamageNegated, value);
}
}
public bool isEffectiveHit => !isBlocked && !isDodged && !isMissed && !isEvaded && !isDenied && target != null;
/// <summary>
/// 根据当前结算字段同步派生 Outcome。这里只记录攻击事实不决定具体反馈表现。
/// </summary>
public void ResolveOutcomes()
{
SetOutcome(Outcome.InvalidTarget, target == null);
SetOutcome(Outcome.Denied, isDenied);
SetOutcome(Outcome.Hit, isEffectiveHit && hitEventsInvoked);
SetOutcome(Outcome.DamageNegated, isDamageNegated);
SetOutcome(Outcome.ShieldAbsorbed, shieldBlockedDamage > 0f);
SetOutcome(Outcome.HealthDamaged, finalDamage > 0f);
SetOutcome(Outcome.Critical, isEffectiveHit && value is { isCritical: true });
SetOutcome(Outcome.StanceDepleted, finalStanceDepletion > 0f);
}
#endregion
}
}
}