Files
Cielonos/Assets/Scripts/MainGame/Items/Submodules/FunctionSubmodule.cs
SoulliesOfficial b047eb5e6d 引雷标记extender
2026-07-26 09:42:03 -04:00

177 lines
6.0 KiB
C#

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<ItemBase>
{
[ReadOnly]
public Dictionary<string, RuntimeFunctionUnit> 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<string, RuntimeFunctionUnit>();
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<FunctionSubmodule>
{
private CharacterBase character => owner.Owner.player;
public FunctionData.FunctionUnit data;
public float currentCooldown;
public float maxCooldown;
private Dictionary<string, HashSet<string>> _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<string> 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<string, HashSet<string>>();
if (!_runtimeTagsBySource.TryGetValue(sourceKey, out HashSet<string> tags))
{
tags = new HashSet<string>();
_runtimeTagsBySource.Add(sourceKey, tags);
}
changed = tags.Add(tag);
}
else if (_runtimeTagsBySource != null &&
_runtimeTagsBySource.TryGetValue(sourceKey, out HashSet<string> 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;
/// <summary> 计算应用 EnergyCostReduction 后的实际能量消耗。 </summary>
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 值变更回调自动触发
}
}
}
}