第一批重构,主要是物理迁移脚本和部分命名空间的调整
- 结构上迁移文件,将脚本分成 Base/Runtime/Presentation/Procedure/Editor 五大文件夹 - 调整 DataTable 和 Definition 下脚本的命名空间 - 依据 UI-5层架构设计规范 拆分原先的 UI 到 Runtime 和 Presentation 下
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using SepCore.Definition;
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class AbsorbComponent : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private float _absorbRangeBase = 3f;
|
||||
[SerializeField] private float _absorbSpeed = 15f;
|
||||
[SerializeField] private float _collectDistance = 0.35f;
|
||||
[SerializeField] private int _maxDetectCount = 64;
|
||||
[SerializeField] private LayerMask _detectLayerMask = ~0;
|
||||
|
||||
private Player _player;
|
||||
private Transform _target;
|
||||
private StatComponent _statComponent;
|
||||
private Collider[] _buffer;
|
||||
|
||||
private StatProperty _absorbRangeStat;
|
||||
private Action<StatModifier, bool> _absorbRangeStatCallback;
|
||||
|
||||
private float AbsorbRange => Mathf.Max(0f, (_absorbRangeBase + _absorbRangeStat.Value) * _absorbRangeStat.Percent);
|
||||
|
||||
public void OnInit(Player player, StatComponent statComponent = null)
|
||||
{
|
||||
_player = player;
|
||||
_target = player != null ? player.CachedTransform : null;
|
||||
_statComponent = statComponent;
|
||||
_buffer = new Collider[Mathf.Max(1, _maxDetectCount)];
|
||||
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_absorbRangeStat = _statComponent.GetStat(StatType.AbsorbRange);
|
||||
_absorbRangeStatCallback = (modifier, isApply) =>
|
||||
_statComponent.UpdateStat(_absorbRangeStat, modifier, isApply);
|
||||
_statComponent.Subscribe(StatType.AbsorbRange, _absorbRangeStatCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
_absorbRangeStat = new StatProperty();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
if (_player == null || !_player.Available || _target == null || _buffer == null) return;
|
||||
|
||||
int hitCount = Physics.OverlapSphereNonAlloc(
|
||||
_target.position,
|
||||
AbsorbRange,
|
||||
_buffer,
|
||||
_detectLayerMask,
|
||||
QueryTriggerInteraction.Collide
|
||||
);
|
||||
|
||||
for (int i = 0; i < hitCount; i++)
|
||||
{
|
||||
Collider hit = _buffer[i];
|
||||
if (hit == null) continue;
|
||||
|
||||
CoinEntity coin = hit.GetComponent<CoinEntity>();
|
||||
if (coin != null && coin.Available)
|
||||
{
|
||||
AbsorbDrop(coin.CachedTransform, elapseSeconds);
|
||||
coin.TryCollect(_player, _collectDistance);
|
||||
continue;
|
||||
}
|
||||
|
||||
ExpEntity exp = hit.GetComponent<ExpEntity>();
|
||||
if (exp != null && exp.Available)
|
||||
{
|
||||
AbsorbDrop(exp.CachedTransform, elapseSeconds);
|
||||
exp.TryCollect(_player, _collectDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_statComponent.Unsubscribe(StatType.AbsorbRange, _absorbRangeStatCallback);
|
||||
_absorbRangeStatCallback = null;
|
||||
}
|
||||
|
||||
_statComponent = null;
|
||||
_target = null;
|
||||
_player = null;
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
private void AbsorbDrop(Transform drop, float deltaTime)
|
||||
{
|
||||
if (drop == null || _target == null) return;
|
||||
drop.position = Vector3.MoveTowards(drop.position, _target.position, _absorbSpeed * deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 46f46350fed64d8389ebb4ae72b65796
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Entity;
|
||||
using Entity.Weapon;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class AttackComponent : MonoBehaviour
|
||||
{
|
||||
private bool _isAttacking = false;
|
||||
private List<WeaponBase> _weapons;
|
||||
|
||||
public void SetIsAttacking(bool isAttacking) => _isAttacking = isAttacking;
|
||||
|
||||
public void OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapsedTime, float realElapseSeconds)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2b35b5ae4185cb4cab2d84b0569db3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,104 @@
|
||||
using System.Collections.Generic;
|
||||
using SepCore.Definition;
|
||||
using Entity.Weapon;
|
||||
using Entity;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class BackpackComponent : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// 武器槽容量,由DRRole决定。
|
||||
/// </summary>
|
||||
private int _weaponCapacity;
|
||||
|
||||
/// <summary>
|
||||
/// 玩家类引用。
|
||||
/// </summary>
|
||||
private Player _player;
|
||||
|
||||
/// <summary>
|
||||
/// 武器列表。
|
||||
/// </summary>
|
||||
private List<WeaponBase> _weapons;
|
||||
|
||||
/// <summary>
|
||||
/// 道具列表。
|
||||
/// </summary>
|
||||
private List<PropItem> _props;
|
||||
|
||||
/// <summary>
|
||||
/// 玩家状态组件引用,用于道具效果的应用。
|
||||
/// </summary>
|
||||
private StatComponent _statComponent;
|
||||
|
||||
/// <summary>
|
||||
/// 武器装备槽,在 Editor 中进行配置,表示武器在世界中的显示位置。
|
||||
/// </summary>
|
||||
[SerializeField] private Transform[] _weaponSlots;
|
||||
|
||||
public IReadOnlyList<WeaponBase> Weapons => _weapons;
|
||||
public IReadOnlyList<PropItem> Props => _props;
|
||||
public int WeaponCapacity => _weaponCapacity;
|
||||
|
||||
public void OnInit(Player player, int weaponCapacity)
|
||||
{
|
||||
_weaponCapacity = weaponCapacity;
|
||||
_player = player;
|
||||
|
||||
_weapons = new List<WeaponBase>(_weaponCapacity);
|
||||
_props = new List<PropItem>();
|
||||
_statComponent = GetComponent<StatComponent>();
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_weapons.Clear();
|
||||
_props.Clear();
|
||||
}
|
||||
|
||||
public bool AttachWeapon(WeaponBase weapon)
|
||||
{
|
||||
if (_weapons.Count == _weaponCapacity) return false;
|
||||
GameEntry.Entity.AttachEntity(weapon.Id, _player.Id, _weaponSlots[_weapons.Count]);
|
||||
_weapons.Add(weapon);
|
||||
weapon.SetEnabled(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DetachWeapon(WeaponBase weapon)
|
||||
{
|
||||
bool result = _weapons.Remove(weapon);
|
||||
if (result)
|
||||
{
|
||||
GameEntry.Entity.DetachEntity(weapon.Id, _player.Id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool AttachProp(PropItem prop)
|
||||
{
|
||||
_props.Add(prop);
|
||||
prop.OnAttach(_statComponent);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DetachProp(PropItem prop)
|
||||
{
|
||||
_props.Remove(prop);
|
||||
prop.OnDetach(_statComponent);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetWeaponState(bool state)
|
||||
{
|
||||
if (_weapons == null) return;
|
||||
foreach (var weapon in _weapons)
|
||||
{
|
||||
weapon.SetEnabled(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2980af28a40c84247a688f6b684dd4c3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using CustomEvent;
|
||||
using SepCore.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class HealthComponent : MonoBehaviour
|
||||
{
|
||||
private int _maxHealthBase;
|
||||
private int _currentHealth;
|
||||
private bool _isDead => _currentHealth <= 0;
|
||||
|
||||
private StatComponent _statComponent;
|
||||
|
||||
private StatProperty _maxHealthStat;
|
||||
private Action<StatModifier, bool> _maxHealthStatCallback;
|
||||
public StatProperty MaxHealthStat => _maxHealthStat;
|
||||
|
||||
private StatProperty _defenseStat;
|
||||
private Action<StatModifier, bool> _defenseStatCallback;
|
||||
public StatProperty DefenseStat => _defenseStat;
|
||||
|
||||
private StatProperty _dodgeStat;
|
||||
private Action<StatModifier, bool> _dodgeStatCallback;
|
||||
public StatProperty DodgeStat => _dodgeStat;
|
||||
|
||||
private bool _isPlayer => _statComponent != null;
|
||||
|
||||
public float HealthRatio => MaxHealth > 0 ? (float)CurrentHealth / MaxHealth : 0f;
|
||||
|
||||
public int MaxHealth => Mathf.CeilToInt((_maxHealthBase + _maxHealthStat.Value) * _maxHealthStat.Percent);
|
||||
|
||||
public int CurrentHealth
|
||||
{
|
||||
get => _currentHealth;
|
||||
set => _currentHealth = value;
|
||||
}
|
||||
|
||||
#region FSM
|
||||
|
||||
public void OnInit(int maxHealth, StatComponent statComponent = null)
|
||||
{
|
||||
_maxHealthBase = maxHealth;
|
||||
_currentHealth = maxHealth;
|
||||
_statComponent = statComponent;
|
||||
|
||||
_statComponent = statComponent;
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_maxHealthStat = _statComponent.GetStat(StatType.MaxHealth);
|
||||
_maxHealthStatCallback = (modifier, isApply) =>
|
||||
{
|
||||
_statComponent.UpdateStat(_maxHealthStat, modifier, isApply);
|
||||
GameEntry.Event.Fire(this, PlayerHealthChangeEventArgs.Create(0, CurrentHealth, MaxHealth));
|
||||
};
|
||||
_statComponent.Subscribe(StatType.MaxHealth, _maxHealthStatCallback);
|
||||
|
||||
_defenseStat = _statComponent.GetStat(StatType.Defense);
|
||||
_defenseStatCallback = (modifier, isApply) =>
|
||||
_statComponent.UpdateStat(_defenseStat, modifier, isApply);
|
||||
_statComponent.Subscribe(StatType.Defense, _defenseStatCallback);
|
||||
|
||||
_dodgeStat = _statComponent.GetStat(StatType.Dodge);
|
||||
_dodgeStatCallback = (modifier, isApply) =>
|
||||
_statComponent.UpdateStat(_dodgeStat, modifier, isApply);
|
||||
_statComponent.Subscribe(StatType.Dodge, _dodgeStatCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
_maxHealthStat = new StatProperty();
|
||||
_defenseStat = new StatProperty();
|
||||
_dodgeStat = new StatProperty();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_statComponent.Unsubscribe(StatType.MaxHealth, _maxHealthStatCallback);
|
||||
_maxHealthStatCallback = null;
|
||||
|
||||
_statComponent.Unsubscribe(StatType.Defense, _defenseStatCallback);
|
||||
_defenseStatCallback = null;
|
||||
|
||||
_statComponent.Unsubscribe(StatType.Dodge, _dodgeStatCallback);
|
||||
_dodgeStatCallback = null;
|
||||
}
|
||||
|
||||
_statComponent = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public void TakeDamage(int damage)
|
||||
{
|
||||
if (_isDead) return;
|
||||
|
||||
if (_dodgeStat.Value > UnityEngine.Random.value)
|
||||
{
|
||||
damage = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int defenseValue = (int)_defenseStat.Value;
|
||||
float defensePercent = _defenseStat.Percent;
|
||||
damage -= defenseValue;
|
||||
damage = Mathf.CeilToInt(damage / defensePercent);
|
||||
}
|
||||
|
||||
_currentHealth -= damage;
|
||||
CauseDamage(damage);
|
||||
if (_isPlayer)
|
||||
GameEntry.Event.Fire(this, PlayerHealthChangeEventArgs.Create(damage, CurrentHealth, MaxHealth));
|
||||
}
|
||||
|
||||
private void CauseDamage(int damage)
|
||||
{
|
||||
if (damage <= 0 || GameEntry.DamageText == null) return;
|
||||
GameEntry.DamageText.ShowDamage(transform.position + Vector3.up * 1.1f, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce4964a40efccaa4fb22df63713b567b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class InputComponent : MonoBehaviour
|
||||
{
|
||||
private PlayerInputActions _inputActions;
|
||||
[SerializeField] private bool _isListening = false;
|
||||
|
||||
[SerializeField] private Vector3 _direction = Vector3.zero;
|
||||
public Vector3 Direction => _direction;
|
||||
|
||||
public void OnInit()
|
||||
{
|
||||
_inputActions = new();
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
if (_isListening && _inputActions != null)
|
||||
{
|
||||
ReadInput();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_inputActions = null;
|
||||
_isListening = false;
|
||||
}
|
||||
|
||||
private void ReadInput()
|
||||
{
|
||||
Vector2 rawDir = _inputActions.Game.Move.ReadValue<Vector2>();
|
||||
_direction = new Vector3(rawDir.x, 0, rawDir.y);
|
||||
}
|
||||
|
||||
public void SetListening(bool isListening)
|
||||
{
|
||||
_isListening = isListening;
|
||||
if (_isListening)
|
||||
{
|
||||
_inputActions.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputActions.Disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f754cc5f339dcfa4aaec3aa32260c4b8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using SepCore.Definition;
|
||||
using Entity;
|
||||
using Simulation;
|
||||
using UnityEngine;
|
||||
using CustomDebugger;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class MovementComponent : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private bool _isMoving;
|
||||
[SerializeField] private Vector3 _direction;
|
||||
[SerializeField] private Transform _cachedTransform;
|
||||
[SerializeField] private bool _avoidEnemyOverlap;
|
||||
[SerializeField] private float _enemyBodyRadius = 0.45f;
|
||||
[SerializeField] private int _separationIterations = 2;
|
||||
|
||||
public float Speed => (_speedBase + _movementStat.Value) * _movementStat.Percent;
|
||||
public bool AvoidEnemyOverlap => _avoidEnemyOverlap;
|
||||
public float EnemyBodyRadius => _enemyBodyRadius;
|
||||
public int SeparationIterations => _separationIterations;
|
||||
public bool IsMoving => _isMoving;
|
||||
public Vector3 Direction => _direction;
|
||||
public Transform CachedTransform => _cachedTransform;
|
||||
[SerializeField] private float _speedBase;
|
||||
|
||||
private StatComponent _statComponent;
|
||||
|
||||
private StatProperty _movementStat;
|
||||
private Action<StatModifier, bool> _movementStatCallback;
|
||||
|
||||
public void OnInit(float speed, Transform target, StatComponent statComponent = null,
|
||||
bool avoidEnemyOverlap = false, float enemyBodyRadius = 0.45f, int separationIterations = 2)
|
||||
{
|
||||
_speedBase = speed;
|
||||
_cachedTransform = target;
|
||||
_direction = Vector3.forward;
|
||||
_avoidEnemyOverlap = avoidEnemyOverlap;
|
||||
_enemyBodyRadius = Mathf.Max(0.01f, enemyBodyRadius);
|
||||
_separationIterations = Mathf.Max(1, separationIterations);
|
||||
|
||||
_statComponent = statComponent;
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_movementStat = _statComponent.GetStat(StatType.MovementSpeed);
|
||||
_movementStatCallback = (modifier, isApply) =>
|
||||
{
|
||||
_statComponent.UpdateStat(_movementStat, modifier, isApply);
|
||||
SyncToSimulationWorld();
|
||||
};
|
||||
_statComponent.Subscribe(StatType.MovementSpeed, _movementStatCallback);
|
||||
}
|
||||
else
|
||||
{
|
||||
_movementStat = new StatProperty();
|
||||
}
|
||||
|
||||
SyncToSimulationWorld();
|
||||
}
|
||||
|
||||
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||
{
|
||||
SyncToSimulationWorld();
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
Transform transformToUnregister = _cachedTransform;
|
||||
|
||||
_speedBase = 0;
|
||||
_cachedTransform = null;
|
||||
_direction = Vector3.zero;
|
||||
_isMoving = false;
|
||||
_avoidEnemyOverlap = false;
|
||||
_enemyBodyRadius = 0.45f;
|
||||
_separationIterations = 2;
|
||||
|
||||
if (_statComponent != null)
|
||||
{
|
||||
_statComponent.Unsubscribe(StatType.MovementSpeed, _movementStatCallback);
|
||||
_movementStatCallback = null;
|
||||
}
|
||||
|
||||
_statComponent = null;
|
||||
|
||||
if (transformToUnregister != null)
|
||||
{
|
||||
var simulationWorld = GameEntry.SimulationWorld;
|
||||
simulationWorld?.UnregisterPlayerMovement(transformToUnregister);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetMove(bool isMoving)
|
||||
{
|
||||
_isMoving = isMoving;
|
||||
SyncToSimulationWorld();
|
||||
}
|
||||
|
||||
public void SetDirection(Vector3 direction)
|
||||
{
|
||||
_direction = direction;
|
||||
SyncToSimulationWorld();
|
||||
}
|
||||
|
||||
private void SyncToSimulationWorld()
|
||||
{
|
||||
using (CustomProfilerMarker.Movement_Update.Auto())
|
||||
{
|
||||
if (_cachedTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SimulationWorld simulationWorld = GameEntry.SimulationWorld;
|
||||
if (simulationWorld == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 direction = _direction;
|
||||
direction.y = 0f;
|
||||
if (direction.sqrMagnitude > Mathf.Epsilon)
|
||||
{
|
||||
direction.Normalize();
|
||||
}
|
||||
|
||||
if (TryGetComponent(out Player _))
|
||||
{
|
||||
simulationWorld.SyncPlayerMovementInput(_cachedTransform, _isMoving, direction, Speed);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryGetComponent(out EnemyBase enemy))
|
||||
{
|
||||
simulationWorld.SyncEnemyMovementInput(enemy.Id, _isMoving, direction, Speed,
|
||||
_avoidEnemyOverlap, _enemyBodyRadius, _separationIterations);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9baf591df06b9bb44ba5313457ba84b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SepCore.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
public class StatComponent : MonoBehaviour
|
||||
{
|
||||
private Dictionary<StatType, List<StatModifier>> _statModifiers;
|
||||
private Dictionary<StatType, Action<StatModifier, bool>> _statModifierCallbacks;
|
||||
|
||||
public void OnInit()
|
||||
{
|
||||
_statModifiers = new Dictionary<StatType, List<StatModifier>>();
|
||||
_statModifierCallbacks = new Dictionary<StatType, Action<StatModifier, bool>>();
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_statModifiers.Clear();
|
||||
_statModifierCallbacks.Clear();
|
||||
}
|
||||
|
||||
public void AddModifier(StatModifier modifier)
|
||||
{
|
||||
if (!_statModifiers.TryGetValue(modifier.StatType, out var modifiers))
|
||||
{
|
||||
_statModifiers.Add(modifier.StatType, modifiers = new List<StatModifier>());
|
||||
modifiers.Add(modifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
modifiers.Add(modifier);
|
||||
}
|
||||
|
||||
if (_statModifierCallbacks.TryGetValue(modifier.StatType, out var callbacks))
|
||||
callbacks?.Invoke(modifier, true);
|
||||
}
|
||||
|
||||
public void RemoveModifier(StatModifier modifier)
|
||||
{
|
||||
if (_statModifiers.TryGetValue(modifier.StatType, out var modifiers))
|
||||
{
|
||||
_statModifiers[modifier.StatType].Remove(modifier);
|
||||
}
|
||||
|
||||
if (_statModifierCallbacks.TryGetValue(modifier.StatType, out var callbacks))
|
||||
callbacks?.Invoke(modifier, false);
|
||||
}
|
||||
|
||||
public void Subscribe(StatType statType, Action<StatModifier, bool> callback)
|
||||
{
|
||||
if (!_statModifierCallbacks.TryAdd(statType, callback))
|
||||
{
|
||||
_statModifierCallbacks[statType] += callback;
|
||||
}
|
||||
}
|
||||
|
||||
public void Unsubscribe(StatType statType, Action<StatModifier, bool> callback)
|
||||
{
|
||||
if (!_statModifierCallbacks.TryGetValue(statType, out var callbacks))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
callbacks -= callback;
|
||||
if (callbacks == null)
|
||||
{
|
||||
_statModifierCallbacks.Remove(statType);
|
||||
}
|
||||
}
|
||||
|
||||
public StatProperty GetStat(StatType statType)
|
||||
{
|
||||
var result = new StatProperty();
|
||||
|
||||
if (!_statModifiers.TryGetValue(statType, out var modifiers)) return result;
|
||||
|
||||
foreach (var modifier in modifiers)
|
||||
{
|
||||
if (modifier.IsPercent) result.Percent += modifier.Value;
|
||||
else result.Value += modifier.Value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void UpdateStat(StatProperty stat, StatModifier modifier, bool isApply)
|
||||
{
|
||||
float coef = isApply ? 1f : -1f;
|
||||
if (modifier.IsPercent)
|
||||
{
|
||||
stat.Percent += modifier.Value * coef;
|
||||
}
|
||||
else
|
||||
{
|
||||
stat.Value += modifier.Value * coef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5befb68803e86e248873416ce662184b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user