using UnityEngine; using System.Collections.Generic; // Управляет режимом строительства: показывает "призрак" здания под курсором, // проверяет ресурсы И пересечение с другими зданиями, спавнит здание в подтверждённой позиции. public class BuildManager : MonoBehaviour { public static BuildManager Instance { get; private set; } [Header("Все типы зданий в игре")] public List allBuildings; [Header("Проверка пересечения")] [Tooltip("Слой, на котором находятся коллайдеры уже построенных зданий")] public LayerMask buildingsLayerMask; [Tooltip("Небольшой отступ вокруг здания, чтобы дома не лепились впритык друг к другу")] public float placementPadding = 0.1f; [Header("Визуал 'призрака' здания при размещении")] [Tooltip("Белая зона -- можно строить")] public Color validPlacementColor = new Color(1f, 1f, 1f, 0.6f); [Tooltip("Красная зона -- нельзя (либо не хватает ресурсов, либо место занято)")] public Color invalidPlacementColor = new Color(1f, 0.2f, 0.2f, 0.6f); private BuildingData currentlyPlacing; private GameObject ghostObject; private Vector2 ghostSize; private Camera mainCam; void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; mainCam = Camera.main; } void Update() { if (currentlyPlacing == null) return; // не в режиме строительства -- ничего не делаем // Двигаем "призрак" здания за пальцем/мышкой Vector3 worldPos = GetPointerWorldPosition(); worldPos.z = 0; ghostObject.transform.position = worldPos; bool canAfford = currentlyPlacing.CanAfford(); bool overlapping = IsOverlapping(worldPos); bool canPlace = canAfford && !overlapping; SpriteRenderer sr = ghostObject.GetComponent(); if (sr != null) sr.color = canPlace ? validPlacementColor : invalidPlacementColor; // Подтверждение размещения по клику/тапу if (Input.GetMouseButtonDown(0)) { TryConfirmPlacement(worldPos, canAfford, overlapping); } // Отмена по правой кнопке / Escape if (Input.GetMouseButtonDown(1) || Input.GetKeyDown(KeyCode.Escape)) { CancelPlacement(); } } // Проверка: пересекается ли здание в этой позиции с уже существующими постройками bool IsOverlapping(Vector3 position) { Vector2 checkSize = ghostSize + new Vector2(placementPadding, placementPadding); Collider2D hit = Physics2D.OverlapBox(position, checkSize, 0f, buildingsLayerMask); return hit != null; } // Список зданий, доступных для стройки ПРЯМО СЕЙЧАС (по уровню Ратуши) public List GetAvailableBuildings() { List available = new List(); int townHallLevel = TownHall.Instance != null ? TownHall.Instance.currentLevel : 1; foreach (var b in allBuildings) { if (b.requiredTownHallLevel <= townHallLevel) available.Add(b); } return available; } // Вызывается из UI-кнопки конкретного здания в меню стройки public void StartPlacing(BuildingData data) { if (currentlyPlacing != null) { CancelPlacement(); } currentlyPlacing = data; ghostObject = Instantiate(data.prefab); // Замеряем размер здания по спрайту (работает даже если коллайдер потом выключим) SpriteRenderer ghostSr = ghostObject.GetComponent(); ghostSize = ghostSr != null ? ghostSr.bounds.size : Vector2.one; // Отключаем на "призраке" всё, что мешает (коллайдер, скрипты сбора ресурсов и т.д.) Collider2D col = ghostObject.GetComponent(); if (col != null) col.enabled = false; MonoBehaviour[] scripts = ghostObject.GetComponents(); foreach (var s in scripts) { s.enabled = false; } } void TryConfirmPlacement(Vector3 position, bool canAfford, bool overlapping) { if (overlapping) { Debug.Log("[СТРОЙКА] Здесь уже что-то построено!"); return; } if (!canAfford) { Debug.Log("[СТРОЙКА] Не хватает ресурсов!"); return; } currentlyPlacing.PayCost(); GameObject built = Instantiate(currentlyPlacing.prefab, position, Quaternion.identity); // Ставим маркер, чтобы SaveManager потом знал, что это за здание при сохранении PlacedBuildingId idMarker = built.AddComponent(); idMarker.buildingName = currentlyPlacing.buildingName; Debug.Log($"[СТРОЙКА] Построено: {currentlyPlacing.buildingName} в {position}"); Destroy(ghostObject); currentlyPlacing = null; } void CancelPlacement() { if (ghostObject != null) Destroy(ghostObject); currentlyPlacing = null; Debug.Log("[СТРОЙКА] Размещение отменено."); } Vector3 GetPointerWorldPosition() { Vector3 screenPos = Input.mousePosition; return mainCam.ScreenToWorldPoint(screenPos); } }