314 lines
7.6 KiB
C#
314 lines
7.6 KiB
C#
using System;
|
|
using UnityEngine;
|
|
|
|
[RequireComponent(typeof(SphereCollider))]
|
|
public class CaptureZone : MonoBehaviour
|
|
{
|
|
public enum Owner
|
|
{
|
|
Neutral,
|
|
Player,
|
|
Enemy
|
|
}
|
|
|
|
public enum CaptureState
|
|
{
|
|
Neutral,
|
|
CapturingByPlayer,
|
|
CapturedByPlayer,
|
|
CapturingByEnemy,
|
|
CapturedByEnemy,
|
|
Contested
|
|
}
|
|
|
|
[Header("Identity")]
|
|
public string zoneName = "Zone";
|
|
|
|
[Header("Capture")]
|
|
public Owner currentOwner = Owner.Neutral;
|
|
[Range(0f, 1f)] public float captureProgress = 0.5f;
|
|
public float captureTime = 5f;
|
|
public int playersInside;
|
|
public int enemiesInside;
|
|
public int pointsPerSecond = 5;
|
|
|
|
[Header("Visuals")]
|
|
public Renderer zoneRenderer;
|
|
public Color neutralColor = new Color(0.55f, 0.55f, 0.55f, 0.45f);
|
|
public Color playerColor = new Color(0.1f, 0.35f, 1f, 0.55f);
|
|
public Color enemyColor = new Color(1f, 0.12f, 0.08f, 0.55f);
|
|
public Color contestedColor = new Color(1f, 0.82f, 0.05f, 0.65f);
|
|
|
|
public event Action<CaptureZone> onCapturedByPlayer;
|
|
public event Action<CaptureZone> onCapturedByEnemy;
|
|
|
|
private Owner lastNotifiedOwner;
|
|
private Vector3 baseScale;
|
|
private float pulseTimer;
|
|
private float scoreTimer;
|
|
|
|
private void Awake()
|
|
{
|
|
SphereCollider trigger = GetComponent<SphereCollider>();
|
|
trigger.isTrigger = true;
|
|
|
|
lastNotifiedOwner = currentOwner;
|
|
baseScale = transform.localScale;
|
|
ApplyOwnerProgress(currentOwner);
|
|
UpdateVisual();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
UpdateCaptureProgress();
|
|
TickScoreIncome();
|
|
UpdateVisual();
|
|
UpdateCapturePulse();
|
|
}
|
|
|
|
private void OnTriggerEnter(Collider other)
|
|
{
|
|
if (other.CompareTag("Player"))
|
|
{
|
|
playersInside++;
|
|
}
|
|
else if (other.CompareTag("Enemy"))
|
|
{
|
|
enemiesInside++;
|
|
}
|
|
}
|
|
|
|
private void OnTriggerExit(Collider other)
|
|
{
|
|
if (other.CompareTag("Player"))
|
|
{
|
|
playersInside = Mathf.Max(0, playersInside - 1);
|
|
}
|
|
else if (other.CompareTag("Enemy"))
|
|
{
|
|
enemiesInside = Mathf.Max(0, enemiesInside - 1);
|
|
}
|
|
}
|
|
|
|
public float GetProgress()
|
|
{
|
|
return captureProgress;
|
|
}
|
|
|
|
public Owner GetOwner()
|
|
{
|
|
return currentOwner;
|
|
}
|
|
|
|
public string GetStateLabel()
|
|
{
|
|
switch (GetCaptureState())
|
|
{
|
|
case CaptureState.CapturingByPlayer:
|
|
return "Capturing by Player";
|
|
case CaptureState.CapturedByPlayer:
|
|
return "Player";
|
|
case CaptureState.CapturingByEnemy:
|
|
return "Capturing by Enemy";
|
|
case CaptureState.CapturedByEnemy:
|
|
return "Enemy";
|
|
case CaptureState.Contested:
|
|
return "Contested";
|
|
default:
|
|
return "Neutral";
|
|
}
|
|
}
|
|
|
|
public CaptureState GetCaptureState()
|
|
{
|
|
bool hasPlayer = playersInside > 0;
|
|
bool hasEnemy = enemiesInside > 0;
|
|
|
|
if (hasPlayer && hasEnemy)
|
|
{
|
|
return CaptureState.Contested;
|
|
}
|
|
|
|
if (hasPlayer && currentOwner != Owner.Player)
|
|
{
|
|
return CaptureState.CapturingByPlayer;
|
|
}
|
|
|
|
if (hasEnemy && currentOwner != Owner.Enemy)
|
|
{
|
|
return CaptureState.CapturingByEnemy;
|
|
}
|
|
|
|
if (currentOwner == Owner.Player)
|
|
{
|
|
return CaptureState.CapturedByPlayer;
|
|
}
|
|
|
|
if (currentOwner == Owner.Enemy)
|
|
{
|
|
return CaptureState.CapturedByEnemy;
|
|
}
|
|
|
|
return CaptureState.Neutral;
|
|
}
|
|
|
|
public void ForceSetOwner(Owner owner)
|
|
{
|
|
SetOwner(owner, true);
|
|
}
|
|
|
|
private void UpdateCaptureProgress()
|
|
{
|
|
bool hasPlayer = playersInside > 0;
|
|
bool hasEnemy = enemiesInside > 0;
|
|
|
|
// Спорная зона замораживает прогресс, чтобы игрок видел механику contest.
|
|
if (hasPlayer == hasEnemy)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float step = Time.deltaTime / Mathf.Max(0.1f, captureTime);
|
|
|
|
if (hasPlayer)
|
|
{
|
|
captureProgress = Mathf.MoveTowards(captureProgress, 1f, step);
|
|
if (Mathf.Approximately(captureProgress, 1f))
|
|
{
|
|
SetOwner(Owner.Player, false);
|
|
}
|
|
}
|
|
else if (hasEnemy)
|
|
{
|
|
captureProgress = Mathf.MoveTowards(captureProgress, 0f, step);
|
|
if (Mathf.Approximately(captureProgress, 0f))
|
|
{
|
|
SetOwner(Owner.Enemy, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void TickScoreIncome()
|
|
{
|
|
if (currentOwner == Owner.Neutral || ResourceManager.Instance == null)
|
|
{
|
|
scoreTimer = 0f;
|
|
return;
|
|
}
|
|
|
|
scoreTimer += Time.deltaTime;
|
|
if (scoreTimer < 1f)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int ticks = Mathf.FloorToInt(scoreTimer);
|
|
scoreTimer -= ticks;
|
|
int amount = pointsPerSecond * ticks;
|
|
|
|
if (currentOwner == Owner.Player)
|
|
{
|
|
ResourceManager.Instance.AddPlayerPoints(amount);
|
|
}
|
|
else if (currentOwner == Owner.Enemy)
|
|
{
|
|
ResourceManager.Instance.AddEnemyPoints(amount);
|
|
}
|
|
}
|
|
|
|
private void SetOwner(Owner owner, bool forceNotify)
|
|
{
|
|
if (currentOwner == owner && !forceNotify)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Owner previousOwner = currentOwner;
|
|
currentOwner = owner;
|
|
ApplyOwnerProgress(owner);
|
|
pulseTimer = 0.25f;
|
|
|
|
if (previousOwner == owner && !forceNotify)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (lastNotifiedOwner != currentOwner || forceNotify)
|
|
{
|
|
lastNotifiedOwner = currentOwner;
|
|
|
|
if (currentOwner == Owner.Player)
|
|
{
|
|
onCapturedByPlayer?.Invoke(this);
|
|
}
|
|
else if (currentOwner == Owner.Enemy)
|
|
{
|
|
onCapturedByEnemy?.Invoke(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ApplyOwnerProgress(Owner owner)
|
|
{
|
|
if (owner == Owner.Player)
|
|
{
|
|
captureProgress = 1f;
|
|
}
|
|
else if (owner == Owner.Enemy)
|
|
{
|
|
captureProgress = 0f;
|
|
}
|
|
else if (Mathf.Approximately(captureProgress, 0f) || Mathf.Approximately(captureProgress, 1f))
|
|
{
|
|
captureProgress = 0.5f;
|
|
}
|
|
}
|
|
|
|
private void UpdateVisual()
|
|
{
|
|
if (zoneRenderer == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Color color = GetVisualColor();
|
|
zoneRenderer.material.color = color;
|
|
}
|
|
|
|
private Color GetVisualColor()
|
|
{
|
|
CaptureState state = GetCaptureState();
|
|
if (state == CaptureState.Contested || state == CaptureState.CapturingByPlayer || state == CaptureState.CapturingByEnemy)
|
|
{
|
|
float pulse = Mathf.Lerp(0.75f, 1f, Mathf.PingPong(Time.time * 3f, 1f));
|
|
return contestedColor * pulse;
|
|
}
|
|
|
|
if (currentOwner == Owner.Player)
|
|
{
|
|
return playerColor;
|
|
}
|
|
|
|
if (currentOwner == Owner.Enemy)
|
|
{
|
|
return enemyColor;
|
|
}
|
|
|
|
return neutralColor;
|
|
}
|
|
|
|
private void UpdateCapturePulse()
|
|
{
|
|
if (pulseTimer <= 0f)
|
|
{
|
|
transform.localScale = baseScale;
|
|
return;
|
|
}
|
|
|
|
pulseTimer -= Time.deltaTime;
|
|
float t = Mathf.Clamp01(pulseTimer / 0.25f);
|
|
float scale = 1f + Mathf.Sin(t * Mathf.PI) * 0.12f;
|
|
transform.localScale = baseScale * scale;
|
|
}
|
|
}
|