using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.Tilemaps;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.Rendering.Universal;
// ============================================================
// 1. ГЛАВНЫЙ КОНТРОЛЛЕР
// ============================================================
public class GameController : MonoBehaviour
{
public static GameController Instance;
[Header("Мир")]
public int worldWidth = 120;
public int worldHeight = 80;
public int seed = 42;
public Tilemap groundTilemap;
public Tilemap wallTilemap;
public Tilemap decorationTilemap;
public Tilemap waterTilemap;
public TileBase[] tiles;
[Header("Игрок")]
public GameObject playerPrefab;
public Transform playerSpawn;
[Header("UI")]
public InventoryUI inventoryUI;
public QuestUI questUI;
public CraftingUI craftingUI;
public GuideUI guideUI;
public MobileControls mobileControls;
public Text debugText;
public GameObject pauseMenu;
[Header("Настройки")]
public int renderDistance = 15;
public float dayLength = 600f;
public int maxInventorySize = 50;
public float autoSaveInterval = 60f;
private Player _player;
private WorldGenerator _worldGenerator;
private QuestManager _questManager;
private DayNightCycle _dayNightCycle;
private GuideSystem _guideSystem;
private List _activeChunks = new List();
private Dictionary _chunkMap = new Dictionary();
private Vector2Int _currentChunk;
private float _tickTimer;
private float _autoSaveTimer;
private bool _isPaused;
void Awake()
{
Instance = this;
Application.targetFrameRate = 60;
Screen.sleepTimeout = SleepTimeout.NeverSleep;
}
void Start()
{
InitializeSystems();
GenerateWorld();
SpawnPlayer();
StartCoroutine(UpdateChunks());
_guideSystem.ShowMessage("Добро пожаловать! Я проводник. Начни с добычи древесины.");
}
void Update()
{
if (_isPaused) return;
_tickTimer += Time.deltaTime;
if (_tickTimer >= 0.5f) { _tickTimer = 0; Tick(); }
_autoSaveTimer += Time.deltaTime;
if (_autoSaveTimer >= autoSaveInterval) { _autoSaveTimer = 0; SaveWorld(); }
if (Input.GetKeyDown(KeyCode.E) || Input.GetKeyDown(KeyCode.Tab)) inventoryUI.Toggle();
if (Input.GetKeyDown(KeyCode.C)) craftingUI.Toggle();
if (Input.GetKeyDown(KeyCode.Q)) questUI.Toggle();
if (Input.GetKeyDown(KeyCode.G)) guideUI.Toggle();
if (Input.GetKeyDown(KeyCode.F3)) debugText.enabled = !debugText.enabled;
if (Input.GetKeyDown(KeyCode.Escape)) TogglePause();
if (Input.GetKeyDown(KeyCode.R)) SaveWorld();
if (Input.GetKeyDown(KeyCode.L)) LoadWorld();
}
void InitializeSystems()
{
_worldGenerator = new WorldGenerator(seed, worldWidth, worldHeight);
_questManager = new QuestManager();
_dayNightCycle = new DayNightCycle(dayLength);
_guideSystem = new GuideSystem();
inventoryUI.Initialize(maxInventorySize);
craftingUI.Initialize();
questUI.Initialize();
guideUI.Initialize(_guideSystem);
if (Application.isMobilePlatform && mobileControls != null)
mobileControls.gameObject.SetActive(true);
}
void GenerateWorld()
{
for (int x = 0; x < worldWidth; x++)
for (int y = 0; y < worldHeight; y++)
SetBlock(x, y, _worldGenerator.GetBlockType(x, y));
_worldGenerator.GenerateTrees(SetBlock);
_worldGenerator.GenerateOres(SetBlock);
_worldGenerator.GenerateDungeons(SetBlock);
_worldGenerator.GenerateWater(SetBlock);
}
void SpawnPlayer()
{
Vector3 spawnPos = playerSpawn.position;
for (int y = 0; y < 20; y++)
{
Vector3 testPos = spawnPos + Vector3.up * y;
if (!IsSolid(Mathf.RoundToInt(testPos.x), Mathf.RoundToInt(testPos.y)))
{ spawnPos = testPos; break; }
}
GameObject obj = Instantiate(playerPrefab, spawnPos, Quaternion.identity);
_player = obj.GetComponent();
_player.Initialize(inventoryUI, _guideSystem);
Camera.main.GetComponent().SetTarget(obj.transform);
}
void Tick()
{
_dayNightCycle.Tick();
_questManager.UpdateQuests(_player?.GetInventory());
foreach (var chunk in _activeChunks) chunk.Tick();
}
void TogglePause()
{
_isPaused = !_isPaused;
pauseMenu.SetActive(_isPaused);
Time.timeScale = _isPaused ? 0 : 1;
}
IEnumerator UpdateChunks()
{
while (true)
{
if (!_isPaused && _player != null)
{
Vector2Int pc = GetChunkPosition(_player.transform.position);
if (pc != _currentChunk) { _currentChunk = pc; UpdateActiveChunks(); }
}
yield return new WaitForSeconds(0.5f);
}
}
void UpdateActiveChunks()
{
List toRemove = new List();
foreach (var kvp in _chunkMap)
if (Vector2Int.Distance(kvp.Key, _currentChunk) > renderDistance)
toRemove.Add(kvp.Value);
foreach (var chunk in toRemove)
{ chunk.Unload(); _chunkMap.Remove(chunk.Position); _activeChunks.Remove(chunk); }
for (int x = -renderDistance; x <= renderDistance; x++)
for (int y = -renderDistance; y <= renderDistance; y++)
{
Vector2Int pos = _currentChunk + new Vector2Int(x, y);
if (!_chunkMap.ContainsKey(pos))
{
Chunk chunk = new Chunk(pos);
chunk.Load(this);
_chunkMap[pos] = chunk;
_activeChunks.Add(chunk);
}
}
}
Vector2Int GetChunkPosition(Vector3 worldPos) =>
new Vector2Int(Mathf.FloorToInt(worldPos.x / 16), Mathf.FloorToInt(worldPos.y / 16));
public void SetBlock(int x, int y, BlockType type)
{
if (x < 0 || x >= worldWidth || y < 0 || y >= worldHeight) return;
Vector3Int pos = new Vector3Int(x, y, 0);
Tilemap target = GetTilemap(type);
target.SetTile(pos, GetTileBase(type));
}
Tilemap GetTilemap(BlockType type)
{
switch (type)
{
case BlockType.Grass: case BlockType.Dirt: case BlockType.Stone: case BlockType.Sand:
return groundTilemap;
case BlockType.Wood: case BlockType.Leaf: case BlockType.Flower:
return decorationTilemap;
case BlockType.Water:
return waterTilemap;
default:
return wallTilemap;
}
}
TileBase GetTileBase(BlockType type)
{
int idx = (int)type;
return (idx >= 0 && idx < tiles.Length) ? tiles[idx] : null;
}
public BlockType GetBlock(int x, int y)
{
if (x < 0 || x >= worldWidth || y < 0 || y >= worldHeight) return BlockType.Air;
Vector3Int pos = new Vector3Int(x, y, 0);
TileBase t = groundTilemap.GetTile(pos) ?? decorationTilemap.GetTile(pos) ?? waterTilemap.GetTile(pos);
if (t == null) return BlockType.Air;
for (int i = 0; i < tiles.Length; i++) if (tiles[i] == t) return (BlockType)i;
return BlockType.Air;
}
public bool IsSolid(int x, int y)
{
BlockType t = GetBlock(x, y);
return t != BlockType.Air && t != BlockType.Water && t != BlockType.Flower;
}
public Player GetPlayer() => _player;
public QuestManager GetQuestManager() => _questManager;
public GuideSystem GetGuideSystem() => _guideSystem;
public int GetWorldWidth() => worldWidth;
public int GetWorldHeight() => worldHeight;
public InventoryUI GetInventoryUI() => inventoryUI;
public CraftingUI GetCraftingUI() => craftingUI;
public QuestUI GetQuestUI() => questUI;
public GuideUI GetGuideUI() => guideUI;
public bool IsPaused() => _isPaused;
public void SaveWorld()
{
try
{
string path = Application.persistentDataPath + "/world.save";
using BinaryWriter w = new BinaryWriter(File.Open(path, FileMode.Create));
w.Write(worldWidth); w.Write(worldHeight); w.Write(seed);
w.Write(_dayNightCycle.GetTime());
for (int x = 0; x < worldWidth; x++)
for (int y = 0; y < worldHeight; y++)
w.Write((int)GetBlock(x, y));
_player?.Save(w);
_questManager.Save(w);
inventoryUI.Save(w);
_guideSystem.ShowMessage("Мир сохранён!");
}
catch (Exception e) { Debug.LogError("Ошибка сохранения: " + e.Message); }
}
public void LoadWorld()
{
try
{
string path = Application.persistentDataPath + "/world.save";
if (!File.Exists(path)) { _guideSystem.ShowMessage("Сохранение не найдено"); return; }
using BinaryReader r = new BinaryReader(File.Open(path, FileMode.Open));
int w = r.ReadInt32(), h = r.ReadInt32(), s = r.ReadInt32();
float time = r.ReadSingle();
if (w != worldWidth || h != worldHeight) { _guideSystem.ShowMessage("Размеры не совпадают"); return; }
for (int x = 0; x < worldWidth; x++)
for (int y = 0; y < worldHeight; y++)
SetBlock(x, y, (BlockType)r.ReadInt32());
_player?.Load(r);
_questManager.Load(r);
inventoryUI.Load(r);
_dayNightCycle.SetTime(time);
_guideSystem.ShowMessage("Мир загружен!");
}
catch (Exception e) { Debug.LogError("Ошибка загрузки: " + e.Message); }
}
}
// ============================================================
// 2. ГЕНЕРАТОР МИРА
// ============================================================
public class WorldGenerator
{
private int _seed, _width, _height;
private System.Random _random;
private float[,] _heightMap, _caveMap, _moistureMap;
public WorldGenerator(int seed, int width, int height)
{
_seed = seed; _width = width; _height = height;
_random = new System.Random(seed);
_heightMap = new float[width, height];
_caveMap = new float[width, height];
_moistureMap = new float[width, height];
for (int x = 0; x < width; x++)
{
float v = 0, s = 1, a = 1;
for (int i = 0; i < 6; i++)
{ v += Mathf.PerlinNoise((x + seed) * s / 80f, seed * s / 80f) * a; s *= 2.2f; a *= 0.45f; }
for (int y = 0; y < height; y++)
_heightMap[x, y] = v * height / 3.5f + height / 4f;
}
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++)
{
_caveMap[x, y] = Mathf.PerlinNoise((x + seed * 2) / 35f, (y + seed * 3) / 35f);
_moistureMap[x, y] = Mathf.PerlinNoise((x + seed * 5) / 60f, (y + seed * 7) / 60f);
}
}
public BlockType GetBlockType(int x, int y)
{
float h = _heightMap[x, y], c = _caveMap[x, y], m = _moistureMap[x, y];
if (y < _height / 4f && m > 0.6f) return BlockType.Water;
if (y > h + 12) return BlockType.Air;
if (y < h - 30) return c > 0.45f ? BlockType.Air : BlockType.Stone;
if (y < h - 25) return BlockType.Stone;
if (y < h) return BlockType.Dirt;
if (Mathf.Abs(y - h) < 1f) return m > 0.5f ? BlockType.Grass : (m > 0.3f ? BlockType.Sand : BlockType.Dirt);
if (Mathf.Abs(y - h) < 2f && m > 0.4f && _random.NextDouble() < 0.1f) return BlockType.Flower;
return BlockType.Air;
}
public void GenerateTrees(Action set)
{
for (int x = 3; x < _width - 3; x++)
for (int y = 3; y < _height - 3; y++)
if (GetBlockType(x, y) == BlockType.Grass && _random.NextDouble() < 0.025f && _moistureMap[x, y] > 0.3f)
{
int height = _random.Next(5, 9);
for (int i = 0; i < height; i++) if (y + i < _height) set(x, y + i, BlockType.Wood);
for (int dx = -3; dx <= 3; dx++)
for (int dy = -2; dy <= 2; dy++)
if (Mathf.Abs(dx) + Mathf.Abs(dy) <= 4)
{ int lx = x + dx, ly = y + height - 3 + dy; if (lx >= 0 && lx < _width && ly >= 0 && ly < _height) set(lx, ly, BlockType.Leaf); }
}
}
public void GenerateOres(Action set)
{
for (int i = 0; i < 80; i++)
{
int x = _random.Next(5, _width - 5), y = _random.Next(10, _height / 2);
if (GetBlockType(x, y) == BlockType.Stone)
{
set(x, y, BlockType.IronOre);
for (int j = 0; j < 3; j++)
{ int dx = _random.Next(-2, 3), dy = _random.Next(-2, 3); if (GetBlockType(x + dx, y + dy) == BlockType.Stone) set(x + dx, y + dy, BlockType.IronOre); }
}
}
for (int i = 0; i < 20; i++)
{
int x = _random.Next(10, _width - 10), y = _random.Next(5, _height / 3);
if (GetBlockType(x, y) == BlockType.Stone) set(x, y, BlockType.GoldOre);
}
}
public void GenerateDungeons(Action set)
{
for (int i = 0; i < 5; i++)
{
int x = _random.Next(15, _width - 15), y = _random.Next(5, _height / 3);
for (int dx = -3; dx <= 3; dx++)
for (int dy = -3; dy <= 3; dy++)
if (Mathf.Abs(dx) == 3 || Mathf.Abs(dy) == 3) set(x + dx, y + dy, BlockType.Stone);
set(x, y, BlockType.Chest);
if (_random.NextDouble() < 0.5f) set(x + 1, y, BlockType.Chest);
}
}
public void GenerateWater(Action set)
{
for (int x = 0; x < _width; x++)
for (int y = 0; y < _height / 4; y++)
if (GetBlockType(x, y) == BlockType.Air && _moistureMap[x, y] > 0.7f)
set(x, y, BlockType.Water);
}
}
// ============================================================
// 3. ИГРОК
// ============================================================
public class Player : MonoBehaviour
{
[Header("Движение")]
public float speed = 4f;
public float jumpForce = 7f;
public float gravity = -25f;
public int reachDistance = 5;
public float fallResetY = -50f;
[Header("Инвентарь")]
public int hotbarSize = 9;
public GameObject blockHighlight;
public SpriteRenderer highlightRenderer;
private Rigidbody2D _rb;
private Vector2 _velocity;
private bool _isGrounded;
private Inventory _inventory;
private InventoryUI _inventoryUI;
private GuideSystem _guideSystem;
private int _selectedSlot;
private Vector3Int _targetBlock;
private Camera _cam;
private bool _isBuilding;
private float _hInput;
private bool _jumpInput, _breakInput, _placeInput;
private MobileControls _mobile;
void Start()
{
_rb = GetComponent();
_cam = Camera.main;
_inventory = new Inventory(hotbarSize + 41);
blockHighlight.SetActive(false);
_mobile = FindObjectOfType();
}
public void Initialize(InventoryUI ui, GuideSystem guide)
{
_inventoryUI = ui; _guideSystem = guide;
_inventoryUI.SetInventory(_inventory);
_inventoryUI.SetPlayer(this);
_inventory.AddItem(new Item(BlockType.Wood, 10));
_inventory.AddItem(new Item(BlockType.Dirt, 20));
_inventory.AddItem(new Item(BlockType.Stone, 5));
_inventory.AddItem(new Item(BlockType.Grass, 3));
}
void Update()
{
if (GameController.Instance == null || GameController.Instance.IsPaused()) return;
if (transform.position.y < fallResetY)
{ transform.position = new Vector3(0, 20, 0); _guideSystem?.ShowMessage("Возврат из бездны!"); }
HandleInput();
HandleMovement();
HandleInteraction();
HandleHotbar();
HandleBuildMode();
UpdateHighlight();
}
void HandleInput()
{
if (_mobile != null && _mobile.gameObject.activeSelf)
{
_hInput = _mobile.GetHorizontal();
_jumpInput = _mobile.GetJump();
_breakInput = _mobile.GetBreak();
_placeInput = _mobile.GetPlace();
}
else
{
_hInput = Input.GetAxis("Horizontal");
_jumpInput = Input.GetButtonDown("Jump");
_breakInput = Input.GetMouseButtonDown(0);
_placeInput = Input.GetMouseButtonDown(1);
}
}
void HandleMovement()
{
_velocity.x = _hInput * speed;
if (_jumpInput && _isGrounded) _velocity.y = jumpForce;
if (!_isGrounded) _velocity.y += gravity * Time.deltaTime;
_rb.velocity = _velocity;
RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down, 0.7f);
_isGrounded = hit.collider != null && hit.collider.gameObject.layer == LayerMask.NameToLayer("Ground");
if (_hInput != 0) transform.localScale = new Vector3(Mathf.Sign(_hInput), 1, 1);
}
void HandleInteraction()
{
Vector2 mousePos = _cam.ScreenToWorldPoint(Input.mousePosition);
Vector2 dir = (mousePos - (Vector2)transform.position).normalized;
if (_isBuilding)
{
_targetBlock = GetTargetBlock(mousePos);
if (_targetBlock != null)
{
blockHighlight.transform.position = _targetBlock + Vector3.one * 0.5f;
blockHighlight.SetActive(true);
if (_placeInput) PlaceBlock();
}
return;
}
for (int i = 0; i < reachDistance; i++)
{
Vector2 pos = (Vector2)transform.position + dir * (i + 0.5f);
Vector3Int gp = new Vector3Int(Mathf.RoundToInt(pos.x), Mathf.RoundToInt(pos.y), 0);
if (GameController.Instance.IsSolid(gp.x, gp.y))
{
_targetBlock = gp;
blockHighlight.transform.position = gp + Vector3.one * 0.5f;
blockHighlight.SetActive(true);
if (_breakInput) BreakBlock();
else if (_placeInput) PlaceBlockAdjacent();
return;
}
}
blockHighlight.SetActive(false);
}
Vector3Int GetTargetBlock(Vector2 mousePos) =>
Vector2.Distance(mousePos, transform.position) < reachDistance ?
new Vector3Int(Mathf.RoundToInt(mousePos.x), Mathf.RoundToInt(mousePos.y), 0) : Vector3Int.zero;
void BreakBlock()
{
if (_targetBlock == null) return;
BlockType t = GameController.Instance.GetBlock(_targetBlock.x, _targetBlock.y);
if (t != BlockType.Air && t != BlockType.Water)
{
GameController.Instance.SetBlock(_targetBlock.x, _targetBlock.y, BlockType.Air);
_inventory.AddItem(new Item(t, 1));
foreach (var q in GameController.Instance.GetQuestManager().GetActiveQuests())
if (q.Type == QuestType.Gather && q.Target == t) q.Progress++;
_guideSystem?.ShowMessage($"Добыто: {t}");
}
}
void PlaceBlockAdjacent()
{
if (_targetBlock == null) return;
Vector3Int pos = _targetBlock;
Vector2 dir = (Vector2)_targetBlock - (Vector2)transform.position;
if (Mathf.Abs(dir.x) > Mathf.Abs(dir.y)) pos.x += dir.x > 0 ? -1 : 1;
else pos.y += dir.y > 0 ? -1 : 1;
PlaceBlockAt(pos);
}
void PlaceBlock() { if (_targetBlock != null) PlaceBlockAt(_targetBlock); }
void PlaceBlockAt(Vector3Int pos)
{
if (pos.x < 0 || pos.x >= GameController.Instance.GetWorldWidth() ||
pos.y < 0 || pos.y >= GameController.Instance.GetWorldHeight()) return;
if (!GameController.Instance.IsSolid(pos.x, pos.y))
{
Item item = _inventory.GetItem(_selectedSlot);
if (item != null && item.Count > 0 && item.Type != BlockType.Air && item.Type != BlockType.Water)
{
GameController.Instance.SetBlock(pos.x, pos.y, item.Type);
item.Count--;
if (item.Count <= 0) _inventory.SetItem(_selectedSlot, null);
_inventoryUI.UpdateUI();
_guideSystem?.ShowMessage($"Установлен: {item.Type}");
}
}
}
void UpdateHighlight()
{
if (!blockHighlight.activeSelf) return;
highlightRenderer.color = _isBuilding ? new Color(0, 1, 0, 0.3f) : new Color(1, 1, 1, 0.2f);
}
void HandleHotbar()
{
for (int i = 0; i < hotbarSize; i++) if (Input.GetKeyDown(KeyCode.Alpha1 + i)) _selectedSlot = i;
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll != 0) { _selectedSlot += (int)Mathf.Sign(scroll); _selectedSlot = (_selectedSlot + hotbarSize) % hotbarSize; }
}
void HandleBuildMode()
{
if (Input.GetKeyDown(KeyCode.B) || (_mobile != null && _mobile.GetBuildToggle())) _isBuilding = !_isBuilding;
}
public Inventory GetInventory() => _inventory;
public void Save(BinaryWriter w)
{
w.Write(transform.position.x); w.Write(transform.position.y);
w.Write(_selectedSlot);
_inventory.Save(w);
}
public void Load(BinaryReader r)
{
transform.position = new Vector3(r.ReadSingle(), r.ReadSingle(), 0);
_selectedSlot = r.ReadInt32();
_inventory.Load(r);
_inventoryUI.UpdateUI();
}
}
// ============================================================
// 4. ИНВЕНТАРЬ
// ============================================================
[System.Serializable]
public class Item
{
public BlockType Type;
public int Count;
public Item(BlockType t, int c) { Type = t; Count = c; }
public Sprite GetSprite() => Resources.Load($"Items/{Type}");
}
public class Inventory
{
private Item[] _items;
public int Length => _items.Length;
public Inventory(int size) { _items = new Item[size]; }
public Item GetItem(int idx) { return (idx >= 0 && idx < _items.Length) ? _items[idx] : null; }
public void SetItem(int idx, Item item) { if (idx >= 0 && idx < _items.Length) _items[idx] = item; }
public void AddItem(Item item)
{
for (int i = 0; i < _items.Length; i++)
if (_items[i] != null && _items[i].Type == item.Type) { _items[i].Count += item.Count; return; }
for (int i = 0; i < _items.Length; i++)
if (_items[i] == null) { _items[i] = new Item(item.Type, item.Count); return; }
}
public int GetItemCount(BlockType type)
{
int c = 0;
foreach (var item in _items) if (item != null && item.Type == type) c += item.Count;
return c;
}
public void RemoveItem(BlockType type, int count)
{
for (int i = 0; i < _items.Length && count > 0; i++)
{
if (_items[i] != null && _items[i].Type == type)
{
int take = Mathf.Min(_items[i].Count, count);
_items[i].Count -= take;
count -= take;
if (_items[i].Count <= 0) _items[i] = null;
}
}
}
public void Save(BinaryWriter w)
{
w.Write(_items.Length);
foreach (var item in _items)
{
if (item == null) { w.Write(-1); continue; }
w.Write((int)item.Type); w.Write(item.Count);
}
}
public void Load(BinaryReader r)
{
int size = r.ReadInt32();
_items = new Item[size];
for (int i = 0; i < size; i++)
{
int type = r.ReadInt32();
if (type < 0) continue;
_items[i] = new Item((BlockType)type, r.ReadInt32());
}
}
}
// ============================================================
// 5. UI ИНВЕНТАРЯ
// ============================================================
public class InventoryUI : MonoBehaviour
{
public GameObject slotPrefab;
public Transform slotContainer;
public Transform hotbarContainer;
public Text titleText;
public GameObject panel;
private Inventory _inventory;
private Player _player;
private List _slots = new List();
private bool _isOpen;
public void Initialize(int maxSize)
{
panel.SetActive(false);
for (int i = 0; i < maxSize; i++)
{
Transform parent = i < 9 ? hotbarContainer : slotContainer;
GameObject go = Instantiate(slotPrefab, parent);
var slot = go.GetComponent();
slot.SetIndex(i);
_slots.Add(slot);
}
}
public void SetInventory(Inventory inv) { _inventory = inv; UpdateUI(); }
public void SetPlayer(Player p) { _player = p; }
public void UpdateUI()
{
for (int i = 0; i < _slots.Count; i++)
_slots[i].SetItem(_inventory?.GetItem(i));
}
public void Toggle()
{
_isOpen = !_isOpen;
panel.SetActive(_isOpen);
if (_isOpen) UpdateUI();
}
public void Save(BinaryWriter w) { _inventory?.Save(w); }
public void Load(BinaryReader r) { _inventory?.Load(r); UpdateUI(); }
}
public class InventorySlot : MonoBehaviour, IPointerClickHandler
{
public Image icon;
public Text countText;
public Image highlight;
private int _index;
private Item _item;
public void SetIndex(int idx) { _index = idx; }
public void SetItem(Item item)
{
_item = item;
if (item == null) { icon.sprite = null; icon.color = Color.clear; countText.text = ""; return; }
icon.sprite = item.GetSprite();
icon.color = Color.white;
countText.text = item.Count > 1 ? item.Count.ToString() : "";
}
public void OnPointerClick(PointerEventData e) { }
}
// ============================================================
// 6. КРАФТ
// ============================================================
public class CraftingUI : MonoBehaviour
{
public GameObject panel;
public Transform recipeContainer;
public GameObject recipePrefab;
public Button craftButton;
public Image resultImage;
public Text resultName;
public Text resultCount;
public Text ingredientsText;
private Inventory _inventory;
private List _recipes = new List();
private Recipe _selectedRecipe;
private bool _isOpen;
void Start()
{
panel.SetActive(false);
LoadRecipes();
craftButton.onClick.AddListener(Craft);
}
void LoadRecipes()
{
_recipes.Add(new Recipe("Деревянная кирка", new Dictionary { { BlockType.Wood, 3 } }, BlockType.Planks, 1));
_recipes.Add(new Recipe("Каменная кирка", new Dictionary { { BlockType.Cobblestone, 3 } }, BlockType.Cobblestone, 1));
_recipes.Add(new Recipe("Деревянный меч", new Dictionary { { BlockType.Wood, 2 } }, BlockType.Planks, 1));
_recipes.Add(new Recipe("Сундук", new Dictionary { { BlockType.Wood, 8 } }, BlockType.Chest, 1));
_recipes.Add(new Recipe("Верстак", new Dictionary { { BlockType.Wood, 4 } }, BlockType.CraftingTable, 1));
_recipes.Add(new Recipe("Стекло", new Dictionary { { BlockType.Sand, 3 } }, BlockType.Glass, 3));
_recipes.Add(new Recipe("Кирпич", new Dictionary { { BlockType.Dirt, 2 } }, BlockType.Brick, 4));
}
public void Initialize() { _inventory = GameController.Instance?.GetPlayer()?.GetInventory(); UpdateUI(); }
public void Toggle()
{
_isOpen = !_isOpen;
panel.SetActive(_isOpen);
if (_isOpen) UpdateUI();
}
void UpdateUI()
{
foreach (Transform c in recipeContainer) Destroy(c.gameObject);
foreach (var r in _recipes)
{
GameObject go = Instantiate(recipePrefab, recipeContainer);
go.GetComponent().SetRecipe(r, this);
}
UpdateCraftButton();
}
public void SelectRecipe(Recipe r)
{
_selectedRecipe = r;
resultImage.sprite = GetSprite(r.Result);
resultName.text = r.Name;
resultCount.text = $"x{r.ResultCount}";
ingredientsText.text = string.Join("\n", r.Ingredients.Select(kvp => $"{kvp.Key}: {kvp.Value}"));
UpdateCraftButton();
}
void UpdateCraftButton()
{
if (_selectedRecipe == null || _inventory == null) { craftButton.interactable = false; return; }
bool can = true;
foreach (var kvp in _selectedRecipe.Ingredients)
if (_inventory.GetItemCount(kvp.Key) < kvp.Value) { can = false; break; }
craftButton.interactable = can;
}
void Craft()
{
if (_selectedRecipe == null || _inventory == null) return;
foreach (var kvp in _selectedRecipe.Ingredients) _inventory.RemoveItem(kvp.Key, kvp.Value);
_inventory.AddItem(new Item(_selectedRecipe.Result, _selectedRecipe.ResultCount));
GameController.Instance?.GetGuideSystem()?.ShowMessage($"Создано: {_selectedRecipe.Name}");
UpdateUI();
GameController.Instance?.GetInventoryUI()?.UpdateUI();
}
Sprite GetSprite(BlockType t) => Resources.Load($"Items/{t}");
}
public class Recipe
{
public string Name;
public Dictionary Ingredients;
public BlockType Result;
public int ResultCount;
public Recipe(string n, Dictionary ing, BlockType r, int c)
{ Name = n; Ingredients = ing; Result = r; ResultCount = c; }
}
public class RecipeUI : MonoBehaviour
{
public Text nameText;
public Image iconImage;
private Recipe _recipe;
private CraftingUI _craftingUI;
public void SetRecipe(Recipe r, CraftingUI ui)
{
_recipe = r; _craftingUI = ui;
nameText.text = r.Name;
iconImage.sprite = Resources.Load($"Items/{r.Result}");
GetComponent