Untitled

 avatar
unknown
plain_text
a year ago
117 kB
17
Indexable
using Assimp;
using Graven.Engine;
using Driftmark.Properties;
using OpenTK;
using OpenTK.GLControl;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Input;
using OpenTK.Mathematics;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.GraphicsLibraryFramework;
using System;
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ProgressBar;
using Button = System.Windows.Forms.Button;
using Image = System.Drawing.Image; // For GraphicsPath
using Keys = System.Windows.Forms.Keys;
using ProgressBar = System.Windows.Forms.ProgressBar;
using Timer = System.Windows.Forms.Timer;
using ToolTip = System.Windows.Forms.ToolTip;
using Properties = Driftmark.Properties;


namespace Graven
{
    public partial class Game : Form
    {
        public RenderForm rnd;
        public string WorldName;

        public bool LoadedWorld;

        private bool isMultiplayer;

        #region Inventory system
        private const int ROWS = 5; // 4 inventory + 1 hotbar
        private const int COLS = 8;
        private const int TOTAL_SLOTS = ROWS * COLS;
        private const int MAX_STACK = 64;
        public List<FloatingLoot> floatingLoots = new List<FloatingLoot>();

        private Panel p_inventory;
        private Panel p_hotbar;
        private PictureBox[] allSlots;
        private InventoryItem[] allItems;
        private int selectedSlot = 0;

        private PictureBox draggedItem;
        private DragInfo dragInfo;
        private Point dragOffset;
        private Panel craftPanel; // <-- your CraftPanel

        private ToolTip slotTooltip;
        private ContextMenuStrip slotMenu;
        private Image emptySlotIcon = Properties.Resources.Slot;

        public bool inventoryVisible = false;
        private bool isTesting = false;

        // Loader panel
        private Panel loaderPanel;
        private PictureBox loaderImage;
        private Label loaderText;


        //Console
        private Panel panel_Console;
        private System.Windows.Forms.TextBox tb_CommandInput;
        private RichTextBox rtb_Console;
        private System.Windows.Forms.Button btn_executeCommand;

        private Label lootboxLabel = null;
        private const float LOOTBOX_RADIUS = 2.5f; // distance to show label

        private int HaveAmount1, HaveAmount2, HaveAmount3;

        private Form dragForm; // top-level transparent form

        public Stopwatch frameTimer = new Stopwatch();
        public double lastFrameTime;

        public bool initialized = false;


        private bool workbenchUnlocked = false;


        // Mining state
        public bool isMining = false;
        public float miningTimer = 0f;
        public float miningDuration = 1.5f; // seconds per hit, adjust per object
        public dynamic miningTarget = null;


        public Crosshair crosshair = new Crosshair();

        private class DragInfo
        {
            public int Index;                // -1 for loot or furnace slot
            public InventoryItem Item;       // what’s being dragged
            public LootSlot LootSlot;        // null if dragging from inventory
            public Point Offset;             // mouse offset
            public bool FromFurnace;         // NEW: true if dragged from furnace
        }

        #endregion


        private Panel lootPanel;
        private LootSlot[,] lootSlots;


        #region setup Loader
        private void InitLoaderPanel()
        {
            loaderPanel = new Panel
            {
                Dock = DockStyle.Fill,
                BackColor = Color.Black
            };

            // Background image
            loaderImage = new PictureBox
            {
                Dock = DockStyle.Fill,
                Image = Properties.Resources.t, // your image
                SizeMode = PictureBoxSizeMode.StretchImage
            };
            loaderPanel.Controls.Add(loaderImage);

            // Status text
            loaderText = new Label
            {
                Text = "Starting...",
                ForeColor = Color.White,
                Font = new Font("Segoe UI", 18, FontStyle.Bold),
                AutoSize = false,
                TextAlign = ContentAlignment.MiddleCenter,
                Dock = DockStyle.Bottom,
                Height = 60,
                BackColor = Color.Transparent
            };
            loaderPanel.Controls.Add(loaderText);

            this.Controls.Add(loaderPanel);
            loaderPanel.BringToFront();
        }

        private void SetLoaderText(string text)
        {
            if (loaderText.InvokeRequired)
            {
                loaderText.Invoke(new Action(() => loaderText.Text = text));
            }
            else
            {
                loaderText.Text = text;
            }
            loaderPanel.Refresh();
        }


        #endregion

        public Game(string worldName, bool loadedWorld = false, bool isMultiplayer = false)
        {
            this.FormBorderStyle = FormBorderStyle.None;
            this.WindowState = FormWindowState.Maximized;
            this.BackColor = Color.Black;
            this.Text = "Driftmark - Survival Game";

            this.WorldName = worldName;
            this.LoadedWorld = loadedWorld;
            this.isMultiplayer = isMultiplayer;

            InitLoaderPanel(); // show loader first
            RunLoaderAsync();

        }

        private async Task RunLoaderAsync()
        {
            loaderPanel.Visible = true;
            loaderPanel.BringToFront();

            if (!LoadedWorld)
                await ShowStep($"🌟 Forging a New World: \"{WorldName}\"...");
            else
                await ShowStep($"🌟 Forging Into World: \"{WorldName}\"...");

            if (isMultiplayer)
                await ShowStep($"Setting Up Server..");

            await ShowStep("Setting up inventory...", () =>
            {
                SetupSlots();
                inventoryVisible = false; // explicitly hide at start
            });
            if (!LoadedWorld)  // <-- only add example items if this is a fresh game
                await ShowStep("Setting up loot...", SetupExampleItems);


            await ShowStep("Creating craft menu...", CreateCraftMenu);
            await ShowStep("Creating Furnace menu...", InitFurnaceX);
            await ShowStep("Setting up tooltips...", SetupTooltipsAndContext);
            await ShowStep("Setting up lootable UI...", SetupLootPanel);
            await ShowStep("Finalizing...");


            // Initialize systems before rendering
            InitConsole();
            InitHealthBox();
            InitStatusBars();
            SetupRegionLabel();
            InitHandOverlay();
            // InitOverlay();

            // Create RenderForm and add it to the form
            rnd = new RenderForm(this)
            {
                TopLevel = false,
                FormBorderStyle = FormBorderStyle.None,
                Dock = DockStyle.Fill,
            };
            this.Controls.Add(rnd);
            rnd.BringToFront();
            rnd.Show();
            rnd.Refresh(); // force first frame to avoid black flicker


            // Hide loader AFTER RenderForm is shown
            loaderPanel.Visible = false;

            // Make sure other UI controls (inventory, hotbar, panels) are above the render form
            foreach (Control c in this.Controls)
                if (!(c is RenderForm))
                    c.BringToFront();

            // Layout hooks
            this.Resize += (s, e) => PositionPanels();
            PositionPanels();

            dragForm = new Form();
            dragForm.FormBorderStyle = FormBorderStyle.None;
            dragForm.TopMost = true;
            dragForm.ShowInTaskbar = false;
            dragForm.BackColor = Color.Lime;       // any color you want to be transparent
            dragForm.TransparencyKey = Color.Lime; // match the BackColor
            dragForm.StartPosition = FormStartPosition.Manual;
            dragForm.Size = this.Size;
            dragForm.Location = this.Location;
            dragForm.Show();

            // Make sure it moves with the main form
            this.LocationChanged += (s, e) => dragForm.Location = this.Location;
            this.SizeChanged += (s, e) => dragForm.Size = this.Size;



            if (LoadedWorld)
            {
                LoadGame(WorldName);
            }
            else
            {
                SaveGame();
            }

            // Finalize
            CraftManager.craftableItems(this);
            FurnaceManager.SetupFurnace(this);
            this.KeyPreview = true;
            this.KeyDown += Game_KeyDown;
            this.KeyUp += Game_KeyUp;
            this.MouseWheel += Game_MouseWheel;
            this.FormClosing += Game_FormClosing;
            initialized = true;
            rnd.canMove = true;

            InventoryItem tool = null;
            if (selectedSlot >= 0 && selectedSlot < allItems.Length)
                tool = allItems[selectedSlot];
        }

        private async Task ShowStep(string message, Action action = null)
        {
            SetLoaderText(message);
            await Task.Delay(900); // give user a moment to see the text
            action?.Invoke();
        }

        #region Hand Overlay Setup
        private Form handOverlay;
        private PictureBox Hand;
        private float idleTime = 0f;
        private float idleAmplitude = 10f;
        private float idleSpeed = 2f;


        private float mineTime = 0f;
        private float mineDuration = 0.3f;
        private float mineAmplitude = 50f;

        private float mineForwardAmplitude = 50f; // how far hand swings forward
        private float mineDownAmplitude = 20f;    // how much it moves down slightly

        private Vector3 handLocalOffset = new Vector3(0.5f, -0.5f, 1f); // X=right, Y=down, Z=forward
        private float handScale = 400f; // size for PictureBox
        private PictureBox itemInHandBox = null;
        public void InitHandOverlay()
        {
            // Create overlay form
            handOverlay = new Form
            {
                FormBorderStyle = FormBorderStyle.None,
                TopMost = true,
                ShowInTaskbar = false,
                BackColor = Color.Lime,
                TransparencyKey = Color.Lime,
                StartPosition = FormStartPosition.Manual,
                Size = this.Size,
                Location = this.Location
            };

            // Hand PictureBox
            Hand = new PictureBox
            {
                BackColor = Color.Transparent,
                Image = Resources.Hand_Icon,
                SizeMode = PictureBoxSizeMode.StretchImage,
                Size = new Size(600, 600),
                Left = handOverlay.Width - 430, // initial right-bottom placement
                Top = handOverlay.Height - 420
            };

            handOverlay.Controls.Add(Hand);
            handOverlay.Show();

            // Make overlay follow main form
            this.LocationChanged += (s, e) => handOverlay.Location = this.Location;
            this.SizeChanged += (s, e) =>
            {
                handOverlay.Size = this.Size;
                Hand.Left = handOverlay.Width - Hand.Width - 30;
                Hand.Top = handOverlay.Height - Hand.Height - 20;
            };
        }

        /// Call this to trigger mining
        public void StartMiningWithHand()
        {
            isMining = true;
            mineTime = 0f;
        }
        private Image originalHandImage;

        public void UpdateHand(float dt, Engine.Camera camera)
        {
            // --- Idle motion ---
            idleTime += dt;
            float idleOffset = MathF.Sin(idleTime * idleSpeed) * idleAmplitude;

            // --- Mining / swing animation ---
            Vector2 mineOffset = Vector2.Zero;
            float mineRotation = 0f;

            if (isMining)
            {
                mineTime += dt;
                float progress = MathF.Min(mineTime / mineDuration, 1f);

                // Rust-style swing curve:
                // Back swing: negative rotation first quarter
                // Forward swing: peak rotation mid swing
                // Follow-through: easing out last quarter
                if (progress < 0.25f)
                {
                    mineRotation = Lerp(-30f, 0f, progress / 0.25f); // pull back
                }
                else if (progress < 0.75f)
                {
                    float localProgress = (progress - 0.25f) / 0.5f;
                    mineRotation = Lerp(0f, 70f, localProgress); // forward strike
                }
                else
                {
                    float localProgress = (progress - 0.75f) / 0.25f;
                    mineRotation = Lerp(70f, 0f, localProgress); // follow-through
                }

                // Optional small horizontal sway
                mineOffset.X = MathF.Sin(progress * MathF.PI) * (mineAmplitude * 0.15f);
                mineOffset.Y = MathF.Sin(progress * MathF.PI) * mineAmplitude;

                if (progress >= 1f)
                {
                    mineTime = 0f;
                    //isMining = false;
                }
            }

            // --- Inventory check ---
            if (inventoryVisible)
            {
                Hand.Left = handOverlay.Width - Hand.Width - 30;
                Hand.Top = handOverlay.Height - Hand.Height + 100;
                return;
            }

            // --- Hand follows camera ---
            Vector3 forward = camera.Forward();
            Vector3 right = Vector3.Normalize(Vector3.Cross(Vector3.UnitY, forward));
            Vector3 up = Vector3.UnitY;

            Vector3 handOffset = forward * 2f - right * 1f - up * 1.2f;
            Vector3 handWorldPos = camera.Position + handOffset;

            // Project to screen
            Matrix4 view = camera.GetViewMatrix();
            Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(
                MathHelper.DegreesToRadians(70f),
                handOverlay.Width / (float)handOverlay.Height,
                0.1f, 1000f
            );

            Vector4 clipSpace = new Vector4(handWorldPos, 1f) * view * projection;
            Vector3 ndc = new Vector3(clipSpace.X, clipSpace.Y, clipSpace.Z) / clipSpace.W;

            int screenX = (int)((ndc.X * 0.5f + 0.5f) * handOverlay.Width) - Hand.Width / 2;
            int screenY = (int)((-ndc.Y * 0.5f + 0.5f) * handOverlay.Height) - Hand.Height / 2;

            // Apply offsets + rotation
            Hand.Left = screenX + (int)mineOffset.X;
            Hand.Top = screenY + (int)(idleOffset - mineOffset.Y);

            // Hand rotation: requires custom drawing or rotated PictureBox
            // Hand.Image = RotateImage(Hand.Image, mineRotation);
        }

