251 lines
8.9 KiB
C#
251 lines
8.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Sirenix.OdinInspector;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using VOID.Generic.Dict;
|
|
using VOID.Generic.Objects.Abstract.Events;
|
|
using VOID.Generic.Objects.Unit;
|
|
using VOID.Generic.Objects.Unit.Events;
|
|
using VOID.Generic.Player.Input;
|
|
using VOID.Generic.Player.Util;
|
|
using VOID.UserInterface;
|
|
using VOID.Generic.Util;
|
|
|
|
namespace VOID.Generic.Player
|
|
{
|
|
[DefaultExecutionOrder(-1)]
|
|
public class PathVisualizer : MonoBehaviour
|
|
{
|
|
public static PathVisualizer current { get; private set; }
|
|
|
|
// Configuration
|
|
[SerializeField] private float redrawTimer = 0.1f;
|
|
[SerializeField] private float lineSize = 0.05f;
|
|
[SerializeField] private float verticalOffset = 0.1f;
|
|
[SerializeField, Required] private Material reachableMaterial;
|
|
[SerializeField, Required] private Material unreachableMaterial;
|
|
[SerializeField, Required] private Material dangerousMaterial;
|
|
[SerializeField, Required] private Material neutralMaterial;
|
|
|
|
// References
|
|
private UnitObject _activeUnit;
|
|
private readonly List<LineRenderer> _pathLineRenderers = new();
|
|
|
|
private bool _autoRedraw;
|
|
private float _lastRedrawTimer;
|
|
|
|
private void Awake()
|
|
{
|
|
current = this;
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
PlayerController.current.OnActiveUnitChange += OnActiveUnitChange;
|
|
PlayerInputManager.current.OnForceMoveStart += ShouldAutoRedraw;
|
|
PlayerInputManager.current.OnForceMoveEnd += ShouldAutoRedraw;
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (_autoRedraw == false) return;
|
|
if (GameUI.current.isCursorOverUI)
|
|
{
|
|
HidePath();
|
|
return;
|
|
}
|
|
|
|
_lastRedrawTimer += Time.fixedDeltaTime;
|
|
if (_lastRedrawTimer < redrawTimer) return;
|
|
_lastRedrawTimer = 0f;
|
|
|
|
_activeUnit.unitNavAgent.CalculatePathTo(SelectUtil.SelectPosition());
|
|
UpdatePath(_activeUnit.unitNavAgent.GetCorrectedPath().ToArray());
|
|
}
|
|
|
|
private void ShouldAutoRedraw()
|
|
{
|
|
// No player's active unit!
|
|
if (!_activeUnit)
|
|
{
|
|
_autoRedraw = false;
|
|
return;
|
|
}
|
|
|
|
// Unit is moving on drawn path - don't draw another one
|
|
if (_activeUnit.unitState.isMoving)
|
|
{
|
|
_autoRedraw = false;
|
|
return;
|
|
}
|
|
|
|
// Is something forcing path drawing?
|
|
var isForcedMove = PlayerInputManager.current.isForceMove;
|
|
var isActiveUnitTurn = _activeUnit.basicState.turnManager && _activeUnit.basicState.turnManager.currentUnitTurn == _activeUnit;
|
|
if (!isForcedMove && !isActiveUnitTurn)
|
|
{
|
|
_autoRedraw = false;
|
|
HidePath();
|
|
return;
|
|
}
|
|
|
|
// Is unit casting ability?
|
|
if (_activeUnit.unitState.isCasting) {
|
|
_autoRedraw = false;
|
|
HidePath();
|
|
return;
|
|
}
|
|
|
|
_autoRedraw = true;
|
|
}
|
|
|
|
private void AddLineRenderers(int needed)
|
|
{
|
|
// Add missing LineRenderers to current poll
|
|
for (var i = 0; i < needed - _pathLineRenderers.Count; i++)
|
|
{
|
|
var lineRendererGameObject = new GameObject($"Path Renderer - part {_pathLineRenderers.Count+1}");
|
|
lineRendererGameObject.transform.parent = gameObject.transform;
|
|
var lineRenderer = lineRendererGameObject.AddComponent<LineRenderer>();
|
|
lineRenderer.enabled = false;
|
|
lineRenderer.startWidth = lineSize;
|
|
lineRenderer.endWidth = lineSize;
|
|
lineRenderer.shadowCastingMode = ShadowCastingMode.Off;
|
|
lineRenderer.numCapVertices = 3;
|
|
lineRenderer.numCornerVertices = 3;
|
|
|
|
_pathLineRenderers.Add(lineRenderer);
|
|
}
|
|
}
|
|
|
|
private void UpdateLineRenderers(List<PathType> pathTypes, List<Vector3[]> paths)
|
|
{
|
|
AddLineRenderers(paths.Count);
|
|
|
|
for (var i = 0; i < paths.Count; i++)
|
|
{
|
|
var lineRenderer = _pathLineRenderers[i];
|
|
var pathType = pathTypes[i];
|
|
var path = paths[i];
|
|
var material = pathType switch
|
|
{
|
|
PathType.Reachable => reachableMaterial,
|
|
PathType.UnReachable => unreachableMaterial,
|
|
PathType.Dangerous => dangerousMaterial,
|
|
PathType.Neutral => neutralMaterial,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(pathType), pathType, null)
|
|
};
|
|
|
|
var offsetPath = path.Select(point => point + new Vector3(0, verticalOffset, 0)).ToArray();
|
|
lineRenderer.positionCount = path.Length;
|
|
lineRenderer.SetPositions(offsetPath);
|
|
lineRenderer.sharedMaterial = material;
|
|
lineRenderer.enabled = true;
|
|
}
|
|
}
|
|
|
|
private void OnActiveUnitChange(UnitObject unitObject)
|
|
{
|
|
if (_activeUnit)
|
|
{
|
|
_activeUnit.unitEvents.onMoveStart.RemoveListener(OnUnitMoveStart);
|
|
_activeUnit.unitEvents.onMoveEnd.RemoveListener(OnUnitMoveEnd);
|
|
_activeUnit.basicEvents.onTurnSelfStart.RemoveListener(OnTurnSelfStart);
|
|
_activeUnit.basicEvents.onTurnSelfEnd.RemoveListener(OnTurnSelfEnd);
|
|
_activeUnit.basicEvents.onTurnQueueEnter.RemoveListener(OnTurnQueueEnter);
|
|
_activeUnit.basicEvents.onTurnQueueLeave.RemoveListener(OnTurnQueueLeave);
|
|
_activeUnit.unitEvents.onStartCasting.RemoveListener(OnStartCasting);
|
|
_activeUnit.unitEvents.onEndCasting.RemoveListener(OnEndCasting);
|
|
}
|
|
HidePath();
|
|
ShouldAutoRedraw();
|
|
_activeUnit = unitObject;
|
|
_activeUnit.unitEvents.onMoveStart.AddListener(OnUnitMoveStart);
|
|
_activeUnit.unitEvents.onMoveEnd.AddListener(OnUnitMoveEnd);
|
|
_activeUnit.basicEvents.onTurnSelfStart.AddListener(OnTurnSelfStart);
|
|
_activeUnit.basicEvents.onTurnSelfEnd.AddListener(OnTurnSelfEnd);
|
|
_activeUnit.basicEvents.onTurnQueueEnter.AddListener(OnTurnQueueEnter);
|
|
_activeUnit.basicEvents.onTurnQueueLeave.AddListener(OnTurnQueueLeave);
|
|
_activeUnit.unitEvents.onStartCasting.AddListener(OnStartCasting);
|
|
_activeUnit.unitEvents.onEndCasting.AddListener(OnEndCasting);
|
|
}
|
|
|
|
private void OnUnitMoveStart(MoveEvent moveEvent)
|
|
{
|
|
_autoRedraw = false;
|
|
UpdatePath(moveEvent.pathCorrected.path.ToArray());
|
|
}
|
|
|
|
private void OnUnitMoveEnd(MoveEvent moveEvent)
|
|
{
|
|
HidePath();
|
|
ShouldAutoRedraw();
|
|
}
|
|
|
|
private void OnTurnSelfStart(TurnEvent turnEvent)
|
|
{
|
|
ShouldAutoRedraw();
|
|
}
|
|
|
|
private void OnTurnSelfEnd(TurnEvent turnEvent)
|
|
{
|
|
ShouldAutoRedraw();
|
|
}
|
|
|
|
private void OnTurnQueueEnter(TurnEvent turnEvent)
|
|
{
|
|
ShouldAutoRedraw();
|
|
}
|
|
|
|
private void OnTurnQueueLeave(TurnEvent turnEvent)
|
|
{
|
|
ShouldAutoRedraw();
|
|
}
|
|
|
|
private void OnStartCasting(CastingEvent castingEvent) {
|
|
ShouldAutoRedraw();
|
|
}
|
|
|
|
private void OnEndCasting(CastingEvent castingEvent) {
|
|
ShouldAutoRedraw();
|
|
}
|
|
|
|
private void UpdatePath(Vector3[] path)
|
|
{
|
|
HidePath();
|
|
|
|
Vector3[] reachablePath;
|
|
Vector3[] unreachablePath;
|
|
|
|
if (_activeUnit.basicState.turnManager)
|
|
{
|
|
// Inside combat - some part of path can be unreachable (not enough move points)
|
|
var movePoints = _activeUnit.unitData.currentResources.movePoints;
|
|
NavMeshPathUtil.SplitPath(path, movePoints, out reachablePath, out unreachablePath);
|
|
}
|
|
else
|
|
{
|
|
// Outside combat - whole path is reachable
|
|
reachablePath = path;
|
|
unreachablePath = null;
|
|
}
|
|
|
|
NavMeshPathUtil.SplitReachablePath(reachablePath, out var pathTypes, out var paths);
|
|
|
|
if (unreachablePath is not null)
|
|
{
|
|
pathTypes.Add(PathType.UnReachable);
|
|
paths.Add(unreachablePath);
|
|
}
|
|
|
|
UpdateLineRenderers(pathTypes, paths);
|
|
}
|
|
|
|
private void HidePath()
|
|
{
|
|
_pathLineRenderers.ForEach(lineRenderer => lineRenderer.enabled = false);
|
|
}
|
|
}
|
|
} |