260 lines
8.0 KiB
C#
260 lines
8.0 KiB
C#
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
|
||
}
|
||
}
|
||
}
|