299 lines
12 KiB
C#
Raw Permalink Normal View History

2025-09-19 19:43:49 +05:00
using BulletHellTemplate.Core.Events;
using Cysharp.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
namespace BulletHellTemplate
{
/// <summary>
/// Handles temporary or passive characterBuffsComponent that modify character characterStatsComponent.
/// </summary>
public partial class CharacterBuffsComponent : MonoBehaviour
{
private CharacterStatsComponent characterStatsComponent;
private CharacterControllerComponent characterControllerComponent;
private CharacterEntity characterOwner;
// Active buffs and debuffs tracking
private readonly List<ActiveBuff> activeBuffs = new();
private readonly List<ActiveBuff> activeDebuffs = new();
private void Awake()
{
characterStatsComponent = GetComponent<CharacterStatsComponent>();
characterControllerComponent = GetComponent<CharacterControllerComponent>();
characterOwner = GetComponent<CharacterEntity>();
}
public void Initialize()
{
if (characterStatsComponent == null) characterStatsComponent = GetComponent<CharacterStatsComponent>();
if (characterControllerComponent == null) characterControllerComponent = GetComponent<CharacterControllerComponent>();
if (characterOwner == null) characterOwner = GetComponent<CharacterEntity>();
}
#region Move Speed
/// <summary>
/// Applies a temporary move speed buff (increases speed).
/// </summary>
public void ApplyMoveSpeedBuff(float amount, float duration)
{
float buffAmount = Mathf.Abs(amount);
string id = Guid.NewGuid().ToString();
characterStatsComponent.AlterMoveSpeed(buffAmount);
RemoveModifierAfterDelayAsync(id, StatType.MoveSpeed, BuffCategory.Buff, buffAmount, duration, this.GetCancellationTokenOnDestroy()).Forget();
activeBuffs.Add(new ActiveBuff(id, StatType.MoveSpeed, BuffCategory.Buff, buffAmount, duration));
EventBus.Publish(new BuffReceivedEvent(characterOwner, StatType.MoveSpeed, buffAmount, duration, activeBuffs.Count));
}
/// <summary>
/// Applies a temporary move speed debuff (decreases speed).
/// </summary>
public void ApplyMoveSpeedDebuff(float amount, float duration)
{
float debuffAmount = -Mathf.Abs(amount);
string id = Guid.NewGuid().ToString();
characterStatsComponent.AlterMoveSpeed(debuffAmount);
RemoveModifierAfterDelayAsync(id, StatType.MoveSpeed, BuffCategory.Debuff, debuffAmount, duration, this.GetCancellationTokenOnDestroy()).Forget();
activeDebuffs.Add(new ActiveBuff(id, StatType.MoveSpeed, BuffCategory.Debuff, debuffAmount, duration));
EventBus.Publish(new DebuffReceivedEvent(characterOwner, StatType.MoveSpeed, debuffAmount, duration, activeDebuffs.Count));
}
#endregion
#region Attack Speed
/// <summary>
/// Applies a temporary attack speed buff (increases attack speed).
/// </summary>
public void ApplyAttackSpeedBuff(float amount, float duration)
{
float buffAmount = Mathf.Abs(amount);
string id = Guid.NewGuid().ToString();
characterStatsComponent.AlterAttackSpeed(buffAmount);
RemoveModifierAfterDelayAsync(id, StatType.AttackSpeed, BuffCategory.Buff, buffAmount, duration, this.GetCancellationTokenOnDestroy()).Forget();
activeBuffs.Add(new ActiveBuff(id, StatType.AttackSpeed, BuffCategory.Buff, buffAmount, duration));
EventBus.Publish(new BuffReceivedEvent(characterOwner, StatType.AttackSpeed, buffAmount, duration, activeBuffs.Count));
}
/// <summary>
/// Applies a temporary attack speed debuff (decreases attack speed).
/// </summary>
public void ApplyAttackSpeedDebuff(float amount, float duration)
{
float debuffAmount = -Mathf.Abs(amount);
string id = Guid.NewGuid().ToString();
characterStatsComponent.AlterAttackSpeed(debuffAmount);
RemoveModifierAfterDelayAsync(id, StatType.AttackSpeed, BuffCategory.Debuff, debuffAmount, duration, this.GetCancellationTokenOnDestroy()).Forget();
activeDebuffs.Add(new ActiveBuff(id, StatType.AttackSpeed, BuffCategory.Debuff, debuffAmount, duration));
EventBus.Publish(new DebuffReceivedEvent(characterOwner, StatType.AttackSpeed, debuffAmount, duration, activeDebuffs.Count));
}
#endregion
#region Defense
/// <summary>
/// Applies a temporary defense buff (increases defense).
/// </summary>
public void ApplyDefenseBuff(float amount, float duration)
{
float buffAmount = Mathf.Abs(amount);
string id = Guid.NewGuid().ToString();
characterStatsComponent.AlterDefense(buffAmount);
RemoveModifierAfterDelayAsync(id, StatType.Defense, BuffCategory.Buff, buffAmount, duration, this.GetCancellationTokenOnDestroy()).Forget();
activeBuffs.Add(new ActiveBuff(id, StatType.Defense, BuffCategory.Buff, buffAmount, duration));
EventBus.Publish(new BuffReceivedEvent(characterOwner, StatType.Defense, buffAmount, duration, activeBuffs.Count));
}
/// <summary>
/// Applies a temporary defense debuff (decreases defense).
/// </summary>
public void ApplyDefenseDebuff(float amount, float duration)
{
float debuffAmount = -Mathf.Abs(amount);
string id = Guid.NewGuid().ToString();
characterStatsComponent.AlterDefense(debuffAmount);
RemoveModifierAfterDelayAsync(id, StatType.Defense, BuffCategory.Debuff, debuffAmount, duration, this.GetCancellationTokenOnDestroy()).Forget();
activeDebuffs.Add(new ActiveBuff(id, StatType.Defense, BuffCategory.Debuff, debuffAmount, duration));
EventBus.Publish(new DebuffReceivedEvent(characterOwner, StatType.Defense, debuffAmount, duration, activeDebuffs.Count));
}
#endregion
#region Damage
/// <summary>
/// Applies a temporary damage buff (increases damage).
/// </summary>
public void ApplyDamageBuff(float amount, float duration)
{
float buffAmount = Mathf.Abs(amount);
string id = Guid.NewGuid().ToString();
characterStatsComponent.AlterDamage(buffAmount);
RemoveModifierAfterDelayAsync(id, StatType.Damage, BuffCategory.Buff, buffAmount, duration, this.GetCancellationTokenOnDestroy()).Forget();
activeBuffs.Add(new ActiveBuff(id, StatType.Damage, BuffCategory.Buff, buffAmount, duration));
EventBus.Publish(new BuffReceivedEvent(characterOwner, StatType.Damage, buffAmount, duration, activeBuffs.Count));
}
/// <summary>
/// Applies a temporary damage debuff (decreases damage).
/// </summary>
public void ApplyDamageDebuff(float amount, float duration)
{
float debuffAmount = -Mathf.Abs(amount);
string id = Guid.NewGuid().ToString();
characterStatsComponent.AlterDamage(debuffAmount);
RemoveModifierAfterDelayAsync(id, StatType.Damage, BuffCategory.Debuff, debuffAmount, duration, this.GetCancellationTokenOnDestroy()).Forget();
activeDebuffs.Add(new ActiveBuff(id, StatType.Damage, BuffCategory.Debuff, debuffAmount, duration));
EventBus.Publish(new DebuffReceivedEvent(characterOwner, StatType.Damage, debuffAmount, duration, activeDebuffs.Count));
}
#endregion
#region Shield
/// <summary>
/// Applies shield to the character. If temporary, acts as a buff and reverts after duration.
/// </summary>
public void ApplyShield(int amount, float duration = 0f, bool isTemporary = false)
{
if (amount <= 0 || characterStatsComponent == null)
return;
characterStatsComponent.AddShield(amount);
if (isTemporary && duration > 0f)
{
string id = Guid.NewGuid().ToString();
StatType statType = StatType.Shield;
BuffCategory buffCategory = BuffCategory.Buff;
RemoveShieldAfterDelayAsync(id, statType, buffCategory, amount, duration, this.GetCancellationTokenOnDestroy()).Forget();
activeBuffs.Add(new ActiveBuff(id, statType, buffCategory, amount, duration));
int remainingBuffsCount = activeBuffs.Count;
EventBus.Publish(new BuffReceivedEvent(characterOwner, statType, amount, duration, remainingBuffsCount));
}
}
/// <summary>
/// Async task that waits for the specified duration and then removes the shield buff.
/// </summary>
private async UniTask RemoveShieldAfterDelayAsync(
string id,
StatType statType,
BuffCategory buffCategory,
int amount,
float duration,
CancellationToken token = default)
{
float elapsed = 0f;
while (elapsed < duration)
{
token.ThrowIfCancellationRequested();
if (GameplayManager.Singleton.IsPaused())
{
await UniTask.WaitWhile(() => GameplayManager.Singleton.IsPaused(), cancellationToken: token);
}
elapsed += Time.deltaTime;
await UniTask.Yield(PlayerLoopTiming.Update, token);
}
characterStatsComponent.RemoveShield(amount);
activeBuffs.RemoveAll(b => b.Id == id);
int remainingBuffsCount = activeBuffs.Count;
EventBus.Publish(new BuffRemovedEvent(characterOwner, statType, remainingBuffsCount));
}
#endregion
public void ApplyHealHp(int amount)
{
amount = Mathf.Abs(amount);
characterStatsComponent.HealHP(amount);
}
/// <summary>
/// Waits for a duration (while handling pause) and then removes the stat modifier.
/// </summary>
/// <param name="id">Unique ID of the modifier.</param>
/// <param name="statType">Stat affected by the modifier.</param>
/// <param name="buffCategory">Type of modifier (Buff or Debuff).</param>
/// <param name="amount">Value applied by the modifier.</param>
/// <param name="duration">Duration in seconds before removal.</param>
/// <param name="token">Optional cancellation token.</param>
private async UniTask RemoveModifierAfterDelayAsync(
string id, StatType statType, BuffCategory buffCategory, float amount, float duration, CancellationToken token = default)
{
float elapsed = 0f;
while (elapsed < duration)
{
token.ThrowIfCancellationRequested();
if (GameplayManager.Singleton.IsPaused())
await UniTask.WaitWhile(() => GameplayManager.Singleton.IsPaused(), cancellationToken: token);
elapsed += Time.deltaTime;
await UniTask.Yield(PlayerLoopTiming.Update, token);
}
RemoveModifier(statType, amount);
if (buffCategory == BuffCategory.Buff)
{
activeBuffs.RemoveAll(b => b.Id == id);
int remaining = activeBuffs.Count;
EventBus.Publish(new BuffRemovedEvent(characterOwner, statType, remaining));
}
else
{
activeDebuffs.RemoveAll(b => b.Id == id);
int remaining = activeDebuffs.Count;
EventBus.Publish(new DebuffRemovedEvent(characterOwner, statType, remaining));
}
}
public void RemoveModifier(StatType statType, float amount)
{
switch (statType)
{
case StatType.MoveSpeed:
characterStatsComponent.AlterMoveSpeed(-amount);
break;
case StatType.AttackSpeed:
characterStatsComponent.AlterAttackSpeed(-amount);
break;
case StatType.Damage:
characterStatsComponent.AlterDamage(-amount);
break;
case StatType.Defense:
characterStatsComponent.AlterDefense(-amount);
break;
}
}
}
}