162 lines
6.2 KiB
C#
162 lines
6.2 KiB
C#
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
|
|
// Управляет режимом строительства: показывает "призрак" здания под курсором,
|
|
// проверяет ресурсы И пересечение с другими зданиями, спавнит здание в подтверждённой позиции.
|
|
|
|
public class BuildManager : MonoBehaviour
|
|
{
|
|
public static BuildManager Instance { get; private set; }
|
|
|
|
[Header("Все типы зданий в игре")]
|
|
public List<BuildingData> 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<SpriteRenderer>();
|
|
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<BuildingData> GetAvailableBuildings()
|
|
{
|
|
List<BuildingData> available = new List<BuildingData>();
|
|
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<SpriteRenderer>();
|
|
ghostSize = ghostSr != null ? ghostSr.bounds.size : Vector2.one;
|
|
|
|
// Отключаем на "призраке" всё, что мешает (коллайдер, скрипты сбора ресурсов и т.д.)
|
|
Collider2D col = ghostObject.GetComponent<Collider2D>();
|
|
if (col != null) col.enabled = false;
|
|
|
|
MonoBehaviour[] scripts = ghostObject.GetComponents<MonoBehaviour>();
|
|
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<PlacedBuildingId>();
|
|
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);
|
|
}
|
|
}
|