Untitled
unknown
plain_text
10 months ago
117 kB
16
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