You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
4.5 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Assertions;
namespace Unity.BossRoom.Gameplay.GameplayObjects
{
/// <summary>
/// ServerEnemyPortal is a stationary dungeon element that spawns monsters when a player is
/// nearby. It has one or more "breakable bits". When all the breakable elements are broken,
/// the portal becomes dormant for a fixed amount of time, before repairing its breakables
/// and starting up again.
///
/// The actual monster-spawning logic is managed by a ServerWaveSpawner component.
/// </summary>
/// <remarks>
/// The ServerEnemyPortal also has its own NetworkBreakableState. This is for the graphics of
/// the portal itself (a glowy visual effect stops when Broken, turns back on when unbroken)
/// </remarks>
[RequireComponent(typeof(ServerWaveSpawner))]
public class EnemyPortal : NetworkBehaviour, ITargetable
{
[SerializeField]
[Tooltip("Portal becomes dormant when ALL of these breakables are broken")]
public List<Breakable> m_BreakableElements;
[SerializeField]
[Tooltip("When all breakable elements are broken, wait this long before respawning them (and reactivating)")]
float m_DormantCooldown;
[SerializeField]
Breakable m_Breakable;
public bool IsNpc { get { return true; } }
public bool IsValidTarget { get { return !m_Breakable.IsBroken.Value; } }
// cached reference to our components
[SerializeField]
ServerWaveSpawner m_WaveSpawner;
// currently active "wait X seconds and then restart" coroutine
Coroutine m_CoroDormant;
public override void OnNetworkSpawn()
{
if (!IsServer)
{
enabled = false;
return;
}
foreach (var breakable in m_BreakableElements)
{
breakable.IsBroken.OnValueChanged += OnBreakableBroken;
}
MaintainState();
}
public override void OnNetworkDespawn()
{
if (m_CoroDormant != null)
StopCoroutine(m_CoroDormant);
foreach (var breakable in m_BreakableElements)
{
if (breakable)
breakable.IsBroken.OnValueChanged -= OnBreakableBroken;
}
}
private void OnBreakableBroken(bool wasBroken, bool isBroken)
{
if (!wasBroken && isBroken)
MaintainState();
}
private void MaintainState()
{
bool hasUnbrokenBreakables = false;
foreach (var breakable in m_BreakableElements)
{
if (breakable && !breakable.IsBroken.Value)
{
hasUnbrokenBreakables = true;
break;
}
}
m_Breakable.IsBroken.Value = !hasUnbrokenBreakables;
m_WaveSpawner.SetSpawnerEnabled(hasUnbrokenBreakables);
if (!hasUnbrokenBreakables && m_CoroDormant == null)
{
m_CoroDormant = StartCoroutine(CoroGoDormantAndThenRestart());
}
}
IEnumerator CoroGoDormantAndThenRestart()
{
yield return new WaitForSeconds(m_DormantCooldown);
Restart();
}
void Restart()
{
foreach (var state in m_BreakableElements)
{
if (state)
{
var serverComponent = state.GetComponent<Breakable>();
Assert.IsNotNull(serverComponent);
serverComponent.Unbreak();
}
}
m_Breakable.IsBroken.Value = false;
m_WaveSpawner.SetSpawnerEnabled(true);
m_CoroDormant = null;
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD
public void ForceRestart()
{
if (m_CoroDormant != null)
{
StopCoroutine(m_CoroDormant);
}
Restart();
}
public void ForceDestroy()
{
foreach (var state in m_BreakableElements)
{
if (state)
{
var serverComponent = state.GetComponent<Breakable>();
Assert.IsNotNull(serverComponent);
serverComponent.ReceiveHP(null, Int32.MinValue);
}
}
}
#endif
}
}