        /// Rotate the image around a pivot
        private Bitmap RotateImage(Image img, float angle, PointF pivot)
        {
            Bitmap bmp = new Bitmap(img.Width, img.Height);
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.Clear(Color.Transparent);
                g.TranslateTransform(pivot.X, pivot.Y);
                g.RotateTransform(angle);
                g.TranslateTransform(-pivot.X, -pivot.Y);
                g.DrawImage(img, 0, 0);
            }
            return bmp;
        }
        private float Lerp(float a, float b, float t) => a + (b - a) * t;


        private InventoryItem currentItemInHand = null;
        private bool showItemBox = true;
        public void UpdateHandItem()
        {
            // Swap Hand.Image for specific tools
            if (selectedSlot >= 0 && selectedSlot < allItems.Length)
            {
                var selectedItem = allItems[selectedSlot];
                if (selectedItem != null)
                {
                    switch (selectedItem.Name)
                    {
                        case "Stone_Hatchet":
                            Hand.Image = Resources.Hand_Icon_WithStone_Axe;
                            showItemBox = false;
                            break;

                        case "Stone_Pickaxe":
                            Hand.Image = Resources.Hand_Icon_With_Stone_PickAxe;
                            showItemBox = false;
                            break;

                        default:
                            Hand.Image = Resources.Hand_Icon;
                            showItemBox = true;
                            break;
                    }
                }
            }

            // Get current selected item for item-in-hand overlay
            if (selectedSlot >= 0 && selectedSlot < allItems.Length)
                currentItemInHand = allItems[selectedSlot];
            else
                currentItemInHand = null;

            if (currentItemInHand == null || !showItemBox)
            {
                if (itemInHandBox != null)
                    itemInHandBox.Visible = false; // hide if no item or wooden axe
                return;
            }

            if (itemInHandBox == null)
            {
                itemInHandBox = new PictureBox
                {
                    BackColor = Color.Transparent,
                    SizeMode = PictureBoxSizeMode.StretchImage,
                    Size = new Size(150, 150) // adjust size
                };
                handOverlay.Controls.Add(itemInHandBox);
                itemInHandBox.BringToFront();
            }

            itemInHandBox.Image = currentItemInHand.Icon;
            itemInHandBox.Visible = true;

            // Get offset based on item type
            Vector3 offset = ItemInHandRotationSetup.GetHandOffset(currentItemInHand);

            // Apply offset relative to hand PictureBox
            itemInHandBox.Left = Hand.Left + (int)(offset.X * 50);
            itemInHandBox.Top = Hand.Top + (int)(offset.Y * 90);
        }

        /// Add a finger overlay (for holding items)
        public void AddFingerTowardsMissingGap()
        {
            // Add a PictureBox over Hand for finger positioning
            // Makes holding items look realistic
        }
        #endregion




        private void Game_Load(object sender, EventArgs e)
        {

        }


        private void Updater_Tick(object sender, EventArgs e)
        {
            double now = frameTimer.Elapsed.TotalSeconds;
            float dt = (float)(now - lastFrameTime);
            lastFrameTime = now;

            UpdateMining(dt);


            this.Invalidate(); // trigger Paint

            //UpdateFloatingLabels(dt); // <-- Add this at the end of UpdateLogic

            if (initialized)
            {
                SaveGame();
            }
        }


        #region save and load


