Files
Continentis/Assets/Scripts/MainGame/Buff/BuffSubmodules.cs
SoulliesOfficial ac98ec3aef 更新
2026-04-17 12:01:50 -04:00

496 lines
20 KiB
C#
Raw Permalink 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;
using System.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Card;
using Continentis.MainGame.Character;
using Continentis.MainGame.UI;
using SoftCircuits.Collections;
using SLSUtilities.General;
using SLSUtilities.UModAssistance;
using UnityEngine;
using UnityEngine.Events;
namespace Continentis.MainGame
{
public partial class BuffBase<T>
{
public abstract class BuffSubmodule : SubmoduleBase<BuffBase<T>>
{
public BuffBase<T> buff;
protected BuffSubmodule(BuffBase<T> buff) : base(buff)
{
this.buff = buff;
}
}
public class ContentSubmodule : BuffSubmodule
{
public string modClassName;
public string displayName;
public string originalFunctionText;
public string interpretedFunctionText;
public Dictionary<string, Func<string>> parameterGetters;
/// <summary>
/// 自动根据Buff类名生成显示名称和功能描述文本的Key并本地化。
/// </summary>
/// <param name="buff">所属Buff实例。</param>
/// <param name="willLocalizeFuncText">是否本地化功能描述文本默认为true设为false说明此Buff具有不止一条本地化文本需要切换。</param>
public ContentSubmodule(BuffBase<T> buff, bool willLocalizeFuncText = true) : base(buff)
{
modClassName = ModManager.GetModClassName(buff.GetType());
this.displayName = ("Buff_" + modClassName + "_DisplayName").Localize();
if (willLocalizeFuncText)
{
this.originalFunctionText = ("Buff_" + modClassName + "_FunctionText").Localize();
this.interpretedFunctionText = this.originalFunctionText;
}
parameterGetters = new Dictionary<string, Func<string>>();
}
public ContentSubmodule(BuffBase<T> buff, string displayNameKey, string originalFunctionTextKey) : base(buff)
{
this.displayName = displayNameKey.Localize();
this.originalFunctionText = originalFunctionTextKey.Localize();
this.interpretedFunctionText = this.originalFunctionText;
parameterGetters = new Dictionary<string, Func<string>>();
}
public ContentSubmodule SetParameterGetter(Dictionary<string, Func<string>> getters)
{
parameterGetters = getters;
return this;
}
public ContentSubmodule AddParameterGetter(string parameterName, Func<string> getter)
{
parameterGetters[parameterName] = getter;
return this;
}
}
public class IconSubmodule : BuffSubmodule
{
public string iconID;
public Sprite icon;
public List<string> synchronizedParameters;
public HUD_CharacterBuffIcon buffIcon;
public IconSubmodule(BuffBase<T> buff, string iconID = "") : base(buff)
{
if (string.IsNullOrEmpty(iconID))
{
this.iconID = "BuffIcon_" + ModManager.GetModClassName(buff.GetType());
}
else
{
this.iconID = iconID;
}
Texture2D tex = ModManager.GetAsset<Texture2D>(this.iconID);
if (tex != null)
{
this.icon = SpriteExtension.Create(tex);
}
else
{
this.icon = MainGameManager.Instance.basePrefabs.defaultBuffIcon;
}
if (buff.contentSubmodule != null)
{
this.synchronizedParameters = buff.contentSubmodule.parameterGetters.Keys.ToList();
}
}
public IconSubmodule SetTextFunctions(params string[] syncParameters)
{
synchronizedParameters = syncParameters.ToList();
return this;
}
public void Update()
{
buffIcon?.UpdateIcon();
}
public void Remove()
{
//buffIcon?.RemoveIcon();
}
}
public class EventSubmodule : BuffSubmodule
{
public OrderedDictionary<string, PrioritizedAction> onCombatStart; //战斗开始时
public OrderedDictionary<string, PrioritizedAction> onCombatEnd; //战斗结束时
public OrderedDictionary<string, PrioritizedAction> onRoundStart; //每回合开始时
public OrderedDictionary<string, PrioritizedAction> onRoundEnd; //每回合结束时
public OrderedDictionary<string, PrioritizedAction> onActionStart; //每次行动开始时
public OrderedDictionary<string, PrioritizedAction> onActionEnd; //每次行动结束时
public OrderedDictionary<string, PrioritizedAction<BuffBase<T>>> onOtherBuffApplied; //Buff被添加时参数为被添加的Buff实例
public OrderedDictionary<string, PrioritizedAction<BuffBase<T>>> onOtherBuffFirstApplied; //Buff被新添加时参数为被添加的Buff实例
public OrderedDictionary<string, PrioritizedAction<BuffBase<T>>> onOtherBuffRemoved; //Buff被移除后参数为被移除的Buff实例
public OrderedDictionary<string, PrioritizedAction<CharacterBase, IntendedCard, CharacterBase>> onOpponentDecideAction; //对手AI决定行动时参数为对手和原定的目标角色
public OrderedDictionary<string, PrioritizedAction<AttackResult>> onDealAttack; //造成伤害后,参数为伤害结果
public OrderedDictionary<string, PrioritizedAction<AttackResult>> onGetAttacked; //被攻击后,参数为伤害结果
/// <summary> 在 Block/Shield/Health 计算之前触发,可修改 damage 或设置 isCancelled = true 来免疫本次伤害。</summary>
public OrderedDictionary<string, PrioritizedAction<IncomingDamageModifier>> onBeforeReceiveDamage;
public OrderedDictionary<string, PrioritizedAction<CardInstance>> onDrawCard; //抽到卡牌时
public OrderedDictionary<string, PrioritizedAction<CardInstance, List<CharacterBase>>> onBeforePlayCard; //使用卡牌前,参数为目标列表
public OrderedDictionary<string, PrioritizedAction<CardInstance, List<CharacterBase>>> onAfterPlayCard; //使用卡牌后,参数为目标列表
public OrderedDictionary<string, PrioritizedAction<CardInstance>> onDiscardCard; //卡牌被弃牌时
public OrderedDictionary<string, PrioritizedAction<CardInstance>> onExhaustCard; //卡牌被消耗时
public EventSubmodule(BuffBase<T> buff) : base(buff)
{
onCombatStart = new OrderedDictionary<string, PrioritizedAction>();
onCombatEnd = new OrderedDictionary<string, PrioritizedAction>();
onRoundStart = new OrderedDictionary<string, PrioritizedAction>();
onRoundEnd = new OrderedDictionary<string, PrioritizedAction>();
onActionStart = new OrderedDictionary<string, PrioritizedAction>();
onActionEnd = new OrderedDictionary<string, PrioritizedAction>();
onOtherBuffApplied = new OrderedDictionary<string, PrioritizedAction<BuffBase<T>>>();
onOtherBuffFirstApplied = new OrderedDictionary<string, PrioritizedAction<BuffBase<T>>>();
onOtherBuffRemoved = new OrderedDictionary<string, PrioritizedAction<BuffBase<T>>>();
onDealAttack = new OrderedDictionary<string, PrioritizedAction<AttackResult>>();
onGetAttacked = new OrderedDictionary<string, PrioritizedAction<AttackResult>>();
onBeforeReceiveDamage = new OrderedDictionary<string, PrioritizedAction<IncomingDamageModifier>>();
onOpponentDecideAction = new OrderedDictionary<string, PrioritizedAction<CharacterBase, IntendedCard, CharacterBase>>();
onDrawCard = new OrderedDictionary<string, PrioritizedAction<CardInstance>>();
onBeforePlayCard = new OrderedDictionary<string, PrioritizedAction<CardInstance, List<CharacterBase>>>();
onAfterPlayCard = new OrderedDictionary<string, PrioritizedAction<CardInstance, List<CharacterBase>>>();
onDiscardCard = new OrderedDictionary<string, PrioritizedAction<CardInstance>>();
onExhaustCard = new OrderedDictionary<string, PrioritizedAction<CardInstance>>();
}
}
public class CountSubmodule : BuffSubmodule
{
public int maximumCount;
public int remainingCount;
public bool isInfinite => maximumCount < 0;
public float remainingPercentage => (float)remainingCount / maximumCount;
public UnityAction<int> onCountChanged;
/// <summary>
/// maximumCount为负数表示无限持续时间。
/// </summary>
public CountSubmodule(BuffBase<T> buff, int maximumCount = -1) : base(buff)
{
this.maximumCount = maximumCount;
this.remainingCount = maximumCount;
}
public void Update()
{
if (isInfinite)
{
return;
}
remainingCount--;
onCountChanged?.Invoke(remainingCount);
if (remainingCount <= 0)
{
buff.Remove();
}
}
/// <summary>
/// 刷新剩余时间至初始值。
/// </summary>
public void Refresh()
{
if (isInfinite)
{
return;
}
remainingCount = maximumCount;
onCountChanged?.Invoke(remainingCount);
}
/// <summary>
/// 覆盖可选保留更大的duration并刷新剩余时间。
/// </summary>
public void Refresh(int overrideDuration, bool keepMaximal = true)
{
if (isInfinite)
{
return;
}
maximumCount = keepMaximal ? Mathf.Max(maximumCount, overrideDuration) : overrideDuration;
remainingCount = maximumCount;
onCountChanged?.Invoke(remainingCount);
}
/// <summary>
/// 增加持续时间和剩余时间。
/// </summary>
public void AddCount(int maxCount)
{
if (isInfinite)
{
return;
}
maximumCount += maxCount;
remainingCount += maxCount;
onCountChanged?.Invoke(remainingCount);
}
/// <summary>
/// 仅增加剩余时间,不改变总持续时间。
/// 如果增加后剩余时间超过总持续时间,则剩余时间等于总持续时间。
/// </summary>
public void AddRemainingCount(int count)
{
if (isInfinite)
{
return;
}
remainingCount += count;
remainingCount = Mathf.Min(remainingCount, maximumCount);
onCountChanged?.Invoke(remainingCount);
}
/// <summary>
/// 选择更高的持续时间和剩余时间。
/// </summary>
public void PickHigherCount(CountSubmodule other)
{
if (isInfinite)
{
return;
}
maximumCount = Mathf.Max(maximumCount, other.maximumCount);
remainingCount = Mathf.Max(remainingCount, other.remainingCount);
}
public void PickHigherCount(int maximumCount, int remainingCount)
{
if (isInfinite)
{
return;
}
this.maximumCount = Mathf.Max(this.maximumCount, maximumCount);
this.remainingCount = Mathf.Max(this.remainingCount, remainingCount);
}
}
/// <summary>
/// Buff叠加模块叠加层数统一。
/// </summary>
public class UnitedStackSubmodule : BuffSubmodule
{
public bool willRemoveOnZero; //是否在层数为0时移除Buff
public bool isIntegerRange; //是否为整数范围型Buff可为0或负数
public int stackAmount; //当前层数
public int stackUpperLimit; //层数上限,-1表示无限制
public IBuffExtension_IntegerRange IntegerRange => buff as IBuffExtension_IntegerRange;
public UnitedStackSubmodule(BuffBase<T> buff, int initialStackAmount = 1, bool isIntegerRange = false) : base(buff)
{
this.willRemoveOnZero = true;
this.isIntegerRange = isIntegerRange;
this.stackAmount = initialStackAmount;
this.stackUpperLimit = -1;
}
public UnitedStackSubmodule(BuffBase<T> buff, bool willRemoveOnZero,
int stackUpperLimit = -1, int initialStackAmount = 1, bool isIntegerRange = false) : base(buff)
{
this.willRemoveOnZero = willRemoveOnZero;
this.isIntegerRange = isIntegerRange;
this.stackAmount = initialStackAmount;
this.stackUpperLimit = stackUpperLimit;
}
public void RefreshStackUpperLimit(int upperLimit, bool keepMaximal = true)
{
stackUpperLimit = keepMaximal ? Mathf.Max(stackUpperLimit, upperLimit) : upperLimit;
}
public void PickHigherStack(UnitedStackSubmodule other)
{
stackAmount = Mathf.Max(stackAmount, other.stackAmount);
if (stackUpperLimit > 0)
{
stackAmount = Mathf.Min(stackAmount, stackUpperLimit);
}
}
public void AddStack(int amount) => ModifyStack(amount);
public void ReduceStack(int amount) => ModifyStack(-amount);
public void ModifyStack(int amount)
{
int lastStack = stackAmount;
stackAmount += amount;
if (stackUpperLimit > 0)
{
stackAmount = Mathf.Min(stackAmount, stackUpperLimit);
}
if (willRemoveOnZero)
{
if (!isIntegerRange && stackAmount <= 0)
{
buff.Remove();
return;
}
if (isIntegerRange && stackAmount == 0)
{
buff.Remove();
return;
}
}
if (isIntegerRange)
{
if (lastStack < 0 && stackAmount > 0)
{
IntegerRange.OnBecomePositive();
}
else if (lastStack > 0 && stackAmount < 0)
{
IntegerRange.OnBecomeNegative();
}
else if (lastStack != 0 && stackAmount == 0)
{
IntegerRange.OnBecomeZero();
}
}
if(buff is CharacterBuffBase characterBuff)
{
characterBuff.iconSubmodule?.Update();
}
}
public void ClearAllStacks()
{
stackAmount = 0;
if (willRemoveOnZero)
{
buff.Remove();
}
}
}
/// <summary>
/// Buff独立叠加模块每一层的持续时间和层数可以不同。
/// </summary>
public class IndependentStackSubmodule : BuffSubmodule
{
public bool willRemoveOnZero;
public List<IndependentStackUnit> independentStacks;
public int totalStackAmount;
public IndependentStackSubmodule(BuffBase<T> buff, bool willRemoveOnZero) : base(buff)
{
this.willRemoveOnZero = willRemoveOnZero;
independentStacks = new List<IndependentStackUnit>();
totalStackAmount = 0;
}
public void Update()
{
independentStacks.ForEach(x => x.remainingTime--);
independentStacks.RemoveAll(x => !x.IsInfinite && x.remainingTime <= 0);
totalStackAmount = independentStacks.Sum(x => x.stackAmount);
if (willRemoveOnZero && totalStackAmount <= 0)
{
buff.Remove();
}
}
public IndependentStackUnit AddStack(int stackAmount, int duration)
{
IndependentStackUnit addition = new IndependentStackUnit(stackAmount, duration);
independentStacks.Add(addition);
independentStacks.Sort();
return addition;
}
public void ReduceStack(int stackAmount)
{
for (int i = 0; i < independentStacks.Count; i++)
{
if (stackAmount <= independentStacks[i].stackAmount)
{
independentStacks[i].stackAmount -= stackAmount;
if (independentStacks[i].stackAmount <= 0)
{
independentStacks.RemoveAt(i);
}
return;
}
stackAmount -= independentStacks[i].stackAmount;
independentStacks.RemoveAt(i);
i--;
}
totalStackAmount = independentStacks.Sum(x => x.stackAmount);
if (willRemoveOnZero && totalStackAmount <= 0)
{
buff.Remove();
}
}
public class IndependentStackUnit : IComparable<IndependentStackUnit>
{
public int stackAmount;
public int duration;
public int remainingTime;
public Dictionary<string, float> parameters;
public bool IsInfinite => duration < 0;
public IndependentStackUnit(int stackAmount, int duration)
{
this.stackAmount = stackAmount;
this.duration = duration;
this.remainingTime = duration;
this.parameters = new Dictionary<string, float>();
}
public int CompareTo(IndependentStackUnit other)
{
return remainingTime.CompareTo(other.remainingTime);
}
}
}
}
}