using UnityEngine;
using UnityEngine.InputSystem;
namespace PlatypusIdeas.AirPath.Runtime.Modes {
/// <summary>
/// Traditional mouse-click based pathfinding mode for turn-based games
/// </summary>
public class MouseClickPathfindingMode : PathfindingModeBase {
private Vector2Int _startGridPos;
private Vector2Int _endGridPos;
private bool _hasStartPoint;
private GameObject _startMarker;
private GameObject _endMarker;
// Configuration
private float _markerHeight = 2f;
private float _markerSize = 1f;
private bool _showMarkers = true;
public override string ModeName => "Mouse Click";
public override void OnActivate() {
base.OnActivate();
_hasStartPoint = false;
UpdateInstructionText("Click on the terrain to set START point");
if (_showMarkers) {
CreateMarkers();
}
}
public override void OnDeactivate() {
base.OnDeactivate();
DestroyMarkers();
}
public override void UpdateMode() {
if (!_isActive) return;
HandleMouseInput();
}
public override void Cleanup() {
base.Cleanup();
DestroyMarkers();
}
public override void DrawDebugVisualization() {
if (!_isActive) return;
// Draw markers visualization only if markers are enabled
if (_showMarkers) {
if (_hasStartPoint) {
var startWorld = GridToWorldPosition(_startGridPos, _markerHeight);
Gizmos.color = StartColor;
Gizmos.DrawWireSphere(startWorld, _markerSize);
Gizmos.DrawLine(startWorld, startWorld + Vector3.up * 5f);
}
if (_hasStartPoint && _endGridPos != _startGridPos) {
var endWorld = GridToWorldPosition(_endGridPos, _markerHeight);
Gizmos.color = EndColor;
Gizmos.DrawWireSphere(endWorld, _markerSize);
Gizmos.DrawLine(endWorld, endWorld + Vector3.up * 5f);
}
}
}
public override string GetStatusInfo() {
if (!_hasStartPoint) {
return "Waiting for start point...";
} else {
return $"Start: ({_startGridPos.x}, {_startGridPos.y}) | Click for end point";
}
}
/// <summary>
/// Set whether to show start/end markers
/// </summary>
public void SetMarkersVisibility(bool visible) {
_showMarkers = visible;
if (_showMarkers && _isActive) {
// Create markers if they don't exist
if (_startMarker == null || _endMarker == null) {
CreateMarkers();
}
// Update existing markers' visibility
if (_startMarker != null) {
_startMarker.SetActive(_hasStartPoint);
}
if (_endMarker != null) {
_endMarker.SetActive(false); // Will be shown when end point is set
}
} else {
// Hide markers
if (_startMarker != null) {
_startMarker.SetActive(false);
}
if (_endMarker != null) {
_endMarker.SetActive(false);
}
}
}
/// <summary>
/// Handles mouse input to set start and end points for pathfinding by detecting clicks on valid terrain positions.
/// </summary>
private void HandleMouseInput() {
// Check for mouse click
if (!Mouse.current.leftButton.wasPressedThisFrame) return;
var mousePos = Mouse.current.position.ReadValue();
if (GetTerrainHitFromScreenPoint(mousePos, out RaycastHit hit)) {
Vector2Int gridPos = WorldToGridPosition(hit.point);
if (IsValidGridPosition(gridPos)) {
if (!_hasStartPoint) {
SetStartPoint(gridPos);
} else {
SetEndPointAndRequestPath(gridPos);
}
}
}
}
/// <summary>
/// Sets the starting point for pathfinding on the grid.
/// </summary>
/// <param name="gridPos">The grid position to set as the start point.</param>
private void SetStartPoint(Vector2Int gridPos) {
ClearVisualization();
_startGridPos = gridPos;
_hasStartPoint = true;
var worldPos = GridToWorldPosition(_startGridPos, 0);
Debug.Log($"<color=cyan>[MouseClickMode]</color> START point set to Grid: {_startGridPos}, World: {worldPos}");
// Visualize start point if cell colors are enabled
if (Context.ShowPathCellColors) {
SetCellColor(gridPos, StartColor);
}
if (_showMarkers) {
UpdateStartMarker(gridPos);
}
UpdateInstructionText("Click on the terrain to set END point");
}
/// <summary>
/// Sets the endpoint for the pathfinding process, updates the visualization,
/// and requests a path calculation based on the provided grid position.
/// </summary>
/// <param name="gridPos">The grid position to be used as the endpoint for the pathfinding request.</param>
private void SetEndPointAndRequestPath(Vector2Int gridPos) {
_endGridPos = gridPos;
Vector2Int finalStartPos = _startGridPos;
bool usedBirdPosition = false;
Debug.Log($"<color=yellow>[MouseClickMode] === PATH REQUEST DEBUG ===</color>");
Debug.Log($"<color=yellow>[MouseClickMode]</color> Current _startGridPos: {_startGridPos}");
Debug.Log($"<color=yellow>[MouseClickMode]</color> End grid position: {_endGridPos}");
Debug.Log($"<color=yellow>[MouseClickMode]</color> IsPathActive(): {IsPathActive()}");
// Check if we should try to use bird position
bool shouldUseBirdPosition = IsPathActive();
if (shouldUseBirdPosition) {
Debug.Log($"<color=yellow>[MouseClickMode]</color> Path is active - attempting to use bird position");
// Get average bird position
var avgBirdWorldPos = GetAverageBirdPosition();
Debug.Log($"<color=yellow>[MouseClickMode]</color> GetAverageBirdPosition() returned: {avgBirdWorldPos}");
// Check if position is valid (not zero and not NaN)
if (avgBirdWorldPos != Vector3.zero &&
!float.IsNaN(avgBirdWorldPos.x) &&
!float.IsNaN(avgBirdWorldPos.y) &&
!float.IsNaN(avgBirdWorldPos.z)) {
Debug.Log($"<color=yellow>[MouseClickMode]</color> Bird position is valid, converting to grid position");
// Convert to grid position
var birdGridPos = WorldToGridPosition(avgBirdWorldPos);
Debug.Log($"<color=yellow>[MouseClickMode]</color> Bird grid position: {birdGridPos}");
// Validate grid position
if (IsValidGridPosition(birdGridPos)) {
finalStartPos = birdGridPos;
usedBirdPosition = true;
Debug.Log($"<color=green>[MouseClickMode] ✓ Using BIRD position as start: {finalStartPos}</color>");
} else {
Debug.LogWarning($"<color=red>[MouseClickMode] ✗ Bird grid position {birdGridPos} is INVALID (out of bounds)</color>");
Debug.LogWarning($"<color=red>[MouseClickMode]</color> Falling back to clicked start position: {finalStartPos}");
}
} else {
Debug.LogWarning($"<color=red>[MouseClickMode] ✗ Bird position is INVALID (zero or NaN)</color>");
Debug.LogWarning($"<color=red>[MouseClickMode]</color> Falling back to clicked start position: {finalStartPos}");
}
} else {
Debug.Log($"<color=yellow>[MouseClickMode]</color> Path NOT active - using clicked start position: {finalStartPos}");
}
// Update internal state
_startGridPos = finalStartPos;
// Convert positions to world space for logging
var finalStartWorld = GridToWorldPosition(finalStartPos, 0);
var endWorld = GridToWorldPosition(_endGridPos, 0);
Debug.Log($"<color=cyan>[MouseClickMode]</color> START: Grid {finalStartPos} → World {finalStartWorld}");
Debug.Log($"<color=cyan>[MouseClickMode]</color> END: Grid {_endGridPos} → World {endWorld}");
Debug.Log($"<color=cyan>[MouseClickMode]</color> Used bird position: {usedBirdPosition}");
ClearVisualization();
// Visualize start and end points if cell colors are enabled
if (Context.ShowPathCellColors) {
SetCellColor(finalStartPos, StartColor);
SetCellColor(_endGridPos, EndColor);
}
if (_showMarkers) {
UpdateStartMarker(finalStartPos);
UpdateEndMarker(_endGridPos);
}
// Request path calculation
RequestPath(finalStartPos, _endGridPos);
UpdateInstructionText("Path calculated! Click anywhere to set a new destination.");
}
private void CreateMarkers() {
if (_startMarker == null) {
_startMarker = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
_startMarker.name = "PathStartMarker";
_startMarker.transform.localScale = Vector3.one * _markerSize;
var startRenderer = _startMarker.GetComponent<MeshRenderer>();
if (startRenderer != null) {
startRenderer.material.color = StartColor;
}
var startCollider = _startMarker.GetComponent<Collider>();
if (startCollider != null) {
Object.Destroy(startCollider);
}
_startMarker.SetActive(false);
}
if (_endMarker == null) {
_endMarker = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
_endMarker.name = "PathEndMarker";
_endMarker.transform.localScale = Vector3.one * _markerSize;
var endRenderer = _endMarker.GetComponent<MeshRenderer>();
if (endRenderer != null) {
endRenderer.material.color = EndColor;
}
var endCollider = _endMarker.GetComponent<Collider>();
if (endCollider != null) {
Object.Destroy(endCollider);
}
_endMarker.SetActive(false);
}
}
private void UpdateStartMarker(Vector2Int gridPos) {
if (_startMarker && _showMarkers) {
var worldPos = GridToWorldPosition(gridPos, _markerHeight);
_startMarker.transform.position = worldPos;
_startMarker.SetActive(true);
}
}
private void UpdateEndMarker(Vector2Int gridPos) {
if (_endMarker && _showMarkers) {
var worldPos = GridToWorldPosition(gridPos, _markerHeight);
_endMarker.transform.position = worldPos;
_endMarker.SetActive(true);
}
}
private void DestroyMarkers() {
if (_startMarker != null) {
if (Application.isPlaying) {
Object.Destroy(_startMarker);
} else {
Object.DestroyImmediate(_startMarker);
}
_startMarker = null;
}
if (_endMarker != null) {
if (Application.isPlaying) {
Object.Destroy(_endMarker);
} else {
Object.DestroyImmediate(_endMarker);
}
_endMarker = null;
}
}
// Reset method for when switching back to this mode
public void Reset() {
_hasStartPoint = false;
_startGridPos = Vector2Int.zero;
_endGridPos = Vector2Int.zero;
ClearVisualization();
DestroyMarkers();
if (_showMarkers) {
CreateMarkers();
}
UpdateInstructionText("Click on the terrain to set START point");
}
}
}