        public void SaveGame()
        {
            if (string.IsNullOrEmpty(WorldName)) WorldName = "NewWorld";
            string filePath = Path.Combine("Saves", WorldName, $"{WorldName}.json");

            Directory.CreateDirectory("Saves");

            var data = new SaveData
            {
                WorldName = WorldName,
                CurrentHealth = currentHealth,
                MaxHealth = maxHealth,
                CurrentFood = currentFood,
                MaxFood = maxFood,
                CurrentWater = currentWater,
                MaxWater = maxWater
            };

            for (int i = 0; i < allItems.Length; i++)
            {
                var item = allItems[i];
                if (item != null)
                {
                    data.Items.Add(new InventoryItemData
                    {
                        Name = item.Name,
                        Count = item.Count,
                        Type = item.Type,
                        UID = item.UID
                    });
                }
            }

            File.WriteAllText(filePath, JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }));
        }

        public void LoadGame(string worldName)
        {
            string filePath = Path.Combine("Saves", worldName, $"{worldName}.json");
            if (!File.Exists(filePath)) return;

            var json = File.ReadAllText(filePath);
            var data = JsonSerializer.Deserialize<SaveData>(json);

            WorldName = data.WorldName;

            // Clear current items but keep allItems array
            if (allItems == null)
                allItems = new InventoryItem[TOTAL_SLOTS]; // initialize array if null

            // Now safe to clear
            for (int i = 0; i < allItems.Length; i++)
                allItems[i] = null;

            // Load items
            foreach (var itemData in data.Items)
            {
                Image icon = itemData.Name switch
                {
                    "Wood" => Properties.Resources.Wood,
                    "Stone" => Properties.Resources.StoneIcon,
                    "Metal_Shard" => Properties.Resources.Metal_Ore_Icon,
                    "Water_Jar" => Properties.Resources.Water,
                    "Stick" => Properties.Resources.Stick,
                    "Coal" => Properties.Resources.Coal_Icon,
                    "Hide" => Properties.Resources.Animal_Hide_Icon,
                    "Sap" => Properties.Resources.Tree_Sap_Icon,
                    "Raspberry" => Properties.Resources.raspberry_Icon,
                    "Fairy_Dust" => Properties.Resources.Fairy_Dust_Icon,
                    "Campfire" => Properties.Resources.CampFireItem,
                    "Furnace" => Properties.Resources.Furnace_Icon,
                    "Workbench" => Properties.Resources.WorkBenchIcon,
                    "Metal_Axe" => Properties.Resources.MetalAxe,
                    "Saddle" => Properties.Resources.SaddleIcon,
                    "Spirit_Log" => Properties.Resources.Spirit_realm_Log_Icon,
                    "Common_Item_Card" => Properties.Resources.Painting1,
                    "Sleeping_Tarot_Card" => Properties.Resources.sleepingCard_Icon,
                    "Night_Tarot_Card" => Properties.Resources.NightCard_Icon,
                    "Herbal_Infusion_Tea" => Properties.Resources.Herbal__Infusion_Tea_Icon,
                    "Spirit_Blade" => Properties.Resources.Spirit_blade_icon,
                    "Spirit_Torch" => Properties.Resources.spirit_torch_icon,
                    "Spirit_Stick" => Properties.Resources.Spirit_Stick_Icon,
                    "Empty_Jar" => Properties.Resources.Empty_Water_Jar_Icon,
                    "Broken_Glass" => Properties.Resources.Broken_Glass_Icon,
                    "Wooden_Shield" => Properties.Resources.wooden_Shield_Icon,
                    "Stone_Pickaxe" => Properties.Resources.Stone_Pickaxe_Icon,
                    "Stone_Hatchet" => Properties.Resources.Stone_Hatchet,
                    "Midnight_Pickaxe" => Properties.Resources.Midnight_Pickaxe_Icon,
                    "Midnight_Hatchet" => Properties.Resources.Midnight_Hatchet_Icon,
                    "Metal_Ingot" => Properties.Resources.Metal_Ore_Cooked_Icon,
                    "Cooked_Meat" => Properties.Resources.Cooked_Meta_Icon,
                    "Wooden_Arrow" => Properties.Resources.Wooden_Arrow,
                    "Hunting_Bow" => Properties.Resources.Bow_Icon,
                    _ => null
                };

                AddItem(new InventoryItem
                {
                    Name = itemData.Name,
                    Count = itemData.Count,
                    Type = itemData.Type,
                    UID = itemData.UID,
                    Icon = icon
                });
            }

            // Restore stats
            currentHealth = data.CurrentHealth;
            maxHealth = data.MaxHealth;
            currentFood = data.CurrentFood;
            maxFood = data.MaxFood;
            currentWater = data.CurrentWater;
            maxWater = data.MaxWater;

            // Force UI update
            for (int i = 0; i < allSlots.Length; i++)
                allSlots[i].Invalidate();

            ModifyFood(0);
            ModifyWater(0);
        }

        #endregion


        #region healthbarOfObjects
        // In your Game Form constructor or Load method
        Panel healthBox = new Panel();
        Panel healthBar = new Panel();

        private void InitHealthBox()
        {
            // Create the container
            healthBox.Size = new Size(120, 20);

            // Center it on the form
            healthBox.Location = new Point(
                (this.ClientSize.Width - healthBox.Width) / 2,
                (this.ClientSize.Height - healthBox.Height) / 2
            );

            healthBox.BackColor = Color.DarkGray; // background color

            // Create the health bar
            healthBar.Size = new Size(120, 20); // full width initially
            healthBar.BackColor = Color.White; // health bar color


            // Add controls to the healthBox
            healthBox.Controls.Add(healthBar);


            // Add healthBox to the form
            this.Controls.Add(healthBox);

            // Optional: keep it centered if the form is resized
            this.Resize += (s, e) =>
            {
                healthBox.Location = new Point(
                    (this.ClientSize.Width - healthBox.Width) / 2,
                    (this.ClientSize.Height - healthBox.Height) / 2
                );
            };

            healthBox.Visible = false;
            healthBar.Visible = false;
        }

        public void UpdateHealthBar(int currentHealth, int maxHealth)
        {
            float percent = (float)currentHealth / maxHealth;
            healthBar.Width = (int)(healthBox.Width * percent);
        }

        private void ShowHealthBar(int currentHealth, int maxHealth, string itemName)
        {
            healthBox.Visible = true;
            healthBar.Visible = true;
            UpdateHealthBar(currentHealth, maxHealth);
        }

        private void HideHealthBar()
        {
            healthBox.Visible = false;
        }

        #endregion

        #region PlayerStatusBars

        // Panels for containers + bars
        private Panel healthBoxs, healthBars;
        private Panel foodBox, foodBar;
        private Panel waterBox, waterBar;

        // Labels
        private Label healthLabel, foodLabel, waterLabel;

        // Player stats
        private int currentHealth = 100;
        private int maxHealth = 100;

        private int currentFood = 100;
        private int maxFood = 100;

        private int currentWater = 100;
        private int maxWater = 100;

        // Timer for survival updates
        private System.Windows.Forms.Timer statusTimer;

        public void ModifyFood(int amount)
        {
            currentFood = Math.Clamp(currentFood + amount, 0, maxFood);
            RefreshStatusBars();
        }

        public void ModifyWater(int amount)
        {
            currentWater = Math.Clamp(currentWater + amount, 0, maxWater);
            RefreshStatusBars();
        }

        private void InitStatusBars()
        {
            int barWidth = 150;
            int barHeight = 18;
            int spacing = 6; // spacing between bars
            int labelWidth = 50;

            // -------------------------
            // HEALTH
            // -------------------------
            healthBoxs = new Panel
            {
                Size = new Size(barWidth, barHeight),
                BackColor = Color.DarkGray
            };
            healthBars = new Panel
            {
                Size = new Size(barWidth, barHeight),
                BackColor = Color.Red
            };
            healthBoxs.Controls.Add(healthBars);

            healthLabel = new Label
            {
                Text = "❤️",
                Size = new Size(labelWidth, barHeight),
                ForeColor = Color.White,
                BackColor = Color.Red,
                Font = new Font("Arial", 10, FontStyle.Bold),
                TextAlign = ContentAlignment.MiddleLeft
            };
            this.Controls.Add(healthLabel);

            // -------------------------
            // FOOD
            // -------------------------
            foodBox = new Panel
            {
                Size = new Size(barWidth, barHeight),
                BackColor = Color.DarkGray
            };
            foodBar = new Panel
            {
                Size = new Size(barWidth, barHeight),
                BackColor = Color.Orange
            };
            foodBox.Controls.Add(foodBar);

            foodLabel = new Label
            {
                Text = "🍗",
                Size = new Size(labelWidth, barHeight),
                ForeColor = Color.White,
                BackColor = Color.Orange,
                Font = new Font("Arial", 10, FontStyle.Bold),
                TextAlign = ContentAlignment.MiddleLeft
            };
            this.Controls.Add(foodLabel);

            // -------------------------
            // WATER
            // -------------------------
            waterBox = new Panel
            {
                Size = new Size(barWidth, barHeight),
                BackColor = Color.DarkGray
            };
            waterBar = new Panel
            {
                Size = new Size(barWidth, barHeight),
                BackColor = Color.DodgerBlue
            };
            waterBox.Controls.Add(waterBar);

            waterLabel = new Label
            {
                Text = "🥛",
                Size = new Size(labelWidth, barHeight),
                ForeColor = Color.White,
                BackColor = Color.DodgerBlue,
                Font = new Font("Arial", 10, FontStyle.Bold),
                TextAlign = ContentAlignment.MiddleLeft
            };
            this.Controls.Add(waterLabel);

            // Add panels to form
            this.Controls.Add(healthBoxs);
            this.Controls.Add(foodBox);
            this.Controls.Add(waterBox);

            // Position bars and labels
            PositionStatusBars(barWidth, barHeight, spacing, labelWidth);

            // Adjust on resize
            this.Resize += (s, e) => PositionStatusBars(barWidth, barHeight, spacing, labelWidth);

            // Initialize UI with current stats
            RefreshStatusBars();
        }

        private void PositionStatusBars(int width, int height, int spacing, int labelWidth)
        {
            int margin = 12; // distance from edge
            int bottom = this.ClientSize.Height - margin - height;

            // Water at top
            waterBox.Location = new Point(margin + labelWidth, bottom - (height + spacing) * 2);
            waterLabel.Location = new Point(margin, bottom - (height + spacing) * 2);

            // Food in middle
            foodBox.Location = new Point(margin + labelWidth, bottom - (height + spacing) * 1);
            foodLabel.Location = new Point(margin, bottom - (height + spacing) * 1);

            // Health at bottom
            healthBoxs.Location = new Point(margin + labelWidth, bottom - (height + spacing) * 0);
            healthLabel.Location = new Point(margin, bottom - (height + spacing) * 0);
        }

        private void UpdateBar(Panel bar, Panel box, int current, int max)
        {
            float percent = Math.Max(0, Math.Min(1f, (float)current / max));
            bar.Width = (int)(box.Width * percent);
        }

        // Refresh all bars
        private void RefreshStatusBars()
        {
            UpdateBar(healthBars, healthBoxs, currentHealth, maxHealth);
            UpdateBar(foodBar, foodBox, currentFood, maxFood);
            UpdateBar(waterBar, waterBox, currentWater, maxWater);
        }

        // Example methods to change stats
        public void DamagePlayer(int amount)
        {
            currentHealth = Math.Max(0, currentHealth - amount);
            RefreshStatusBars();
        }

        public void HealPlayer(int amount)
        {
            currentHealth = Math.Min(maxHealth, currentHealth + amount);
            RefreshStatusBars();
        }

        public void EatFood(int amount)
        {
            currentFood = Math.Min(maxFood, currentFood + amount);
            RefreshStatusBars();
        }

        public void DrinkWater(int amount)
        {
            currentWater = Math.Min(maxWater, currentWater + amount);
            RefreshStatusBars();
        }

        // ----------------------------
        // Survival updater loop
        // ----------------------------

        public void StartStatusUpdater(float dt)
        {
            if (statusTimer != null)
                statusTimer.Stop();

            statusTimer = new System.Windows.Forms.Timer();
            statusTimer.Interval = (int)(dt * 1000); // Convert seconds to milliseconds

            statusTimer.Tick += (s, e) =>
            {
                // Reduce food + water
                currentFood = Math.Max(0, currentFood - 1);
                currentWater = Math.Max(0, currentWater - 2);

                // If starving or dehydrated, take health damage
                if (currentFood <= 0) DamagePlayer(1);
                if (currentWater <= 0) DamagePlayer(2);

                RefreshStatusBars();
            };

            statusTimer.Start();
        }

        #endregion


        #region Biome Setup
        private Label lbl_region = new Label();
        public string CurrentRegion;

        public void SetupRegionLabel()
        {
            lbl_region = new Label
            {
                AutoSize = true,
                Font = new Font("Segoe UI Black", 18F, FontStyle.Bold, GraphicsUnit.Point, 0),
                ForeColor = SystemColors.ButtonFace,
                Location = new Point(12, 9), // top-left corner
                Text = "region"
            };
            this.Controls.Add(lbl_region);
        }

        public void UpdateCurrentRegionName(string regionName)
        {
            CurrentRegion = regionName;
            lbl_region.Text = "r. " + regionName;
        }
        #endregion


        #region floating loot label setup
        private int lootStackOffset = 40;


        public void SpawnLootLabel(string itemName, Image icon, int amount)
        {
            if (icon == null || amount <= 0) return;

            // check if a floating loot of the same item already exists
            var existing = floatingLoots.FirstOrDefault(f => f.ItemName == itemName && !f.IsFalling);
            if (existing != null)
            {
                existing.AddAmount(amount); // add to existing
                return;
            }

            // --- Calculate a spawn position that avoids overlap ---
            int baseY = this.ClientSize.Height - 122;
            int offset = 0;
            int spacing = 36; // leave a bit more than IconBox height

            // move down until no existing loot is at that Y
            while (floatingLoots.Any(f => !f.IsFalling && Math.Abs(f.IconBox.Top - (baseY - offset)) < spacing))
            {
                offset += spacing;
            }

            Point startPos = new Point(10, baseY - offset);
            floatingLoots.Add(new FloatingLoot(this, itemName, icon, amount, startPos));
        }

        #endregion


        #region CraftMenu System

        // --- Fields ---
        private Panel CraftPanel;
        private Panel panel_CraftInfo;
        private NumericUpDown CraftAmount;
        private Label CM_ItemName, CM_Desc, lblWillGet;
        private PictureBox CM_ItemImage;
        private Button Btn_CraftItem;

        private PictureBox[] CM_RP_ITEM_Images;
        private Label[] CM_RP_LblAmounts;
        private Label[] CM_HaveAmounts;

        private TabControl tabControl;
        private Recipe currentRecipe;

        // --- Recipe ---
        public class Recipe
        {
            public string ResultName;
            public Image ResultIcon;
            public string Description;
            public ItemType ResultType { get; set; }
            public (string itemName, Image icon, int amountNeeded)[] Requirements;
            public int TabIndex;
            public int ResultAmount = 1; // default 1
        }

        // --- Reset Craft Info ---
        private void ResetCraftInfo()
        {
            currentRecipe = null;
            panel_CraftInfo.Visible = false;

            CM_ItemImage.Image = null;
            CM_ItemName.Text = "";
            CM_Desc.Text = "";
            lblWillGet.Text = "";

            for (int i = 0; i < 3; i++)
            {
                CM_RP_ITEM_Images[i].Visible = false;
                CM_RP_LblAmounts[i].Visible = false;
                CM_RP_LblAmounts[i].Text = "";
                CM_HaveAmounts[i].Visible = false;
                CM_HaveAmounts[i].Text = "";
            }

            CraftAmount.Value = 1;
            CraftAmount.Maximum = 99;
        }

        // --- Max Craftable Amount ---
        private int MaxCraftableAmount()
        {
            if (currentRecipe == null) return 1;

            int maxAmount = int.MaxValue;

            foreach (var req in currentRecipe.Requirements)
            {
                int have = CountInInventory(req.itemName);
                int maxForThis = have / req.amountNeeded;
                if (maxForThis < maxAmount)
                    maxAmount = maxForThis;
            }

            return Math.Max(1, maxAmount);
        }

        // --- Create Craft Menu (call once in Game_Load) ---
        public void CreateCraftMenu()
        {
            CraftPanel = new Panel
            {
                Size = new Size(920, 500),
                Location = new Point(490, 120),
                BackColor = Color.Black,
                BorderStyle = BorderStyle.FixedSingle,
                Visible = false
            };

            // Tab Control
            tabControl = new TabControl
            {
                Size = new Size(470, 460),
                Location = new Point(10, 10),
                Font = new Font("Segoe UI", 12, FontStyle.Bold)
            };

            string[] tabIcons = { "🍁", "⛏", "⚒️", "💊", "⛊", "📐", "🗝️" };
            foreach (var icon in tabIcons)
            {
                TabPage tab = new TabPage(icon) { BackColor = Color.Black };
                tabControl.TabPages.Add(tab);
            }

            CraftPanel.Controls.Add(tabControl);

            // Craft Info Panel
            panel_CraftInfo = new Panel
            {
                Size = new Size(400, 460),
                Location = new Point(480, 10),
                BackColor = Color.Black,
                BorderStyle = BorderStyle.Fixed3D,
                Visible = false
            };

            CM_ItemImage = new PictureBox
            {
                Size = new Size(100, 100),
                Location = new Point(20, 20),
                SizeMode = PictureBoxSizeMode.Zoom
            };
            panel_CraftInfo.Controls.Add(CM_ItemImage);

            CM_ItemName = new Label
            {
                ForeColor = Color.White,
                Font = new Font("Segoe UI", 14, FontStyle.Bold),
                Location = new Point(140, 20),
                AutoSize = true
            };
            panel_CraftInfo.Controls.Add(CM_ItemName);

            CM_Desc = new Label
            {
                ForeColor = Color.White,
                Font = new Font("Segoe UI", 10),
                Size = new Size(360, 60),
                Location = new Point(20, 130)
            };
            panel_CraftInfo.Controls.Add(CM_Desc);

            Label lblAmount = new Label
            {
                Text = "Amount:",
                ForeColor = Color.White,
                Font = new Font("Segoe UI", 11),
                Location = new Point(20, 200),
                AutoSize = true
            };
            panel_CraftInfo.Controls.Add(lblAmount);

            CraftAmount = new NumericUpDown
            {
                Minimum = 1,
                Maximum = 99,
                Value = 1,
                Font = new Font("Segoe UI", 11, FontStyle.Bold),
                Size = new Size(70, 30),
                Location = new Point(100, 195),
                TextAlign = HorizontalAlignment.Center
            };
            panel_CraftInfo.Controls.Add(CraftAmount);

            lblWillGet = new Label
            {
                Location = new Point(200, 200),
                Size = new Size(180, 25),
                ForeColor = Color.White,
                Font = new Font("Segoe UI", 10),
                Text = ""
            };
            panel_CraftInfo.Controls.Add(lblWillGet);

            CraftAmount.ValueChanged += (s, e) =>
            {
                if (currentRecipe != null)
                    lblWillGet.Text = $"You will get: {CraftAmount.Value * currentRecipe.ResultAmount} x {currentRecipe.ResultName}";
                UpdateCraftInfo();
            };

            // Requirement slots (max 3)
            CM_RP_ITEM_Images = new PictureBox[3];
            CM_RP_LblAmounts = new Label[3];
            CM_HaveAmounts = new Label[3];

            int yOffset = 240;
            for (int i = 0; i < 3; i++)
            {
                CM_RP_ITEM_Images[i] = new PictureBox
                {
                    Size = new Size(40, 40),
                    Location = new Point(20, yOffset),
                    SizeMode = PictureBoxSizeMode.Zoom
                };
                panel_CraftInfo.Controls.Add(CM_RP_ITEM_Images[i]);

                CM_RP_LblAmounts[i] = new Label
                {
                    ForeColor = Color.Maroon,
                    Font = new Font("Segoe UI Black", 12, FontStyle.Bold),
                    Location = new Point(70, yOffset + 5),
                    AutoSize = true
                };
                panel_CraftInfo.Controls.Add(CM_RP_LblAmounts[i]);

                CM_HaveAmounts[i] = new Label
                {
                    ForeColor = Color.Green,
                    Font = new Font("Segoe UI Black", 12, FontStyle.Bold),
                    Location = new Point(120, yOffset + 5),
                    AutoSize = true
                };
                panel_CraftInfo.Controls.Add(CM_HaveAmounts[i]);

                yOffset += 60;
            }

            Btn_CraftItem = new Button
            {
                Text = "CRAFT ITEM",
                ForeColor = Color.White,
                BackColor = Color.FromArgb(70, 70, 70),
                FlatStyle = FlatStyle.Flat,
                Font = new Font("Segoe UI", 12, FontStyle.Bold),
                Size = new Size(360, 40),
                Location = new Point(20, 400)
            };
            panel_CraftInfo.Controls.Add(Btn_CraftItem);

            Btn_CraftItem.Click += CraftItem;
            CraftPanel.Controls.Add(panel_CraftInfo);
            this.Controls.Add(CraftPanel);
            CraftPanel.BringToFront();
        }

        // --- Inventory Helpers ---
        private int CountInInventory(string itemName)
        {
            int count = 0;
            foreach (var item in allItems)
                if (item != null && item.Name == itemName)
                    count += item.Count;
            return count;
        }

        private void RemoveFromInventory(string itemName, int amount)
        {
            for (int i = 0; i < allItems.Length; i++)
            {
                var item = allItems[i];
                if (item != null && item.Name == itemName)
                {
                    if (item.Count > amount)
                    {
                        item.Count -= amount;
                        allSlots[i].Invalidate();
                        return;
                    }
                    else
                    {
                        amount -= item.Count;
                        allItems[i] = null;
                        allSlots[i].Invalidate();
                        if (amount <= 0) return;
                    }
                }
            }
        }

        // --- Add Recipe ---
        public void AddToCraftMenu(Recipe recipe)
        {
            int tabIndex = recipe.TabIndex;
            if (tabIndex < 0 || tabIndex >= tabControl.TabPages.Count)
                tabIndex = 0;

            PictureBox itemIcon = new PictureBox
            {
                Size = new Size(60, 60),
                SizeMode = PictureBoxSizeMode.Zoom,
                Image = recipe.ResultIcon,
                Cursor = Cursors.Hand
            };

            int count = tabControl.TabPages[tabIndex].Controls.Count;
            int x = 20 + (count % 6) * 70;
            int y = 20 + (count / 6) * 70;
            itemIcon.Location = new Point(x, y);

            tabControl.TabPages[tabIndex].Controls.Add(itemIcon);
            itemIcon.Click += (s, e) => ShowRecipe(recipe);
        }

        // --- Show Recipe ---
        private void ShowRecipe(Recipe recipe)
        {
            currentRecipe = recipe;
            panel_CraftInfo.Visible = true;

            CM_ItemImage.Image = recipe.ResultIcon;
            CM_ItemName.Text = recipe.ResultName;
            CM_Desc.Text = recipe.Description;

            for (int i = 0; i < 3; i++)
            {
                if (i < recipe.Requirements.Length)
                {
                    CM_RP_ITEM_Images[i].Visible = true;
                    CM_RP_ITEM_Images[i].Image = recipe.Requirements[i].icon;

                    CM_RP_LblAmounts[i].Visible = true;
                    CM_RP_LblAmounts[i].Text = recipe.Requirements[i].amountNeeded.ToString();

                    int have = CountInInventory(recipe.Requirements[i].itemName);
                    CM_HaveAmounts[i].Visible = true;
                    CM_HaveAmounts[i].Text = "/ " + have;

                    CM_HaveAmounts[i].ForeColor = have >= recipe.Requirements[i].amountNeeded
                                                 ? Color.Green
                                                 : Color.Maroon;
                }
                else
                {
                    CM_RP_ITEM_Images[i].Visible = false;
                    CM_RP_LblAmounts[i].Visible = false;
                    CM_RP_LblAmounts[i].Text = "";
                    CM_HaveAmounts[i].Visible = false;
                    CM_HaveAmounts[i].Text = "";
                }
            }

            Btn_CraftItem.Text = (recipe.TabIndex == 6 && !workbenchUnlocked)
                                 ? "UNLOCK WORKBENCH"
                                 : "CRAFT ITEM";

            CraftAmount.Value = 1;
            CraftAmount.Maximum = MaxCraftableAmount();
            lblWillGet.Text = $"You will get: {CraftAmount.Value * recipe.ResultAmount} x {recipe.ResultName}";
        }

        // --- Update Craft Info ---
        public void UpdateCraftInfo()
        {
            if (currentRecipe == null || !panel_CraftInfo.Visible) return;

            for (int i = 0; i < currentRecipe.Requirements.Length; i++)
            {
                var req = currentRecipe.Requirements[i];
                int have = CountInInventory(req.itemName);

                CM_HaveAmounts[i].Text = "/ " + have;
                CM_HaveAmounts[i].ForeColor = have >= req.amountNeeded * (int)CraftAmount.Value
                                             ? Color.Green
                                             : Color.Maroon;
            }

            CraftAmount.Maximum = MaxCraftableAmount();
            if (CraftAmount.Value > CraftAmount.Maximum)
                CraftAmount.Value = CraftAmount.Maximum;

            lblWillGet.Text = $"You will get: {CraftAmount.Value * currentRecipe.ResultAmount} x {currentRecipe.ResultName}";
        }

        // --- Craft Item ---
        private void CraftItem(object sender, EventArgs e)
        {
            if (currentRecipe == null) return;
            if (currentRecipe.TabIndex == 6 && !workbenchUnlocked) return;

            int craftCount = (int)CraftAmount.Value;

            // Check inventory
            foreach (var req in currentRecipe.Requirements)
                if (CountInInventory(req.itemName) < req.amountNeeded * craftCount)
                    return;

            // Remove materials
            foreach (var req in currentRecipe.Requirements)
                RemoveFromInventory(req.itemName, req.amountNeeded * craftCount);

            // Add crafted items
            AddItem(new InventoryItem
            {
                Name = currentRecipe.ResultName,
                Icon = currentRecipe.ResultIcon,
                Count = craftCount * currentRecipe.ResultAmount, // <-- Use ResultAmount
                Type = currentRecipe.ResultType
            });
            SpawnLootLabel(currentRecipe.ResultName, currentRecipe.ResultIcon, currentRecipe.ResultAmount);

            UpdateCraftInfo();
        }

        #endregion
        #region FurnaceX System

        // --- FurnaceX Recipe ---
        public class FurnaceXRecipe
        {
            public string BurnItemName;
            public Image BurnItemIcon;
            public string SmeltItemName;
            public Image SmeltItemIcon;
            public (string itemName, Image icon, int amount)[] Outputs;
        }

        // --- FurnaceX Fields ---
        private Panel FurnaceX_Panel;
        private PictureBox[] furnaceSlots;
        private InventoryItem[] furnaceItems = new InventoryItem[4]; // 0=burn,1=smelt,2=out1,3=out2
        private Button FurnaceX_btnSmelt;
        private List<FurnaceXRecipe> FurnaceX_Recipes = new();
        private Label lbl_furnaceName = new Label();
        private FurnaceXRecipe FurnaceX_CurrentRecipe;

        // --- Drag info for Furnace ---
        private DragInfo furnaceDragInfo;

        // --- Smelting Timer ---
        private Timer furnaceTimer;
        private float furnaceProgress = 0f; // 0–1 progress of current smelt
        private const float SmeltDuration = 3f; // seconds per smelt

        // --- Initialize Furnace UI ---
        private void InitFurnaceX()
        {
            FurnaceX_Panel = new Panel
            {
                Size = new Size(600, 180),
                Location = new Point(653, 500),
                BackColor = Color.Black,
                Visible = false
            };
            this.Controls.Add(FurnaceX_Panel);

            furnaceSlots = new PictureBox[4];
            for (int i = 0; i < 4; i++)
            {
                furnaceSlots[i] = new PictureBox
                {
                    Size = new Size(80, 80),
                    Location = new Point(20 + i * 140, 40),
                    BorderStyle = BorderStyle.FixedSingle,
                    SizeMode = PictureBoxSizeMode.StretchImage,
                    Image = emptySlotIcon
                };
                furnaceSlots[i].MouseDown += FurnaceSlot_MouseDown;
                furnaceSlots[i].MouseUp += FurnaceSlot_MouseUp;
                furnaceSlots[i].Paint += FurnaceSlot_Paint;
                furnaceSlots[i].MouseMove += Slot_MouseMove;
                FurnaceX_Panel.Controls.Add(furnaceSlots[i]);
            }

            lbl_furnaceName = new Label
            {
                AutoSize = true,
                Font = new Font("Segoe UI Black", 18F, FontStyle.Bold, GraphicsUnit.Point, 0),
                ForeColor = SystemColors.ButtonFace,
                Location = new Point(12, 9),
                Name = "lbl_furnaceName",
                Text = "FURNACE MAGIC"
            };
            FurnaceX_Panel.Controls.Add(lbl_furnaceName);

            FurnaceX_btnSmelt = new Button
            {
                Text = "MAGICAL SMELT",
                Size = new Size(580, 30),
                Location = new Point(10, 140),
                BackColor = Color.White,
                ForeColor = Color.Black
            };
            FurnaceX_btnSmelt.Click += (s, e) => FurnaceX_SmeltRecipe();
            FurnaceX_Panel.Controls.Add(FurnaceX_btnSmelt);
        }

        // --- Drag start from furnace (any slot, including outputs) ---
        private void FurnaceSlot_MouseDown(object sender, MouseEventArgs e)
        {
            PictureBox slot = sender as PictureBox;
            int index = Array.IndexOf(furnaceSlots, slot);
            if (furnaceItems[index] == null) return;

            InventoryItem item = furnaceItems[index];

            draggedItem = new PictureBox
            {
                Image = item.Icon,
                Size = slot.Size,
                SizeMode = PictureBoxSizeMode.StretchImage,
                BackColor = Color.Transparent
            };

            Point mousePos = System.Windows.Forms.Cursor.Position;
            draggedItem.Left = mousePos.X - e.Location.X - dragForm.Left;
            draggedItem.Top = mousePos.Y - e.Location.Y - dragForm.Top;

            dragForm.Controls.Add(draggedItem);
            draggedItem.BringToFront();

            furnaceDragInfo = new DragInfo
            {
                Index = index,
                Item = item,
                Offset = e.Location,
                FromFurnace = true
            };

            // ❌ Do NOT set furnaceItems[index] = null here
        }

        // --- Drop into Furnace Slot or Inventory ---
        private void FurnaceSlot_MouseUp(object sender, MouseEventArgs e)
        {
            if (furnaceDragInfo == null || !furnaceDragInfo.FromFurnace) return;

            Point mousePos = System.Windows.Forms.Cursor.Position;

            // --- Check if over any furnace slot ---
            for (int i = 0; i < furnaceSlots.Length; i++)
            {
                Rectangle slotRect = furnaceSlots[i].RectangleToScreen(furnaceSlots[i].ClientRectangle);
                if (slotRect.Contains(mousePos))
                {
                    // ❌ Furnace → Furnace is NOT allowed, snap back
                    Point originalPos = furnaceSlots[furnaceDragInfo.Index].PointToScreen(Point.Empty);
                    if (draggedItem != null)
                    {
                        draggedItem.Left = originalPos.X - dragForm.Left;
                        draggedItem.Top = originalPos.Y - dragForm.Top;
                        draggedItem.BringToFront();
                    }

                    furnaceSlots[furnaceDragInfo.Index].Invalidate();
                    draggedItem?.Dispose();
                    draggedItem = null;
                    furnaceDragInfo = null;
                    return; // Early exit
                }
            }

            // --- Check inventory slots ---
            int invTargetIndex = -1;
            for (int i = 0; i < TOTAL_SLOTS; i++)
            {
                Rectangle slotRect = allSlots[i].RectangleToScreen(allSlots[i].ClientRectangle);
                if (slotRect.Contains(mousePos))
                {
                    invTargetIndex = i;
                    break;
                }
            }

            if (invTargetIndex != -1) // Furnace → Inventory
            {
                InventoryItem dragged = furnaceDragInfo.Item;
                InventoryItem invTarget = allItems[invTargetIndex];

                if (invTarget == null)
                    allItems[invTargetIndex] = dragged;
                else if (invTarget.Name == dragged.Name)
                    invTarget.Count += dragged.Count;
                else
                    allItems[invTargetIndex] = dragged;

                furnaceItems[furnaceDragInfo.Index] = null;

                // Snap draggedItem to inventory
                if (draggedItem != null)
                {
                    Point slotPos = allSlots[invTargetIndex].PointToScreen(Point.Empty);
                    draggedItem.Left = slotPos.X - dragForm.Left;
                    draggedItem.Top = slotPos.Y - dragForm.Top;
                    draggedItem.BringToFront();
                }

                allSlots[invTargetIndex].Invalidate();
                furnaceSlots[furnaceDragInfo.Index].Invalidate();
            }
            else // Dropped outside → snap back
            {
                Point originalPos = furnaceSlots[furnaceDragInfo.Index].PointToScreen(Point.Empty);
                if (draggedItem != null)
                {
                    draggedItem.Left = originalPos.X - dragForm.Left;
                    draggedItem.Top = originalPos.Y - dragForm.Top;
                    draggedItem.BringToFront();
                }
            }

            draggedItem?.Dispose();
            draggedItem = null;
            furnaceDragInfo = null;
        }

        // --- Paint Furnace Slots ---
        private void FurnaceSlot_Paint(object sender, PaintEventArgs e)
        {
            PictureBox slot = sender as PictureBox;
            int index = Array.IndexOf(furnaceSlots, slot);
            InventoryItem item = furnaceItems[index];
            Image icon = item?.Icon ?? emptySlotIcon;

            e.Graphics.DrawImage(icon, new Rectangle(0, 0, slot.Width, slot.Height));

            // Draw stack count
            if (item != null && item.Count > 1)
            {
                using Brush brush = new SolidBrush(Color.White);
                using Font font = new Font("Arial", 10, FontStyle.Bold);
                e.Graphics.DrawString(item.Count.ToString(), font, brush, new PointF(slot.Width - 18, slot.Height - 18));
            }

            // Smelt progress overlay for output slots (2 & 3)
            if ((index == 2 || index == 3) && furnaceSmelting)
            {
                int height = (int)(slot.Height * furnaceProgress);
                e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(120, Color.OrangeRed)),
                    new Rectangle(0, slot.Height - height, slot.Width, height));
            }
        }

        // --- Smelt button ---
        private bool furnaceSmelting = false;
        private void FurnaceX_SmeltRecipe()
        {
            InventoryItem burnItem = furnaceItems[0];
            InventoryItem smeltItem = furnaceItems[1];

            if (burnItem == null || smeltItem == null)
                return;

            FurnaceX_CurrentRecipe = FurnaceX_Recipes
                .FirstOrDefault(r =>
                    r.BurnItemName.ToLower() == burnItem.Name.ToLower() &&
                    r.SmeltItemName.ToLower() == smeltItem.Name.ToLower());

            if (FurnaceX_CurrentRecipe == null)
                return;

            furnaceProgress = 0f;
            furnaceSmelting = true;
            FurnaceX_btnSmelt.Enabled = false;
        }


        // --- Timer Tick ---
        public void UpdateFurnace(float dt)
        {
            if (!furnaceSmelting || FurnaceX_CurrentRecipe == null)
                return;

            // Advance progress
            furnaceProgress += dt / SmeltDuration;

            // Refresh furnace slots
            foreach (var slot in furnaceSlots)
                slot.Invalidate();

            // Check if smelting finished
            if (furnaceProgress >= 1f)
            {
                furnaceProgress = 0f;
                furnaceSmelting = false;
                FurnaceX_btnSmelt.Enabled = true;
                CompleteSmelt();
            }
        }


        // --- Complete smelt output ---
        // Complete smelt outputs (same as before)
        private void CompleteSmelt()
        {
            InventoryItem burnItem = furnaceItems[0];
            InventoryItem smeltItem = furnaceItems[1];

            if (burnItem == null || smeltItem == null || FurnaceX_CurrentRecipe == null)
                return;

            burnItem.Count--;
            if (burnItem.Count <= 0) furnaceItems[0] = null;

            smeltItem.Count--;
            if (smeltItem.Count <= 0) furnaceItems[1] = null;

            for (int i = 0; i < FurnaceX_CurrentRecipe.Outputs.Length && i < 2; i++)
            {
                var output = FurnaceX_CurrentRecipe.Outputs[i];
                InventoryItem outputSlot = furnaceItems[i + 2];

                if (outputSlot == null)
                {
                    furnaceItems[i + 2] = new InventoryItem
                    {
                        Name = output.itemName,
                        Icon = output.icon,
                        Count = output.amount,
                        Type = ItemType.Resource
                    };
                }
                else if (outputSlot.Name == output.itemName)
                {
                    outputSlot.Count += output.amount;
                }
                else
                {
                    AddItem(new InventoryItem
                    {
                        Name = output.itemName,
                        Icon = output.icon,
                        Count = output.amount,
                        Type = ItemType.Resource
                    });
                }
            }

            foreach (var slot in furnaceSlots) slot.Invalidate();
            FurnaceX_CurrentRecipe = null;
        }


        // --- Show / Hide Furnace ---
        public void ShowFurnaceX() { FurnaceX_Panel.Visible = true; p_inventory.Visible = true; CraftPanel.Visible = false; }
        public void HideFurnaceX() { FurnaceX_Panel.Visible = false; }

        public void FurnaceX_AddRecipe(FurnaceXRecipe recipe) { FurnaceX_Recipes.Add(recipe); }

        #endregion




        #region Console Setup

        private void InitConsole()
        {
            panel_Console = new Panel
            {
                BackColor = Color.FromArgb(200, 0, 0, 0), // semi-transparent black
                Size = new Size(500, 300),
                Location = new Point(10, this.ClientSize.Height - 290),
                Visible = false // hidden by default
            };

            rtb_Console = new RichTextBox
            {
                BackColor = Color.Black,
                ForeColor = Color.PaleVioletRed,
                BorderStyle = BorderStyle.None,
                Location = new Point(5, 5),
                Size = new Size(490, 250),
                ReadOnly = true
            };

            tb_CommandInput = new System.Windows.Forms.TextBox
            {
                Location = new Point(5, 265),
                Size = new Size(400, 23),
                BorderStyle = BorderStyle.FixedSingle
            };

            btn_executeCommand = new System.Windows.Forms.Button
            {
                Text = "Execute",
                Location = new Point(410, 264),
                Size = new Size(80, 25),
                ForeColor = Color.White
            };

            btn_executeCommand.Click += ExecuteCommand;
            tb_CommandInput.KeyDown += Tb_CommandInput_KeyDown;

            panel_Console.Controls.Add(rtb_Console);
            panel_Console.Controls.Add(tb_CommandInput);
            panel_Console.Controls.Add(btn_executeCommand);

            this.Controls.Add(panel_Console);
        }


        private void Tb_CommandInput_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                ExecuteCommand(sender, EventArgs.Empty);
                e.SuppressKeyPress = true; // stop ding sound
            }
            else if (e.KeyCode == Keys.Escape)
            {
                tb_CommandInput.Clear();  // optional: clear input
                e.SuppressKeyPress = true; // stop ding
            }
        }

        private void ExecuteCommand(object sender, EventArgs e)
        {
            string cmd = tb_CommandInput.Text.Trim();
            if (string.IsNullOrEmpty(cmd)) return;

            // log command
            rtb_Console.AppendText($"> {cmd}\n");

            // handle commands (basic example)
            string response = GiveHelper.HandleCommand(cmd, this);
            rtb_Console.AppendText(response + "\n");

            tb_CommandInput.Clear();
        }

        #endregion


        #region lootbox setup and logic

        private Panel dragLayer;
        private Label lootTitle;


        private PictureBox lootDraggedItem;
        private Label lootDraggedCount;
        private LootSlot lootDragInfo;
        private Point lootDragOffset;



        // Either rename the methods
        private void LootSlot_MouseMove(object sender, MouseEventArgs e)
        {
            LootDraggedItem_MouseMove(sender, e);
        }

        private void LootSlot_MouseUp(object sender, MouseEventArgs e)
        {
            LootDraggedItem_MouseUp(sender, e);
        }

        public Point WorldToScreen(Vector3 worldPos)
        {
            Vector4 clipSpacePos = new Vector4(worldPos, 1f);

            Matrix4 view = rnd.camera.GetViewMatrix();
            Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView(
                MathHelper.DegreesToRadians(70f),
                Math.Max(1, rnd.Width) / (float)Math.Max(1, rnd.Height),
                0.1f, 1000f
            );

            clipSpacePos = Vector4.TransformRow(clipSpacePos, view);
            clipSpacePos = Vector4.TransformRow(clipSpacePos, projection);

            if (clipSpacePos.W != 0)
            {
                clipSpacePos.X /= clipSpacePos.W;
                clipSpacePos.Y /= clipSpacePos.W;
                clipSpacePos.Z /= clipSpacePos.W;
            }

            int x = (int)((clipSpacePos.X * 0.5f + 0.5f) * this.ClientSize.Width);
            int y = (int)((-clipSpacePos.Y * 0.5f + 0.5f) * this.ClientSize.Height);

            return new Point(x, y);
        }

        public int AddItemPartial(InventoryItem newItem)
        {
            int remaining = newItem.Count;

            for (int i = 0; i < allItems.Length; i++)
            {
                var slot = allItems[i];

                if (slot == null)
                {
                    allItems[i] = new InventoryItem
                    {
                        Name = newItem.Name,
                        Icon = newItem.Icon,
                        Type = newItem.Type,
                        Count = remaining,
                        UID = newItem.UID
                    };
                    allSlots[i].Invalidate();
                    return 0; // all added
                }
                else if (slot.Name == newItem.Name &&
                         slot.Type != ItemType.Weapon &&
                         slot.Type != ItemType.build)
                {
                    int space = MAX_STACK - slot.Count;
                    int toAdd = Math.Min(space, remaining);
                    slot.Count += toAdd;
                    remaining -= toAdd;
                    allSlots[i].Invalidate();
                    if (remaining <= 0) return 0;
                }
            }

            return remaining; // leftover if inventory full
        }

        private void SetupLootPanel()
        {
            lootPanel = new Panel()
            {
                Size = new Size(8 * 60 + 10, 2 * 60 + 20),
                Location = new Point(p_inventory.Right + 10, p_inventory.Top),
                BackColor = Color.FromArgb(150, 50, 50, 50),
                Visible = false
            };

            lootTitle = new Label
            {
                Text = "[LootBox] Press R to take all",
                ForeColor = Color.LightYellow,
                BackColor = Color.Transparent,
                Font = new Font("Arial", 10, FontStyle.Bold),
                Location = new Point(0, 0),
                AutoSize = true
            };
            lootPanel.Controls.Add(lootTitle);

            lootSlots = new LootSlot[2, 8];
            int slotSize = 60;
            int padding = 10;

            for (int y = 0; y < 2; y++)
            {
                for (int x = 0; x < 8; x++)
                {
                    PictureBox slot = new PictureBox
                    {
                        Size = new Size(slotSize, slotSize),
                        Location = new Point(x * (slotSize + padding), y * (slotSize + padding) + 20),
                        BackColor = Color.FromArgb(60, 60, 60),
                        SizeMode = PictureBoxSizeMode.StretchImage,
                        Cursor = Cursors.Hand,
                        Tag = new Point(x, y),
                        Image = emptySlotIcon
                    };

                    slot.Paint += LootSlot_Paint;

                    // Drag events
                    slot.MouseDown += LootSlot_MouseDown;
                    slot.MouseMove += LootSlot_MouseMove;
                    slot.MouseUp += LootSlot_MouseUp;

                    lootPanel.Controls.Add(slot);

                    Label countLabel = new Label
                    {
                        Size = new Size(slot.Width, 20),
                        Location = new Point(slot.Left, slot.Bottom - 18),
                        ForeColor = Color.White,
                        BackColor = Color.Transparent,
                        TextAlign = ContentAlignment.BottomRight
                    };
                    lootPanel.Controls.Add(countLabel);

                    lootSlots[y, x] = new LootSlot
                    {
                        IconBox = slot,
                        CountLabel = countLabel,
                        Item = null
                    };
                }
            }

            this.Controls.Add(lootPanel);
        }

        private void OpenLootPanel(LootBox box)
        {
            if (box == null || box.LootItems.Count == 0)
            {
                lootPanel.Visible = false;
                return;
            }

            lootPanel.Visible = true;
            lootPanel.BringToFront();

            foreach (var slot in lootSlots)
            {
                slot.Item = null;
                slot.IconBox.Image = emptySlotIcon;
                slot.CountLabel.Text = "";
            }

            for (int i = 0; i < box.LootItems.Count && i < lootSlots.Length; i++)
            {
                int row = i / lootSlots.GetLength(1);
                int col = i % lootSlots.GetLength(1);
                var item = box.LootItems[i];

                lootSlots[row, col].Item = item;
                lootSlots[row, col].IconBox.Image = item.Icon ?? emptySlotIcon;
                lootSlots[row, col].CountLabel.Text = item.Count > 1 ? item.Count.ToString() : "";
            }

            lootTitle.Text = "[LootBox] Press R to take all";
        }


        private void LootSlot_Paint(object sender, PaintEventArgs e)
        {
            PictureBox slot = sender as PictureBox;
            Point pt = (Point)slot.Tag;
            var lootSlot = lootSlots[pt.Y, pt.X];

            Image icon = lootSlot.Item?.Icon ?? Properties.Resources.Slot;
            e.Graphics.DrawImage(icon, new Rectangle(0, 0, slot.Width, slot.Height));

            if (lootSlot.Item != null && lootSlot.Item.Count > 1)
            {
                using (Brush brush = new SolidBrush(Color.White))
                using (Font font = new Font("Arial", 10, FontStyle.Bold))
                {
                    e.Graphics.DrawString(lootSlot.Item.Count.ToString(), font, brush,
                        new PointF(slot.Width - 18, slot.Height - 18));
                }
            }

            using (Pen pen = new Pen(Color.Yellow, 3))
            using (GraphicsPath path = RoundedRect(new Rectangle(0, 0, slot.Width, slot.Height), 6))
            {
                // Optional: highlight if dragging or selected
                // e.Graphics.DrawPath(pen, path);
            }
        }

        private void LootSlot_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;

            PictureBox slotBox = sender as PictureBox;
            Point pt = (Point)slotBox.Tag;
            LootSlot slot = lootSlots[pt.Y, pt.X];
            if (slot.Item == null) return;

            // Start dragging
            lootDraggedItem = new PictureBox
            {
                Image = slot.Item.Icon,
                Size = slotBox.Size,
                SizeMode = PictureBoxSizeMode.StretchImage,
                BackColor = Color.Transparent
            };

            lootDraggedCount = new Label
            {
                Text = slot.Item.Count > 1 ? slot.Item.Count.ToString() : "",
                ForeColor = Color.White,
                BackColor = Color.Transparent,
                AutoSize = true
            };

            lootDragInfo = slot;
            lootDragOffset = e.Location;

            this.Controls.Add(lootDraggedItem);
            this.Controls.Add(lootDraggedCount);
            lootDraggedItem.BringToFront();
            lootDraggedCount.BringToFront();

            slot.Item = null; // temporarily remove
            slot.IconBox.Invalidate();
        }


        private void LootDraggedItem_MouseMove(object sender, MouseEventArgs e)
        {
            if (lootDraggedItem == null) return;

            Point mousePos = this.PointToClient(System.Windows.Forms.Cursor.Position);
            lootDraggedItem.Left = mousePos.X - lootDragOffset.X;
            lootDraggedItem.Top = mousePos.Y - lootDragOffset.Y;

            lootDraggedCount.Left = lootDraggedItem.Left;
            lootDraggedCount.Top = lootDraggedItem.Top;
        }

        private void LootDraggedItem_MouseUp(object sender, MouseEventArgs e)
        {
            if (lootDraggedItem == null || lootDragInfo == null) return;

            Point mousePos = System.Windows.Forms.Cursor.Position;

            // Try dropping into inventory
            int leftover = AddItemPartial(new InventoryItem
            {
                Name = lootDragInfo.Item?.Name,
                Icon = lootDragInfo.Item?.Icon,
                Count = lootDragInfo.Item?.Count ?? 1,
                Type = lootDragInfo.Item?.Type ?? ItemType.Resource,
                UID = lootDragInfo.Item?.UID ?? Guid.NewGuid()
            });

            // Clear loot slot visuals
            lootDragInfo.Item = null;
            lootDragInfo.IconBox.Image = Properties.Resources.Slot;
            lootDragInfo.CountLabel.Text = "";

            this.Controls.Remove(lootDraggedItem);
            this.Controls.Remove(lootDraggedCount);
            lootDraggedItem.Dispose();
            lootDraggedCount.Dispose();
            lootDraggedItem = null;
            lootDraggedCount = null;
            lootDragInfo = null;
        }


        private void UpdateLootboxLabel()
        {
            if (RenderForm.nearbyBox != null)
            {
                float dist = (rnd.camera.Position - RenderForm.nearbyBox.Position).Length;

                if (dist <= LOOTBOX_RADIUS)
                {
                    Point screenPos = WorldToScreen(RenderForm.nearbyBox.Position + new Vector3(0, 2f, 0));

                    if (lootboxLabel == null)
                    {
                        lootboxLabel = new Label
                        {
                            Text = "[LootBox] Press R to take all",
                            AutoSize = true,
                            ForeColor = Color.Yellow,
                            BackColor = Color.Transparent
                        };
                        this.Controls.Add(lootboxLabel);
                        lootboxLabel.BringToFront();
                    }

                    lootboxLabel.Left = screenPos.X - lootboxLabel.Width / 2;
                    lootboxLabel.Top = screenPos.Y - lootboxLabel.Height;

                    // Optional: fade based on distance
                    float opacity = Math.Clamp(1 - (dist / LOOTBOX_RADIUS), 0, 1);
                    lootboxLabel.ForeColor = Color.FromArgb((int)(opacity * 255), Color.Yellow);
                }
                else RemoveLootboxLabel();
            }
            else RemoveLootboxLabel();
        }

        private void TakeAllLoot(LootBox box)
        {
            foreach (var item in box.LootItems)
            {
                AddItem(item);
            }
            box.LootItems.Clear();

            foreach (var ls in lootSlots)
            {
                ls.Item = null;
                ls.IconBox.Invalidate();
                ls.CountLabel.Text = "";
            }

            lootPanel.Visible = false;
        }

        private void RemoveLootboxLabel()
        {
            if (lootboxLabel != null)
            {
                this.Controls.Remove(lootboxLabel);
                lootboxLabel.Dispose();
                lootboxLabel = null;
            }
        }

        #endregion


        #region pickup Loot
        private void LootSlot_Click(object sender, EventArgs e)
        {
            PictureBox slot = sender as PictureBox;
            if (slot?.Tag is InventoryItem item)
            {
                if (AddItem(item))
                {
                    slot.Tag = null;
                    slot.Image = null;
                }
            }
        }
        #endregion

        #region Keys
        private void Game_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Tab)
            {
                inventoryVisible = !inventoryVisible;

                // Show/hide inventory UI
                p_inventory.Visible = inventoryVisible;
                CraftPanel.Visible = inventoryVisible;
                workbenchUnlocked = false;
                HideFurnaceX();

                // Update RenderForm cursor
                if (rnd != null)
                {
                    rnd.InventoryOpen = inventoryVisible;
                }

                // Reset dragged item
                if (draggedItem != null)
                {
                    // Put it back to its original slot
                    var originalSlot = (PictureBox)draggedItem.Tag; // assume you stored original slot in Tag
                    draggedItem.Location = originalSlot.PointToClient(this.PointToScreen(originalSlot.Location));
                    draggedItem.BringToFront();

                    draggedItem = null;
                }

                if (!inventoryVisible)
                    ResetCraftInfo();
            }

            if (e.KeyCode >= Keys.F12)
            {
                rnd.InventoryOpen = true;
                this.AutoScaleDimensions = new SizeF(96F, 96F); // default Windows DPI
            }

            if (e.KeyCode >= Keys.D1 && e.KeyCode <= Keys.D8)
                SelectSlot(e.KeyCode - Keys.D1);


            if (e.KeyCode == Keys.E)
                UseItem(selectedSlot);


            if (e.KeyCode == Keys.E && RenderForm.nearbyBox != null)
            {
                OpenLootPanel(RenderForm.nearbyBox);
            }

            if (e.KeyCode == Keys.E)
            {
                if (RenderForm.nearbyBox != null)
                {
                    OpenLootPanel(RenderForm.nearbyBox);
                }
            }

            if (e.KeyCode == Keys.R && RenderForm.nearbyBox != null)
            {
                TakeAllLoot(RenderForm.nearbyBox);
                lootPanel.Visible = false;

                // Dispose any dragged item
                if (lootDraggedItem != null)
                {
                    lootDraggedItem.Dispose();
                    lootDraggedItem = null;
                    lootDragInfo = null;
                }
            }


            if (e.KeyCode == Keys.Escape)
            {
                workbenchUnlocked = false;


                if (panel_Console.Visible == true) panel_Console.Visible = !panel_Console.Visible;
                HideFurnaceX();

            }

            // --- Console toggle (F1) ---
            if (e.KeyCode == Keys.F1)
            {
                panel_Console.Visible = !panel_Console.Visible;

                if (panel_Console.Visible)
                {
                    tb_CommandInput.Focus();
                    panel_Console.BringToFront();
                }
                else
                {
                    this.Focus(); // return focus to game
                }
            }
        }

        private void Game_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta > 0) // scrolled up → previous slot
                selectedSlot = (selectedSlot - 1 + 8) % 8;
            else if (e.Delta < 0) // scrolled down → next slot
                selectedSlot = (selectedSlot + 1) % 8;

            SelectSlot(selectedSlot);
        }

        private void Game_KeyUp(object sender, KeyEventArgs e)
        {

        }

        // Called when left click starts mining
        public void StartMining(dynamic target, float duration)
        {
            if (target == null || target.IsDestroyed) return;

            isMining = true;
            miningTarget = target;
            miningDuration = duration;
            miningTimer = 0f;

            // Show health bar initially
            ShowHealthBar(target.Health, target.MaxHealth, target.GetType().Name);
        }

        // Called when left click is released
        public void StopMining()
        {
            isMining = false;
            miningTarget = null;
            miningTimer = 0f;

            if (healthBox != null) healthBox.Visible = false;
            if (healthBar != null) healthBar.Visible = false;
        }

        // Called every frame
        public void UpdateMining(float deltaTime)
        {
            if (!isMining || miningTarget == null) return;

            // Get selected tool
            InventoryItem tool = null;
            if (selectedSlot >= 0 && selectedSlot < allItems.Length)
                tool = allItems[selectedSlot];

            // Get speed from helper
            float miningSpeed = MiningHelper.GetMiningSpeed(tool, miningTarget);

            // Update mining timer
            miningTimer += deltaTime * miningSpeed;
            float progress = miningTimer / miningDuration;

            // Show health bar
            UpdateHealthBar(miningTarget.Health, miningTarget.MaxHealth);
            if (healthBox != null) healthBox.Visible = true;
            if (healthBar != null) healthBar.Visible = true;

            // Apply mining hit
            if (progress >= 1f)
            {
                miningTarget.Mine(this);

                Random rnd = new Random();
                if (rnd.NextDouble() < 0.10) ModifyFood(-5);
                if (rnd.NextDouble() < 0.15) ModifyWater(-5);

                miningTimer = 0f;

                if (miningTarget.IsDestroyed)
                    StopMining();
            }
        }
        #endregion


        #region Add Items at start
        private void SetupExampleItems()
        {
            if (isTesting)
            {
                AddItem(new InventoryItem
                {
                    Icon = Properties.Resources.MetalAxe,
                    Name = "Metal_Axe",
                    Count = 1,
                    Type = ItemType.Weapon
                });
                AddItem(new InventoryItem
                {
                    Icon = Properties.Resources.MetalAxe,
                    Name = "Metal_Axe",
                    Count = 1,
                    Type = ItemType.Weapon
                });
                AddItem(new InventoryItem
                {
                    Icon = Properties.Resources.StoneIcon,
                    Name = "Stone",
                    Count = 12,
                    Type = ItemType.Resource
                });

                AddItem(new InventoryItem
                {
                    Icon = Properties.Resources.WorkBenchIcon,
                    Name = "WorkBench",
                    Count = 1,
                    Type = ItemType.Tool
                });

                AddItem(new InventoryItem
                {
                    Icon = Properties.Resources.Wood,
                    Name = "Wood",
                    Count = 5,
                    Type = ItemType.Resource
                });

                AddItem(new InventoryItem
                {
                    Icon = Properties.Resources.Metal_Ore_Icon,
                    Name = "Metal_Shard",
                    Count = 3,
                    Type = ItemType.Resource
                });

                AddItem(new InventoryItem
                {
                    Icon = Properties.Resources.CampFireItem,
                    Name = "Campfire",
                    Count = 1,
                    Type = ItemType.Tool
                });
                AddItem(new InventoryItem
                {
                    Icon = Properties.Resources.CampFireItem,
                    Name = "Campfire",
                    Count = 1,
                    Type = ItemType.Tool
                });

                AddItemToHotbar(new InventoryItem
                {
                    Icon = Properties.Resources.MetalAxe,
                    Name = "Metal_Axe",
                    Count = 1,
                    Type = ItemType.Weapon
                }, 0); // adds Metal Axe to first hotbar slot

                AddItemToHotbar(new InventoryItem
                {
                    Icon = Properties.Resources.Water,
                    Name = "Water_Jar",
                    Count = 1,
                    Type = ItemType.Consumable
                }, 1); // adds Metal Axe to first hotbar slot
            }

            AddItemToHotbar(new InventoryItem
            {
                Icon = Properties.Resources.Painting1,
                Name = "Common_Item_Card",
                Count = 1,
                Type = ItemType.common_painting
            }, 0); // adds Metal Axe to first hotbar slot
        }


        #endregion



        #region Inventory setup and logic
        private void SetupSlots()
        {
            allSlots = new PictureBox[TOTAL_SLOTS];
            allItems = new InventoryItem[TOTAL_SLOTS];

            int slotSize = 60;
            int padding = 10;

            // Hotbar panel (last row)
            p_hotbar = new Panel { BackColor = Color.Transparent, Height = slotSize + 20 };
            this.Controls.Add(p_hotbar);

            // Inventory panel (rows above hotbar)
            p_inventory = new Panel
            {
                BackColor = Color.Transparent,
                Height = (slotSize + padding) * (ROWS - 1) + padding + 20,
                Visible = false // <- only inventory is hidden initially
            };

            this.Controls.Add(p_inventory);

            slotTooltip = new ToolTip();

            for (int r = 0; r < ROWS; r++)
            {
                for (int c = 0; c < COLS; c++)
                {
                    int index = r * COLS + c;

                    PictureBox slot = new PictureBox
                    {
                        Size = new Size(slotSize, slotSize),
                        Location = new Point(padding + c * (slotSize + padding),
                                             padding + (r % (ROWS - 1)) * (slotSize + padding)),
                        BackColor = Color.Yellow,
                        SizeMode = PictureBoxSizeMode.StretchImage,
                        Cursor = Cursors.Hand,
                        Tag = index,
                        Image = emptySlotIcon
                    };
                    slot.MouseEnter += (s, e) =>
                    {
                        int idx = (int)slot.Tag;
                        slot.BackColor = Color.FromArgb(100, 100, 100);

                        if (allItems[idx] != null)
                            slotTooltip.SetToolTip(slot, $"{allItems[idx].Name} x{allItems[idx].Count}");
                    };

                    slot.MouseLeave += (s, e) =>
                    {
                        slot.BackColor = Color.FromArgb(60, 60, 60);
                        // No need to hide the tooltip manually
                    };

                    slot.MouseLeave += (s, e) => slot.BackColor = Color.FromArgb(60, 60, 60);
                    slot.MouseDown += Slot_MouseDown;
                    slot.MouseMove += Slot_MouseMove;
                    slot.MouseUp += Slot_MouseUp;
                    slot.Paint += Slot_Paint;


                    slot.MouseUp += (s, e) =>
                    {
                        if (draggedItem != null) return; // ignore clicks while dragging
                        int idx = (int)slot.Tag;
                        if (e.Button == MouseButtons.Right && allItems[idx] != null)
                        {
                            selectedSlot = idx;
                            slotMenu.Show(System.Windows.Forms.Cursor.Position); // screen coordinates
                        }
                    };
                    allSlots[index] = slot;




                    if (r == ROWS - 1)
                        p_hotbar.Controls.Add(slot); // hotbar row
                    else
                        p_inventory.Controls.Add(slot); // inventory rows
                }
            }
        }

        private void PositionPanels()
        {
            int padding = 10;

            // Hotbar centered at bottom
            p_hotbar.Width = COLS * (60 + padding) + padding;
            p_hotbar.Left = (this.ClientSize.Width - p_hotbar.Width) / 2;
            p_hotbar.Top = this.ClientSize.Height - p_hotbar.Height - 10;

            // Inventory above hotbar
            p_inventory.Width = COLS * (60 + padding) + padding;
            p_inventory.Left = (this.ClientSize.Width - p_inventory.Width) / 2;
            p_inventory.Top = p_hotbar.Top - p_inventory.Height - 10;
        }

        private void SetupTooltipsAndContext()
        {
            slotMenu = new ContextMenuStrip();
            slotMenu.Items.Add("Consume", null, (s, e) => UseItem(selectedSlot)); // 0
            slotMenu.Items.Add("Use", null, (s, e) => InspectItem(selectedSlot)); // 1
            slotMenu.Items.Add("Inspect", null, (s, e) => InspectItem(selectedSlot)); // 2
            slotMenu.Items.Add("Split", null, (s, e) => SplitItem(selectedSlot)); // 3
            slotMenu.Items.Add("Drop", null, (s, e) => DropItem(selectedSlot)); // 4

            foreach (var slot in allSlots)
            {
                slot.MouseUp += (s, e) =>
                {
                    int idx = (int)slot.Tag;
                    var item = allItems[idx];

                    if (e.Button == MouseButtons.Right && item != null)
                    {
                        selectedSlot = idx;

                        // Show "Consume" only for consumables
                        slotMenu.Items[0].Visible = (item.Type == ItemType.Consumable || item.Type == ItemType.common_painting);

                        // Show "Use" only for build items || first is Use
                        slotMenu.Items[1].Visible = (item.Type == ItemType.build);

                        // Show "Inspect" only for build items || first is Use
                        slotMenu.Items[2].Visible = (item.Type == ItemType.common_painting);

                        // Show "Split" only if count > 1
                        slotMenu.Items[3].Visible = (item.Count > 1);

                        // "Drop" is always visible
                        slotMenu.Items[4].Visible = true;

                        slotMenu.Show(System.Windows.Forms.Cursor.Position);
                    }
                };
            }
        }

        private void SplitItem(int slotIndex)
        {
            var item = allItems[slotIndex];
            if (item == null || item.Count <= 1) return;

            int half = item.Count / 2;

            // Reduce original stack
            item.Count -= half;
            allSlots[slotIndex].Invalidate();

            // Find an empty slot for the new stack
            for (int i = 0; i < allItems.Length; i++)
            {
                if (allItems[i] == null)
                {
                    allItems[i] = new InventoryItem
                    {
                        Name = item.Name,
                        Icon = item.Icon,
                        Count = half,
                        Type = item.Type
                    };
                    allSlots[i].Invalidate();
                    break;
                }
            }
        }

        private void Slot_Paint(object sender, PaintEventArgs e)
        {
            PictureBox slot = sender as PictureBox;
            int index = (int)slot.Tag;

            Image icon = allItems[index]?.Icon ?? emptySlotIcon;
            e.Graphics.DrawImage(icon, new Rectangle(0, 0, slot.Width, slot.Height));

            if (allItems[index] != null && allItems[index].Count > 1)
            {
                using (Brush brush = new SolidBrush(Color.White))
                using (Font font = new Font("Arial", 10, FontStyle.Bold))
                {
                    e.Graphics.DrawString(allItems[index].Count.ToString(), font, brush,
                        new PointF(slot.Width - 18, slot.Height - 18));
                }
            }

            // highlight selected slot in hotbar
            if (index == selectedSlot)
            {
                using (Pen pen = new Pen(Color.Yellow, 3))
                using (GraphicsPath path = RoundedRect(new Rectangle(0, 0, slot.Width, slot.Height), 6))
                {
                    e.Graphics.DrawPath(pen, path);
                }
            }
        }



        // --- Mouse down ---
        private void Slot_MouseDown(object sender, MouseEventArgs e)
        {
            PictureBox slot = sender as PictureBox;
            int index = (int)slot.Tag;
            if (allItems[index] == null) return;

            draggedItem = new PictureBox
            {
                Image = allItems[index].Icon,
                Size = slot.Size,
                SizeMode = PictureBoxSizeMode.StretchImage,
                BackColor = Color.Transparent
            };

            dragInfo = new DragInfo
            {
                Index = index,
                Item = allItems[index],
                Offset = e.Location
            };

            Point mousePos = System.Windows.Forms.Cursor.Position;
            draggedItem.Left = mousePos.X - e.Location.X - dragForm.Left;
            draggedItem.Top = mousePos.Y - e.Location.Y - dragForm.Top;

            dragForm.Controls.Add(draggedItem);
            draggedItem.BringToFront();
        }


        // --- Mouse move ---
        private void Slot_MouseMove(object sender, MouseEventArgs e)
        {
            if (draggedItem == null) return;

            // Use whichever drag info is active
            var info = dragInfo ?? furnaceDragInfo;
            if (info == null) return;

            Point mousePos = System.Windows.Forms.Cursor.Position;

            // Always update dragged preview position
            draggedItem.Left = mousePos.X - info.Offset.X - dragForm.Left;
            draggedItem.Top = mousePos.Y - info.Offset.Y - dragForm.Top;
        }


        private void Slot_MouseUp(object sender, MouseEventArgs e)
        {
            Point mousePos = System.Windows.Forms.Cursor.Position;

            // --- Furnace → Inventory ---
            if (furnaceDragInfo != null && furnaceDragInfo.FromFurnace)
            {
                // 1️⃣ Check if over another furnace slot → cancel drag
                for (int i = 0; i < furnaceSlots.Length; i++)
                {
                    Rectangle slotRect = furnaceSlots[i].RectangleToScreen(furnaceSlots[i].ClientRectangle);
                    if (slotRect.Contains(mousePos))
                    {
                        // Snap draggedItem back
                        Point slotPos = furnaceSlots[furnaceDragInfo.Index].PointToScreen(Point.Empty);
                        if (draggedItem != null)
                        {
                            draggedItem.Left = slotPos.X - dragForm.Left;
                            draggedItem.Top = slotPos.Y - dragForm.Top;
                            draggedItem.BringToFront();
                        }

                        draggedItem?.Dispose();
                        draggedItem = null;
                        furnaceDragInfo = null;
                        return; // EARLY EXIT
                    }
                }

                // 2️⃣ Check inventory slots
                Point localMouse = p_inventory.PointToClient(mousePos);
                int invTargetIndex = -1;
                for (int i = 0; i < TOTAL_SLOTS; i++)
                {
                    if (allSlots[i].Bounds.Contains(localMouse))
                    {
                        invTargetIndex = i;
                        break;
                    }
                }

                if (invTargetIndex != -1)
                {
                    InventoryItem furnaceItem = furnaceDragInfo.Item;
                    InventoryItem invTarget = allItems[invTargetIndex];

                    if (invTarget == null)
                        allItems[invTargetIndex] = furnaceItem;
                    else if (invTarget.Name == furnaceItem.Name)
                        invTarget.Count += furnaceItem.Count;
                    else
                        allItems[invTargetIndex] = furnaceItem;

                    furnaceItems[furnaceDragInfo.Index] = null;

                    // Snap draggedItem
                    if (draggedItem != null)
                    {
                        Point slotPos = allSlots[invTargetIndex].PointToScreen(Point.Empty);
                        draggedItem.Left = slotPos.X - dragForm.Left;
                        draggedItem.Top = slotPos.Y - dragForm.Top;
                        draggedItem.BringToFront();
                    }

                    allSlots[invTargetIndex].Invalidate();
                    furnaceSlots[furnaceDragInfo.Index].Invalidate();
                }
                else
                {
                    // Dropped outside → snap back to original furnace slot
                    Point slotPos = furnaceSlots[furnaceDragInfo.Index].PointToScreen(Point.Empty);
                    if (draggedItem != null)
                    {
                        draggedItem.Left = slotPos.X - dragForm.Left;
                        draggedItem.Top = slotPos.Y - dragForm.Top;
                        draggedItem.BringToFront();
                    }
                }

                draggedItem?.Dispose();
                draggedItem = null;
                furnaceDragInfo = null;
                return;
            }

            // --- Inventory → Furnace ---
            if (dragInfo != null && draggedItem != null)
            {
                int furnaceTargetIndex = -1;
                for (int i = 0; i < furnaceSlots.Length; i++)
                {
                    Rectangle slotRect = furnaceSlots[i].RectangleToScreen(furnaceSlots[i].ClientRectangle);
                    if (slotRect.Contains(mousePos))
                    {
                        furnaceTargetIndex = i;
                        break;
                    }
                }

                if (furnaceTargetIndex != -1)
                {
                    InventoryItem invItem = allItems[dragInfo.Index];
                    InventoryItem furnaceTarget = furnaceItems[furnaceTargetIndex];

                    if (furnaceTarget == null)
                    {
                        furnaceItems[furnaceTargetIndex] = invItem;
                        allItems[dragInfo.Index] = null;
                    }
                    else if (furnaceTarget.Name == invItem.Name)
                    {
                        furnaceTarget.Count += invItem.Count;
                        allItems[dragInfo.Index] = null;
                    }
                    else
                    {
                        furnaceItems[furnaceTargetIndex] = invItem;
                        allItems[dragInfo.Index] = furnaceTarget;
                    }

                    allSlots[dragInfo.Index].Invalidate();
                    furnaceSlots[furnaceTargetIndex].Invalidate();
                }
                else // Inventory → Inventory
                {
                    int invTargetIndex2 = -1;
                    for (int i = 0; i < TOTAL_SLOTS; i++)
                    {
                        Rectangle slotRect = allSlots[i].RectangleToScreen(allSlots[i].ClientRectangle);
                        if (slotRect.Contains(mousePos))
                        {
                            invTargetIndex2 = i;
                            break;
                        }
                    }

                    if (invTargetIndex2 != -1 && invTargetIndex2 != dragInfo.Index)
                    {
                        SwapOrStack(dragInfo.Index, invTargetIndex2);

                        // Snap draggedItem
                        Point slotPos = allSlots[invTargetIndex2].PointToScreen(Point.Empty);
                        draggedItem.Left = slotPos.X - dragForm.Left;
                        draggedItem.Top = slotPos.Y - dragForm.Top;
                    }
                }

                draggedItem?.Dispose();
                draggedItem = null;
                dragInfo = null;

                p_inventory.Invalidate();
                p_hotbar.Invalidate();
            }
        }


        // --- Swap or stack helper ---
        private void SwapOrStack(int fromIndex, int toIndex)
        {
            var dragged = allItems[fromIndex];
            var target = allItems[toIndex];

            if (target != null && dragged.Name == target.Name)
            {
                int total = target.Count + dragged.Count;
                if (total <= MAX_STACK)
                {
                    target.Count = total;
                    allItems[fromIndex] = null;
                }
                else
                {
                    target.Count = MAX_STACK;
                    dragged.Count = total - MAX_STACK;
                    allItems[fromIndex] = dragged;
                }
            }
            else
            {
                allItems[fromIndex] = target;
                allItems[toIndex] = dragged;
            }
        }

        public void SelectSlot(int index)
        {
            if (index < 0 || index >= COLS) return;
            selectedSlot = ROWS * COLS - COLS + index; // bottom row hotbar
            for (int i = 0; i < COLS; i++)
                allSlots[ROWS * COLS - COLS + i].Invalidate();
        }

        private void InspectItem(int slotIndex)
        {
            if (slotIndex < 0 || slotIndex >= allItems.Length) return;

            var item = allItems[slotIndex];
            if (item == null) return;

            switch (item.Type)
            {
                case ItemType.build:
                    item.Count--;
                    if (item.Count <= 0)
                        ViewItemInGame(item);
                    break;
            }
        }

        private void ViewItem(int slotIndex)
        {
            if (slotIndex < 0 || slotIndex >= allItems.Length) return;

            var item = allItems[slotIndex];
            if (item == null) return;

            switch (item.Type)
            {
                case ItemType.build:
                    item.Count--;
                    if (item.Count <= 0)
                        ViewItemInGame(item);
                    break;
            }
        }

        public void ViewItemInGame(InventoryItem item)
        {
            if (item == null) return;

            switch (item.Name.ToLower()) // comparison is lowercase
            {
                case "workbench": // also lowercase here
                    UnlockWorkbench();
                    break;

                case "furnace":
                    ShowFurnaceX();
                    break;

                default:
                    // Other build items could be handled here
                    break;
            }
        }

        private void UnlockWorkbench()
        {
            if (workbenchUnlocked) return; // already unlocked

            workbenchUnlocked = true;

            // Optionally: open crafting menu right after unlocking
            inventoryVisible = true;
            p_inventory.Visible = true;
            CraftPanel.Visible = true;

            if (rnd != null)
                rnd.InventoryOpen = inventoryVisible;

            ResetCraftInfo();
        }

        private void UseItem(int slotIndex)
        {
            if (slotIndex < 0 || slotIndex >= allItems.Length) return;

            var item = allItems[slotIndex];
            if (item == null) return;

            switch (item.Type)
            {
                case ItemType.Consumable:
                    item.Count--;
                    if (item.Count <= 0)
                        allItems[slotIndex] = null;
                    allSlots[slotIndex].Invalidate();

                    DrinkItem(item);
                    break;

                case ItemType.Weapon:
                    // equip logic
                    break;

                case ItemType.Tool:
                    // use tool logic
                    break;

                case ItemType.Resource:
                    // cannot use
                    break;

                case ItemType.common_painting:
                    // Remove the painting first so AddItem can work properly
                    allItems[slotIndex] = null;
                    allSlots[slotIndex].Invalidate();

                    // Give new items
                    GiveCommonPainting();

                    // Refresh inventory panel so UI updates
                    p_inventory?.Invalidate();
                    break;
            }
        }



        private Random rng = new Random();
        public void GiveCommonPainting()
        {
            var possibleItems = new (string Name, Image Icon, ItemType Type)[]
            {
        ("Wood", Properties.Resources.Wood, ItemType.Resource),
        ("Stone", Properties.Resources.StoneIcon, ItemType.Resource),
        ("Metal_Shard", Properties.Resources.Metal_Ore_Icon, ItemType.Resource),
        ("Water_Jar", Properties.Resources.Water, ItemType.Consumable),
        ("Metal_Ingot", Properties.Resources.Metal_Ore_Cooked_Icon, ItemType.Resource),
        ("Cooked_Meat", Properties.Resources.Cooked_Meta_Icon, ItemType.Consumable)
            };

            int itemsToGive = rng.Next(2, 4); // 2 or 3 items
            var shuffled = possibleItems.OrderBy(x => rng.Next()).Take(itemsToGive);

            foreach (var item in shuffled)
            {
                int amount = rng.Next(1, 7); // 1–6 each

                // Each drop gets a new unique UID so it never merges with existing items
                var newItem = new InventoryItem
                {
                    Name = item.Name,
                    Icon = item.Icon,
                    Count = amount,
                    Type = item.Type,
                    UID = Guid.NewGuid()
                };
                SpawnLootLabel(newItem.Name, newItem.Icon, newItem.Count);

                AddItem(newItem);
            }
        }


        public void DrinkItem(InventoryItem item)
        {
            if (item == null) return;

            switch (item.Name.ToLower()) // make comparison lowercase
            {
                case "water_jar": // lowercase!
                    DrinkWater(5);
                    AddItem(new InventoryItem
                    {
                        Icon = Properties.Resources.Empty_Water_Jar_Icon,
                        Name = "Empty_Jar",
                        Count = 1,
                        Type = ItemType.Resource
                    });
                    break;

                case "raspberry":
                    EatFood(5);
                    break;

                case "cooked_meat":
                    EatFood(10);
                    break;

                case "sleeping_tarot_card":
                    TurnDay();
                    break;

                case "night_tarot_card":
                    TurnNight();
                    break;

                default:
                    // Other items do nothing
                    break;
            }
        }

        public void TurnDay()
        {
            rnd.SetDay();
        }

        public void TurnNight()
        {
            rnd.SetNight();
        }
        public void DropItem(int slotIndex)
        {
            if (slotIndex < 0 || slotIndex >= TOTAL_SLOTS || allItems[slotIndex] == null) return;
            allItems[slotIndex] = null;
            allSlots[slotIndex].Invalidate();
        }

        public bool AddItem(InventoryItem newItem)
        {
            for (int i = 0; i < allItems.Length; i++)
            {
                var slot = allItems[i];

                // Empty slot: place directly
                if (slot == null)
                {
                    allItems[i] = newItem;
                    allSlots[i].Invalidate();
                    return true;
                }

                // Stackable items (Resources and Consumables only)
                else if (slot.Name == newItem.Name
                         && slot.Type != ItemType.Weapon
                         && slot.Type != ItemType.build
                         && slot.Type == newItem.Type)
                {
                    int remaining = MAX_STACK - slot.Count;
                    if (newItem.Count <= remaining)
                    {
                        slot.Count += newItem.Count;
                        allSlots[i].Invalidate();
                        return true;
                    }
                    else
                    {
                        slot.Count = MAX_STACK;
                        newItem.Count -= remaining;
                    }
                }
            }

            // Could not add all items
            return false;
        }

        private const int HOTBAR_ROW = 4; // last row = h
        private bool AddItemToHotbar(InventoryItem item, int hotbarSlot)
        {
            const int COLS = 8; // number of slots per row
            if (hotbarSlot < 0 || hotbarSlot >= COLS) return false;

            int index = HOTBAR_ROW * COLS + hotbarSlot;

            // For weapons, no stacking
            if (item.Type == ItemType.Weapon)
            {
                allItems[index] = item;
                allSlots[index].Invalidate();
                return true;
            }

            // If stackable (not weapons), try to stack first
            if (allItems[index] != null && allItems[index].Name == item.Name && allItems[index].Type != ItemType.Weapon || allItems[index] != null && allItems[index].Name == item.Name && allItems[index].Type != ItemType.build)
            {
                int total = allItems[index].Count + item.Count;
                if (total <= MAX_STACK)
                {
                    allItems[index].Count = total;
                    allSlots[index].Invalidate();
                    return true;
                }
                else
                {
                    allItems[index].Count = MAX_STACK;
                    item.Count = total - MAX_STACK;
                }
            }

            // Place remaining item in the hotbar slot
            if (allItems[index] == null)
            {
                allItems[index] = item;
                allSlots[index].Invalidate();
                return true;
            }

            return false; // could not add
        }
        private GraphicsPath RoundedRect(Rectangle bounds, int radius)
        {
            int d = radius * 2;
            GraphicsPath path = new GraphicsPath();
            path.AddArc(bounds.X, bounds.Y, d, d, 180, 90);
            path.AddArc(bounds.Right - d, bounds.Y, d, d, 270, 90);
            path.AddArc(bounds.Right - d, bounds.Bottom - d, d, d, 0, 90);
            path.AddArc(bounds.X, bounds.Bottom - d, d, d, 90, 90);
            path.CloseFigure();
            return path;
        }
        #endregion

        private void Game_FormClosing(object sender, FormClosingEventArgs e)
        {
            handOverlay.Close();
            dragForm.Close();
        }
    }

    public class LootSlot
    {
        public PictureBox IconBox;
        public Label CountLabel;
        public InventoryItem Item;
    }

    public enum ItemType
    {
        build,
        Consumable,
        Weapon,
        Tool,
        Resource,
        common_painting,

    }

    public class InventoryItem
    {
        public Image Icon { get; set; }
        public string Name { get; set; }
        public int Count { get; set; }
        public ItemType Type { get; set; }
        public int Damage { get; set; }
        public float speed { get; set; }

        public Guid UID { get; set; } = Guid.NewGuid(); // unique per pickup
    }

}
Editor is loading...
Leave a Comment