using System; using System.Collections.Generic; using System.Linq; using Cielonos.MainGame.Characters; using Cielonos.MainGame.UI; using Sirenix.OdinInspector; using SLSUtilities.General; using UnityEngine; namespace Cielonos.MainGame.Inventory { public abstract partial class ItemBase { public class FunctionSubmodule : SubmoduleBase { [ReadOnly] public Dictionary functionUnits; public RuntimeFunctionUnit mainFunction => functionUnits.Count > 0 ? functionUnits.Values.First() : null; public RuntimeFunctionUnit this[string functionName] => functionUnits.ContainsKey(functionName) ? functionUnits[functionName] : null; public FunctionSubmodule(ItemBase owner, FunctionData data) : base(owner) { functionUnits = new Dictionary(); foreach (var unit in data.functionUnitList) { functionUnits[unit.unitName] = new RuntimeFunctionUnit(this, unit); } } public void Update(float deltaTime) { foreach (var unit in functionUnits.Values) { unit.Update(deltaTime); } } } public class RuntimeFunctionUnit : SubmoduleBase { private CharacterBase character => owner.Owner.player; public FunctionData.FunctionUnit data; public float currentCooldown; public float maxCooldown; private Dictionary> _runtimeTagsBySource; public event Action OnPresentationChanged; public RuntimeFunctionUnit(FunctionSubmodule owner, FunctionData.FunctionUnit data) : base(owner) { this.data = data; maxCooldown = data.cooldown; currentCooldown = 0f; } public bool HasTag(string tag) { // Runtime tag 只影响当前 FunctionUnit 实例,不修改共享的 FunctionData。 if (data.tags != null && data.tags.Contains(tag)) return true; if (_runtimeTagsBySource == null) return false; foreach (HashSet tags in _runtimeTagsBySource.Values) { if (tags.Contains(tag)) return true; } return false; } public void SetRuntimeTag(string sourceKey, string tag, bool enabled) { // sourceKey 让多个装备可以独立添加或撤销同名表现标签。 bool changed = false; if (enabled) { _runtimeTagsBySource ??= new Dictionary>(); if (!_runtimeTagsBySource.TryGetValue(sourceKey, out HashSet tags)) { tags = new HashSet(); _runtimeTagsBySource.Add(sourceKey, tags); } changed = tags.Add(tag); } else if (_runtimeTagsBySource != null && _runtimeTagsBySource.TryGetValue(sourceKey, out HashSet tags)) { changed = tags.Remove(tag); if (tags.Count == 0) { _runtimeTagsBySource.Remove(sourceKey); } } if (changed) { OnPresentationChanged?.Invoke(); } } public void RemoveRuntimeTags(string sourceKey) { if (_runtimeTagsBySource != null && _runtimeTagsBySource.Remove(sourceKey)) { OnPresentationChanged?.Invoke(); } } public void Update(float deltaTime) { currentCooldown -= deltaTime; currentCooldown = Mathf.Max(currentCooldown, 0f); } public bool IsAvailable() { bool cooldownAvailable = currentCooldown <= 0f; float energyCost = GetEffectiveEnergyCost(); bool energyAvailable = character.attributeSm[CharacterAttribute.Energy] >= energyCost || energyCost <= 0; bool available = cooldownAvailable && energyAvailable; if (!available) { if(character is Player && owner.Owner is MainWeaponBase) { if (data.shownInUI || data.isMain) { PlayerCanvas.MainWeaponUIArea.functionIconDict[data.unitName].SetFrameOutline(0.25f, Color.red); } } } return available; } public RuntimeFunctionUnit Execute() { ResetCooldown(); ConsumeEnergy(); if(character is Player && owner.Owner is MainWeaponBase) { if (data.shownInUI || data.isMain) { PlayerCanvas.MainWeaponUIArea.functionIconDict[data.unitName].SetFrameOutline(0.25f); } } return this; } private const float MaxCooldownReduction = 0.9f; private const float MaxEnergyCostReduction = 0.9f; /// 计算应用 EnergyCostReduction 后的实际能量消耗。 private float GetEffectiveEnergyCost() { float energyCostReduction = Mathf.Clamp(character.attributeSm[CharacterAttribute.EnergyCostReduction], 0f, MaxEnergyCostReduction); return data.energyCost * (1f - energyCostReduction); } private void ResetCooldown() { float cooldownReduction = Mathf.Clamp(character.attributeSm[CharacterAttribute.CooldownReduction], 0f, MaxCooldownReduction); currentCooldown = maxCooldown * (1f - cooldownReduction); } private void ConsumeEnergy() { float cost = GetEffectiveEnergyCost(); character.attributeSm[CharacterAttribute.Energy] -= cost; // onEnergyChanged 已由 AttributeSubmodule 值变更回调自动触发 } } } }