215 lines
6.3 KiB
C#
215 lines
6.3 KiB
C#
#if FUSION2
|
|
using Fusion;
|
|
#endif
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.UI;
|
|
using System.Collections.Generic;
|
|
using BulletHellTemplate.VFX;
|
|
|
|
namespace BulletHellTemplate
|
|
{
|
|
#if FUSION2
|
|
[RequireComponent(typeof(NetworkObject))]
|
|
public class BoxEntity : NetworkBehaviour
|
|
#else
|
|
public class BoxEntity : MonoBehaviour
|
|
#endif
|
|
{
|
|
[Header("HP")]
|
|
public int maxHP = 100;
|
|
private int currentHP;
|
|
|
|
[System.Serializable] public class DropChance { public DropEntity drop; public int chance; }
|
|
[Header("Drops")]
|
|
public List<DropChance> dropChances;
|
|
public int hpValue, xpValue, goldValue, shieldValue;
|
|
|
|
[Header("UI (igual monstro)")]
|
|
public GameObject hpContainer;
|
|
public Image hpBar, hpBarDecrease;
|
|
public float decreaseDelay = 0.12f, decreaseSpeed = 1f;
|
|
private Coroutine hpAnimCo;
|
|
|
|
[Header("UnityEvents")]
|
|
public UnityEvent OnEnableEvent, OnDisableEvent;
|
|
[System.Serializable] public class IntEvent : UnityEvent<int> { }
|
|
public IntEvent OnReceiveHit;
|
|
|
|
#if FUSION2
|
|
[Networked, OnChangedRender(nameof(OnNetHpChanged))] private int NetHp { get; set; }
|
|
#endif
|
|
|
|
void Start()
|
|
{
|
|
currentHP = Mathf.Max(1, maxHP);
|
|
#if FUSION2
|
|
if (Object && HasStateAuthority) NetHp = currentHP;
|
|
#endif
|
|
SetBothBars(1f);
|
|
if (hpContainer) hpContainer.SetActive(false);
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
GameplayManager.Singleton?.ActiveBoxesList.Add(transform);
|
|
OnEnableEvent?.Invoke();
|
|
}
|
|
void OnDisable()
|
|
{
|
|
GameplayManager.Singleton?.ActiveBoxesList.Remove(transform);
|
|
OnDisableEvent?.Invoke();
|
|
}
|
|
|
|
public void ReceiveDamage(int damage)
|
|
{
|
|
if (damage <= 0) return;
|
|
|
|
#if FUSION2
|
|
if (Runner && Runner.IsRunning && Object && !HasStateAuthority)
|
|
{
|
|
RPC_RequestDamage(damage); // pede para a autoridade aplicar
|
|
return;
|
|
}
|
|
#endif
|
|
ApplyDamageInternal(damage);
|
|
#if FUSION2
|
|
if (Runner && Runner.IsRunning && Object && HasStateAuthority)
|
|
NetHp = currentHP;
|
|
#endif
|
|
}
|
|
|
|
#if FUSION2
|
|
[Rpc(RpcSources.All, RpcTargets.StateAuthority)]
|
|
private void RPC_RequestDamage(int damage)
|
|
{
|
|
ApplyDamageInternal(damage);
|
|
NetHp = currentHP;
|
|
}
|
|
|
|
private void OnNetHpChanged()
|
|
{
|
|
LocalApplyHp(currentHP, NetHp);
|
|
currentHP = NetHp;
|
|
}
|
|
#endif
|
|
|
|
private void ApplyDamageInternal(int damage)
|
|
{
|
|
int old = currentHP;
|
|
currentHP = Mathf.Clamp(currentHP - damage, 0, maxHP);
|
|
OnReceiveHit?.Invoke(damage);
|
|
LocalApplyHp(old, currentHP);
|
|
if (currentHP <= 0) DestroyBox();
|
|
}
|
|
|
|
private void LocalApplyHp(int oldValue, int newValue)
|
|
{
|
|
float n = maxHP > 0 ? (float)newValue / maxHP : 0f;
|
|
|
|
if (hpContainer && newValue < maxHP)
|
|
hpContainer.SetActive(true);
|
|
|
|
if (hpBar) hpBar.fillAmount = n;
|
|
if (newValue < oldValue)
|
|
{
|
|
if (hpAnimCo != null) StopCoroutine(hpAnimCo);
|
|
hpAnimCo = StartCoroutine(DecreaseBarSmooth(n));
|
|
}
|
|
else SetBothBars(n);
|
|
}
|
|
private System.Collections.IEnumerator DecreaseBarSmooth(float target)
|
|
{
|
|
yield return new WaitForSeconds(decreaseDelay);
|
|
while (hpBarDecrease && hpBarDecrease.fillAmount > target)
|
|
{
|
|
hpBarDecrease.fillAmount -= Time.deltaTime * decreaseSpeed;
|
|
yield return null;
|
|
}
|
|
if (hpBarDecrease) hpBarDecrease.fillAmount = target;
|
|
hpAnimCo = null;
|
|
}
|
|
private void SetBothBars(float n)
|
|
{
|
|
if (hpBar) hpBar.fillAmount = n;
|
|
if (hpBarDecrease) hpBarDecrease.fillAmount = n;
|
|
}
|
|
|
|
private bool NetActive =>
|
|
#if FUSION2
|
|
Runner && Runner.IsRunning;
|
|
#else
|
|
false;
|
|
#endif
|
|
|
|
private void DestroyBox()
|
|
{
|
|
if (TryPickDrop(out int idx))
|
|
{
|
|
var entry = dropChances[idx];
|
|
if (entry?.drop)
|
|
{
|
|
var type = entry.drop.type;
|
|
int amount = ResolveValueByType(type);
|
|
|
|
#if FUSION2
|
|
if (NetActive && HasStateAuthority)
|
|
{
|
|
RPC_SpawnDrop(idx, amount, transform.position);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
var drop = DropPool.Spawn(entry.drop, transform.position);
|
|
drop.SetValue(amount);
|
|
}
|
|
}
|
|
}
|
|
|
|
Destroy(gameObject);
|
|
}
|
|
|
|
private bool TryPickDrop(out int index)
|
|
{
|
|
index = -1;
|
|
if (dropChances == null || dropChances.Count == 0) return false;
|
|
|
|
int total = 0;
|
|
foreach (var c in dropChances) if (c?.drop) total += Mathf.Max(0, c.chance);
|
|
if (total <= 0) return false;
|
|
|
|
int pick = Random.Range(0, total), acc = 0;
|
|
for (int i = 0; i < dropChances.Count; i++)
|
|
{
|
|
var c = dropChances[i];
|
|
if (c == null || c.drop == null || c.chance <= 0) continue;
|
|
acc += c.chance;
|
|
if (pick < acc) { index = i; return true; }
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private int ResolveValueByType(DropEntity.DropType t) => t switch
|
|
{
|
|
DropEntity.DropType.Gold => goldValue,
|
|
DropEntity.DropType.Experience => xpValue,
|
|
DropEntity.DropType.Health => hpValue,
|
|
DropEntity.DropType.Shield => shieldValue,
|
|
_ => 0
|
|
};
|
|
|
|
#if FUSION2
|
|
[Rpc(RpcSources.StateAuthority, RpcTargets.All)]
|
|
private void RPC_SpawnDrop(int dropIndex, int amount, Vector3 pos)
|
|
{
|
|
if (dropIndex < 0 || dropIndex >= dropChances.Count) return;
|
|
var entry = dropChances[dropIndex];
|
|
if (!entry?.drop) return;
|
|
|
|
var drop = DropPool.Spawn(entry.drop, pos);
|
|
drop.SetValue(amount);
|
|
}
|
|
#endif
|
|
}
|
|
}
|