572 lines
20 KiB
C#
572 lines
20 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using SoftCircuits.Collections;
|
||
using SLSUtilities.General;
|
||
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
|
||
namespace Cielonos.MainGame.Buffs
|
||
{
|
||
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 displayName;
|
||
public string originalFunctionText;
|
||
public string interpretedFunctionText;
|
||
public Dictionary<string, Func<string>> parameterGetters;
|
||
|
||
public ContentSubmodule(BuffBase<T> buff) : base(buff)
|
||
{
|
||
string displayNameKey = buff.GetType().Name + "_DisplayName";
|
||
string originalFunctionTextKey = buff.GetType().Name + "_FunctionText";
|
||
this.displayName = displayNameKey.Localize();
|
||
this.originalFunctionText = originalFunctionTextKey.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 (buff.contentSubmodule != null)
|
||
{
|
||
this.synchronizedParameters = buff.contentSubmodule.parameterGetters.Keys.ToList();
|
||
}
|
||
}
|
||
|
||
public IconSubmodule SetTextFunctions(params string[] syncParameters)
|
||
{
|
||
synchronizedParameters = syncParameters.ToList();
|
||
return this;
|
||
}
|
||
|
||
public void Update()
|
||
{
|
||
|
||
}
|
||
|
||
public void Remove()
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
public class EventSubmodule : BuffSubmodule
|
||
{
|
||
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<Attack.Result>> onDealAttack; //造成伤害后,参数为伤害结果
|
||
public OrderedDictionary<string, PrioritizedAction<AttackAreaBase, Attack.Context>> onBeforeGetAttacked; //被攻击前,参数为攻击上下文
|
||
public OrderedDictionary<string, PrioritizedAction<AttackAreaBase, Attack.Result>> onAfterGetAttacked; //被攻击后,参数为伤害结果
|
||
|
||
public EventSubmodule(BuffBase<T> buff) : base(buff)
|
||
{
|
||
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<Attack.Result>>();
|
||
onBeforeGetAttacked = new OrderedDictionary<string, PrioritizedAction<AttackAreaBase, Attack.Context>>();
|
||
onAfterGetAttacked = new OrderedDictionary<string, PrioritizedAction<AttackAreaBase, Attack.Result>>();
|
||
}
|
||
}
|
||
|
||
public class TimeSubmodule : BuffSubmodule
|
||
{
|
||
public bool isInfinite;
|
||
public float duration;
|
||
public float timeLeft;
|
||
public float TimeLeftPercentage => timeLeft / duration;
|
||
|
||
public List<IntervalAction> intervalActions;
|
||
|
||
public TimeSubmodule(BuffBase<T> buff, bool isInfinite): base(buff)
|
||
{
|
||
this.buff = buff;
|
||
this.isInfinite = isInfinite;
|
||
this.duration = -1;
|
||
this.timeLeft = -1;
|
||
this.intervalActions = new List<IntervalAction>();
|
||
}
|
||
|
||
public TimeSubmodule(BuffBase<T> buff, float duration): base(buff)
|
||
{
|
||
this.buff = buff;
|
||
this.isInfinite = false;
|
||
this.duration = duration;
|
||
this.timeLeft = duration;
|
||
this.intervalActions = new List<IntervalAction>();
|
||
}
|
||
|
||
public void Update(float deltaTime)
|
||
{
|
||
foreach (var action in intervalActions)
|
||
{
|
||
action.Update(deltaTime);
|
||
}
|
||
|
||
if (isInfinite)
|
||
{
|
||
return;
|
||
}
|
||
|
||
timeLeft -= deltaTime;
|
||
|
||
if (timeLeft <= 0)
|
||
{
|
||
buff.Remove();
|
||
}
|
||
}
|
||
|
||
public void AddIntervalAction(Action action, float interval)
|
||
{
|
||
intervalActions.Add(new IntervalAction(action, interval));
|
||
}
|
||
|
||
public void RefreshDuration()
|
||
{
|
||
timeLeft = duration;
|
||
}
|
||
|
||
public void PickHigherDuration(TimeSubmodule other)
|
||
{
|
||
duration = Mathf.Max(duration, other.duration);
|
||
timeLeft = duration;
|
||
}
|
||
|
||
public void AddDuration(TimeSubmodule other)
|
||
{
|
||
duration += other.duration;
|
||
timeLeft += other.duration;
|
||
}
|
||
|
||
public void AddDuration(float additionalDuration)
|
||
{
|
||
duration += additionalDuration;
|
||
timeLeft += additionalDuration;
|
||
}
|
||
|
||
|
||
public class IntervalAction
|
||
{
|
||
public float timer;
|
||
public float interval;
|
||
Action action;
|
||
|
||
public IntervalAction(Action action, float interval)
|
||
{
|
||
this.action = action;
|
||
this.interval = interval;
|
||
this.timer = 0f;
|
||
}
|
||
|
||
public void Update(float deltaTime)
|
||
{
|
||
timer += deltaTime;
|
||
if (timer >= interval)
|
||
{
|
||
action.Invoke();
|
||
timer -= interval;
|
||
}
|
||
}
|
||
|
||
public void SetInterval(float interval)
|
||
{
|
||
this.interval = interval;
|
||
if (timer > interval)
|
||
{
|
||
timer = interval;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
|
||
/// <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();
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool ReachedUpperLimit()
|
||
{
|
||
if (stackUpperLimit < 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return stackAmount >= stackUpperLimit;
|
||
}
|
||
|
||
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 = true) : base(buff)
|
||
{
|
||
independentStacks = new List<IndependentStackUnit>();
|
||
totalStackAmount = 0;
|
||
this.willRemoveOnZero = willRemoveOnZero;
|
||
}
|
||
|
||
public IndependentStackSubmodule(BuffBase<T> buff, int stack, float duration,
|
||
bool willRemoveOnZero = true) : base(buff)
|
||
{
|
||
independentStacks = new List<IndependentStackUnit>();
|
||
AddStack(stack, duration);
|
||
totalStackAmount = stack;
|
||
this.willRemoveOnZero = willRemoveOnZero;
|
||
}
|
||
|
||
public IndependentStackSubmodule(BuffBase<T> buff, int stack, float duration,
|
||
bool willRemoveOnZero, out IndependentStackUnit unit) : base(buff)
|
||
{
|
||
independentStacks = new List<IndependentStackUnit>();
|
||
unit = AddStack(stack, duration);
|
||
totalStackAmount = stack;
|
||
this.willRemoveOnZero = willRemoveOnZero;
|
||
}
|
||
|
||
public void Update(float deltaTime)
|
||
{
|
||
independentStacks.ForEach(x => x.remainingTime -= deltaTime);
|
||
|
||
independentStacks.RemoveAll(x => !x.IsInfinite && x.remainingTime <= 0);
|
||
|
||
totalStackAmount = independentStacks.Sum(x => x.stackAmount);
|
||
|
||
if (willRemoveOnZero && totalStackAmount <= 0)
|
||
{
|
||
buff.Remove();
|
||
}
|
||
}
|
||
|
||
public IndependentStackUnit LongestUnit => independentStacks.Count > 0 ? independentStacks[^1] : null;
|
||
|
||
public void Merge(IndependentStackSubmodule other)
|
||
{
|
||
foreach (IndependentStackUnit stack in other.independentStacks)
|
||
{
|
||
independentStacks.Add(stack.Clone());
|
||
}
|
||
independentStacks.Sort();
|
||
}
|
||
|
||
public IndependentStackUnit AddStack(int stackAmount, float 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>, ICloneable<IndependentStackUnit>
|
||
{
|
||
public int stackAmount;
|
||
public float duration;
|
||
public float remainingTime;
|
||
public Dictionary<string, float> parameters;
|
||
|
||
public bool IsInfinite => duration < 0;
|
||
public float RemainingPercentage => IsInfinite ? 1f : remainingTime / duration;
|
||
|
||
public IndependentStackUnit(int stackAmount, float 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);
|
||
}
|
||
|
||
public IndependentStackUnit Clone()
|
||
{
|
||
IndependentStackUnit clone = new IndependentStackUnit(stackAmount, duration);
|
||
clone.remainingTime = remainingTime;
|
||
foreach (var param in parameters)
|
||
{
|
||
clone.parameters[param.Key] = param.Value;
|
||
}
|
||
return clone;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} |