Files
Northbound/Assets/Northbound/Gameplay/World/PortalSite.cs
T

201 lines
6.7 KiB
C#

using Northbound.Core;
using Northbound.Gameplay.Buildings;
using Northbound.Gameplay.Player;
using UnityEngine;
namespace Northbound.Gameplay.World
{
public enum PortalState
{
Enemy,
Ruined,
Allied
}
public sealed class PortalSite : MonoBehaviour, IDamageable, IInteractable, IEnemyStructureTarget
{
[SerializeField] private int networkIndex;
[SerializeField] private PortalState state = PortalState.Enemy;
[SerializeField] private BuildingDefinition buildingDefinition;
[SerializeField] private float rebuildManaCost = 40f;
[SerializeField] private float rebuildActionDuration = 0.5f;
[SerializeField] private bool allowsMovementWhileRebuilding;
[SerializeField] private bool canCancelRebuilding = true;
[SerializeField] private float travelActionDuration = 0.25f;
[SerializeField] private bool allowsMovementWhileTraveling;
[SerializeField] private bool canCancelTraveling;
[SerializeField] private Renderer stateRenderer;
[SerializeField] private Material enemyMaterial;
[SerializeField] private Material ruinedMaterial;
[SerializeField] private Material alliedMaterial;
public int NetworkIndex => networkIndex;
public PortalState State => state;
public Team Team => state == PortalState.Enemy ? Team.Enemy : Team.Player;
public bool IsAlive => state != PortalState.Ruined;
public bool CanInteract => state != PortalState.Enemy;
public float CurrentHealth { get; private set; }
public float MaximumHealth => buildingDefinition == null ? 1f : buildingDefinition.MaximumHealth;
public bool CanBeTargetedByEnemies => state == PortalState.Allied && IsAlive;
public IDamageable DamageReceiver => this;
private void Awake()
{
CurrentHealth = MaximumHealth;
RefreshVisual();
}
private void Start()
{
SharedGameState.Instance?.Register(this);
}
public void Configure(
int index,
PortalState initialState,
BuildingDefinition definition,
Renderer renderer,
Material enemy,
Material ruined,
Material allied)
{
networkIndex = index;
state = initialState;
buildingDefinition = definition;
stateRenderer = renderer;
enemyMaterial = enemy;
ruinedMaterial = ruined;
alliedMaterial = allied;
CurrentHealth = MaximumHealth;
RefreshVisual();
}
public void TakeDamage(float amount, Team sourceTeam)
{
var playerAttackingEnemyPortal = state == PortalState.Enemy && sourceTeam == Team.Player;
var enemyAttackingAlliedPortal = state == PortalState.Allied && sourceTeam == Team.Enemy;
if (!playerAttackingEnemyPortal && !enemyAttackingAlliedPortal)
{
return;
}
CurrentHealth = Mathf.Max(0f, CurrentHealth - Mathf.Max(0f, amount));
if (CurrentHealth > 0f)
{
return;
}
state = PortalState.Ruined;
RefreshVisual();
var gameState = SharedGameState.Instance;
if (playerAttackingEnemyPortal && gameState != null && gameState.CountEnemyPortals() == 0)
{
gameState.MarkVictory();
}
}
public string GetInteractionPrompt()
{
if (state == PortalState.Ruined)
{
return $"Rebuild portal ({rebuildManaCost:0} mana)";
}
return "Travel through adjacent portal";
}
public PlayerActionDefinition GetActionDefinition()
{
return state == PortalState.Ruined
? new PlayerActionDefinition("Rebuild Portal", rebuildActionDuration, allowsMovementWhileRebuilding, canCancelRebuilding)
: new PlayerActionDefinition("Travel Portal", travelActionDuration, allowsMovementWhileTraveling, canCancelTraveling);
}
public bool CanStartInteraction(GameObject actor)
{
var gameState = SharedGameState.Instance;
if (state == PortalState.Ruined)
{
return gameState != null && gameState.Mana + 0.001f >= rebuildManaCost;
}
if (state != PortalState.Allied || !actor.TryGetComponent<PlayerController>(out var player))
{
return false;
}
var direction = player.LastMoveDirection.z >= 0f ? 1 : -1;
var destination = gameState?.GetPortal(networkIndex + direction);
return destination != null && destination.State == PortalState.Allied;
}
public bool Interact(GameObject actor)
{
if (state == PortalState.Ruined)
{
return Rebuild();
}
if (state != PortalState.Allied || !actor.TryGetComponent<PlayerController>(out var player))
{
return false;
}
var direction = player.LastMoveDirection.z >= 0f ? 1 : -1;
var destination = SharedGameState.Instance?.GetPortal(networkIndex + direction);
if (destination == null || destination.State != PortalState.Allied)
{
return false;
}
var controller = actor.GetComponent<CharacterController>();
if (controller != null)
{
controller.enabled = false;
}
actor.transform.position = destination.transform.position + Vector3.forward * (direction * 2.5f) + Vector3.up;
if (controller != null)
{
controller.enabled = true;
}
return true;
}
private bool Rebuild()
{
var gameState = SharedGameState.Instance;
if (gameState == null || !gameState.TrySpendMana(rebuildManaCost))
{
return false;
}
state = PortalState.Allied;
CurrentHealth = MaximumHealth;
RefreshVisual();
return true;
}
private void RefreshVisual()
{
if (stateRenderer == null)
{
return;
}
stateRenderer.sharedMaterial = state switch
{
PortalState.Enemy => enemyMaterial,
PortalState.Allied => alliedMaterial,
_ => ruinedMaterial
};
transform.localScale = state == PortalState.Ruined
? new Vector3(1f, 0.28f, 1f)
: Vector3.one;
}
}
}