Files
Cielonos/Assets/Scripts/MainGame/AttackArea/AttackInfo/AttackResult.cs
SoulliesOfficial 39b43680a9 爆更
2026-07-18 03:16:20 -04:00

260 lines
8.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}
}
}