using UnityEngine;
///
/// Компонент на игроке.
/// ЛКМ — удержание → разрушение блока с прогрессом.
/// ПКМ — мгновенная постановка блока из хотбара.
///
public class BlockPlacer : MonoBehaviour
{
[Header("Settings")]
public float reach = 5f; // дальность в Unity-единицах
public float breakTime = 1f; // секунд до разрушения
public LayerMask blockLayer; // слой чанков (назначить в Inspector)
[Header("Break Overlay")]
public Material breakMaterial; // Blocks/BlockBreak шейдер
public Texture2D crackTexture; // текстура трещин (grayscale)
// ── Приватное ─────────────────────────────────────────────────────────────
Camera _cam;
float _breakProgress;
Vector3Int _breakingBlock = Vector3Int.one * int.MinValue; // "никакой"
GameObject _breakOverlay;
MeshRenderer _overlayRenderer;
static readonly int PropProgress = Shader.PropertyToID("_Progress");
void Start()
{
_cam = Camera.main;
if (breakMaterial == null)
{
var shader = Shader.Find("Blocks/BlockBreak");
if (shader != null) breakMaterial = new Material(shader);
}
if (breakMaterial != null && crackTexture != null)
breakMaterial.SetTexture("_CrackTex", crackTexture);
}
void Update()
{
HandleBreak();
HandlePlace();
}
// ── Разрушение (ЛКМ удержание) ───────────────────────────────────────────
void HandleBreak()
{
if (!Input.GetMouseButton(0))
{
CancelBreak();
return;
}
if (!Raycast(out RaycastHit hit, out Vector3Int blockPos))
{
CancelBreak();
return;
}
// Сменился ли целевой блок?
if (blockPos != _breakingBlock)
{
CancelBreak();
_breakingBlock = blockPos;
SpawnBreakOverlay(blockPos);
}
_breakProgress += Time.deltaTime / breakTime;
UpdateOverlay(_breakProgress);
if (_breakProgress >= 1f)
{
FinishBreak(blockPos);
}
}
void FinishBreak(Vector3Int blockPos)
{
string blockId = WorldManager.Instance.BreakBlock(blockPos);
CancelBreak();
if (blockId == null) return;
// Дроп предмета
var def = BlockRegistry.Instance?.GetById(blockId);
if (def?.linkedItem != null && InventoryItemFactory.Instance != null)
{
Vector3 dropPos = new Vector3(blockPos.x + 0.5f, blockPos.y + 0.5f, blockPos.z + 0.5f);
InventoryItemFactory.Instance.CreateWorldItem(def.linkedItem, 1, dropPos);
}
}
void CancelBreak()
{
_breakProgress = 0f;
_breakingBlock = Vector3Int.one * int.MinValue;
if (_breakOverlay != null) Destroy(_breakOverlay);
_breakOverlay = null;
}
// ── Постановка (ПКМ) ─────────────────────────────────────────────────────
void HandlePlace()
{
if (!Input.GetMouseButtonDown(1)) return;
if (!Raycast(out RaycastHit hit, out Vector3Int blockPos)) return;
// Ячейка, куда ставим = сосед грани которую мы смотрим
Vector3Int placePos = blockPos + Vector3Int.RoundToInt(hit.normal);
// Не ставим блок внутри игрока
if (IsInsidePlayer(placePos)) return;
// Получаем блок из хотбара
var selectedItem = InventoryManager.Instance?.GetSelectedHotbarItem();
if (selectedItem == null) return;
var def = BlockRegistry.Instance?.GetByItemId(selectedItem.id);
if (def == null)
{
Debug.Log($"[BlockPlacer] {selectedItem.itemName} не является блоком.");
return;
}
WorldManager.Instance.PlaceBlock(placePos, def.blockId);
// Потребляем 1 из хотбара
InventoryManager.Instance.RemoveItem(selectedItem.id, 1);
}
// ── Overlay разрушения ────────────────────────────────────────────────────
void SpawnBreakOverlay(Vector3Int blockPos)
{
if (breakMaterial == null) return;
_breakOverlay = GameObject.CreatePrimitive(PrimitiveType.Cube);
_breakOverlay.name = "BreakOverlay";
_breakOverlay.transform.position = blockPos + new Vector3(0.5f, 0.5f, 0.5f);
_breakOverlay.transform.localScale = Vector3.one * 1.001f; // чуть больше, без z-fight
// Убираем коллайдер — он не нужен
Destroy(_breakOverlay.GetComponent());
_overlayRenderer = _breakOverlay.GetComponent();
_overlayRenderer.sharedMaterial = breakMaterial;
_overlayRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
}
void UpdateOverlay(float progress)
{
if (_overlayRenderer == null) return;
// MaterialPropertyBlock — не создаём новый экземпляр материала
var mpb = new MaterialPropertyBlock();
_overlayRenderer.GetPropertyBlock(mpb);
mpb.SetFloat(PropProgress, progress);
_overlayRenderer.SetPropertyBlock(mpb);
}
// ── Утилиты ───────────────────────────────────────────────────────────────
bool Raycast(out RaycastHit hit, out Vector3Int blockPos)
{
blockPos = default;
Ray ray = new Ray(_cam.transform.position, _cam.transform.forward);
if (Physics.Raycast(ray, out hit, reach, blockLayer))
{
// hit.point лежит на грани — смещаемся чуть внутрь блока
Vector3 inner = hit.point - hit.normal * 0.01f;
blockPos = WorldManager.WorldToBlock(inner);
return true;
}
return false;
}
bool IsInsidePlayer(Vector3Int blockPos)
{
// Проверяем две ячейки (ноги и голова)
Vector3Int feet = WorldManager.WorldToBlock(transform.position);
Vector3Int head = WorldManager.WorldToBlock(transform.position + Vector3.up * 1.8f);
return blockPos == feet || blockPos == head;
}
}