AStarHeightmapGrid / Assets / PlatypusIdeas / AirPath / Runtime / Modes / MouseClickpathfingMode.cs
MouseClickpathfingMode.cs
Raw
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");
        }
    }
}