121 lines
3.7 KiB
C#
Raw Normal View History

2025-09-19 19:43:49 +05:00
using Cysharp.Threading.Tasks;
#if FUSION2
using Fusion;
#endif
using System;
using System.Linq;
using UnityEngine;
using BulletHellTemplate.PVP;
namespace BulletHellTemplate
{
#if FUSION2
[RequireComponent(typeof(NetworkObject))]
#endif
public class SafeZoneController :
#if FUSION2
NetworkBehaviour
#else
MonoBehaviour
#endif
{
[SerializeField] private SphereCollider sphere; // isTrigger
[SerializeField] private Transform visual;
#if FUSION2
[Networked] public Vector3 Center { get; private set; }
[Networked] public float InitialRadius { get; private set; }
[Networked] public float Radius { get; private set; }
private BattleRoyaleModeData _data;
private TickTimer _tick;
public void InitializeFrom(BattleRoyaleModeData data, Vector3 center)
{
if (!HasStateAuthority) return;
_data = data;
Center = center;
InitialRadius = data.initialRadius;
Radius = data.initialRadius;
UpdateLocalVisuals();
ShrinkRoutine().Forget();
}
public override void FixedUpdateNetwork()
{
UpdateLocalVisuals();
if (!HasStateAuthority || _data == null) return;
if (_tick.ExpiredOrNotRunning(Runner))
{
_tick = TickTimer.CreateFromSeconds(Runner, Mathf.Max(0.05f, _data.tickInterval));
ApplyDamageOutside();
}
}
private void UpdateLocalVisuals()
{
if (sphere) sphere.radius = Radius;
if (visual) visual.localScale = Vector3.one * (Radius / InitialRadius);
transform.position = Center;
}
private async UniTaskVoid ShrinkRoutine()
{
if (!HasStateAuthority || _data == null) return;
int stages = Mathf.Max(1, _data.stageDurations != null ? _data.stageDurations.Length : 0);
float current = Radius;
for (int i = 0; i < stages; i++)
{
float duration = (_data.stageDurations != null && _data.stageDurations.Length > i)
? _data.stageDurations[i]
: 30f;
float target = (_data.stageTargetRadius != null && _data.stageTargetRadius.Length > i)
? _data.stageTargetRadius[i]
: Mathf.Lerp(_data.initialRadius, _data.finalRadius, (i + 1f) / stages);
float t = 0f;
while (t < duration)
{
t += Runner.DeltaTime;
Radius = Mathf.Lerp(current, target, Mathf.Clamp01(t / duration));
await UniTask.Yield(PlayerLoopTiming.FixedUpdate);
}
current = target;
if (_data.pauseBetweenStages > 0)
await UniTask.Delay(TimeSpan.FromSeconds(_data.pauseBetweenStages));
}
}
private void ApplyDamageOutside()
{
#if UNITY_6000_0_OR_NEWER
var chars = FindObjectsByType<CharacterEntity>(FindObjectsSortMode.None);
#else
var chars = FindObjectsOfType<CharacterEntity>();
#endif
float chunk = Mathf.Max(0.01f, _data.damagePerSecondOutside) * Mathf.Max(0.05f, _data.tickInterval);
foreach (var ce in chars)
{
if (!ce || ce.IsDead) continue;
float dist = Vector3.Distance(ce.transform.position, Center);
if (dist > Radius)
{
int dmg = Mathf.CeilToInt(chunk);
ce.ReceiveDamage(dmg);
}
}
}
#endif
}
}