Files
2026-06-04 15:09:10 +03:00

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;
}
}