diff --git a/TrueCraft.API/Logic/IBurnableItem.cs b/TrueCraft.API/Logic/IBurnableItem.cs
new file mode 100644
index 0000000..1c0d681
--- /dev/null
+++ b/TrueCraft.API/Logic/IBurnableItem.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace TrueCraft.API.Logic
+{
+ ///
+ /// Describes an item that can be burnt as fuel in a furnace.
+ ///
+ public interface IBurnableItem
+ {
+ ///
+ /// The duration of time this item can act as fuel.
+ ///
+ TimeSpan BurnTime { get; }
+ }
+}
\ No newline at end of file
diff --git a/TrueCraft.API/Logic/ISmeltableItem.cs b/TrueCraft.API/Logic/ISmeltableItem.cs
new file mode 100644
index 0000000..1c02b5b
--- /dev/null
+++ b/TrueCraft.API/Logic/ISmeltableItem.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace TrueCraft.API.Logic
+{
+ ///
+ /// Describes an item that can be smelted in a furnace to produce a new item.
+ ///
+ public interface ISmeltableItem
+ {
+ ///
+ /// The item this becomes when smelted.
+ ///
+ ItemStack SmeltingOutput { get; }
+ }
+}
\ No newline at end of file
diff --git a/TrueCraft.API/TrueCraft.API.csproj b/TrueCraft.API/TrueCraft.API.csproj
index df50c42..34fa688 100644
--- a/TrueCraft.API/TrueCraft.API.csproj
+++ b/TrueCraft.API/TrueCraft.API.csproj
@@ -122,6 +122,8 @@
+
+
diff --git a/TrueCraft.API/Windows/IWindow.cs b/TrueCraft.API/Windows/IWindow.cs
index 58912dc..abf56f9 100644
--- a/TrueCraft.API/Windows/IWindow.cs
+++ b/TrueCraft.API/Windows/IWindow.cs
@@ -1,4 +1,5 @@
using System;
+using TrueCraft.API.Networking;
namespace TrueCraft.API.Windows
{
@@ -6,6 +7,7 @@ namespace TrueCraft.API.Windows
{
event EventHandler WindowChange;
+ IRemoteClient Client { get; set; }
IWindowArea[] WindowAreas { get; }
sbyte ID { get; set; }
string Name { get; }
diff --git a/TrueCraft.Core/Logic/Blocks/FurnaceBlock.cs b/TrueCraft.Core/Logic/Blocks/FurnaceBlock.cs
index 86da822..6fccbb0 100644
--- a/TrueCraft.Core/Logic/Blocks/FurnaceBlock.cs
+++ b/TrueCraft.Core/Logic/Blocks/FurnaceBlock.cs
@@ -3,6 +3,13 @@ using TrueCraft.API.Logic;
using TrueCraft.API;
using TrueCraft.API.Networking;
using TrueCraft.API.World;
+using TrueCraft.Core.Windows;
+using TrueCraft.API.Windows;
+using System.Collections.Generic;
+using fNbt;
+using TrueCraft.API.Server;
+using TrueCraft.Core.Networking.Packets;
+using TrueCraft.Core.Entities;
namespace TrueCraft.Core.Logic.Blocks
{
@@ -20,6 +27,123 @@ namespace TrueCraft.Core.Logic.Blocks
public override string DisplayName { get { return "Furnace"; } }
+ protected override ItemStack[] GetDrop(BlockDescriptor descriptor, ItemStack item)
+ {
+ return new ItemStack[] { new ItemStack(BlockID) };
+ }
+
+ public override void BlockMined(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user)
+ {
+ var entity = world.GetTileEntity(descriptor.Coordinates);
+ if (entity != null)
+ {
+ foreach (var item in (NbtList)entity["Items"])
+ {
+ var manager = user.Server.GetEntityManagerForWorld(world);
+ var slot = ItemStack.FromNbt((NbtCompound)item);
+ manager.SpawnEntity(new ItemEntity(descriptor.Coordinates + new Vector3(0.5), slot));
+ }
+ world.SetTileEntity(descriptor.Coordinates, null);
+ }
+ base.BlockMined(descriptor, face, world, user);
+ }
+
+ public override bool BlockRightClicked(BlockDescriptor descriptor, BlockFace face, IWorld world, IRemoteClient user)
+ {
+ var window = new FurnaceWindow(user.Server.Scheduler, descriptor.Coordinates,
+ user.Server.ItemRepository, (InventoryWindow)user.Inventory);
+
+ var entity = world.GetTileEntity(descriptor.Coordinates);
+ if (entity != null)
+ {
+ int i = 0;
+ foreach (var item in (NbtList)entity["Items"])
+ {
+ var slot = ItemStack.FromNbt((NbtCompound)item);
+ window[i++] = slot;
+ }
+ }
+
+ user.OpenWindow(window);
+
+ if (entity != null)
+ {
+ var burnTime = entity["BurnTime"].ShortValue;
+ var burnTotal = entity["BurnTotal"].ShortValue;
+ var cookTime = entity["CookTime"].ShortValue;
+ var burnProgress = (short)(((double)burnTime / burnTotal) * 250);
+ if (burnTime == 0)
+ burnProgress = 0;
+ if (cookTime != 0)
+ window.Client.QueuePacket(new UpdateProgressPacket(window.ID,
+ UpdateProgressPacket.ProgressTarget.ItemCompletion, cookTime));
+ if (burnProgress != 0)
+ window.Client.QueuePacket(new UpdateProgressPacket(window.ID,
+ UpdateProgressPacket.ProgressTarget.AvailableHeat, burnProgress));
+ }
+
+ window.WindowChange += (sender, e) => FurnaceWindowChanged(sender, e, world);
+ return false;
+ }
+
+ private bool Handling = false;
+
+ private NbtCompound CreateTileEntity()
+ {
+ return new NbtCompound(new NbtTag[]
+ {
+ new NbtShort("BurnTime", 0),
+ new NbtShort("BurnTotal", 0),
+ new NbtShort("CookTime", 20),
+ new NbtList("Items", new[]
+ {
+ ItemStack.EmptyStack.ToNbt(),
+ ItemStack.EmptyStack.ToNbt(),
+ ItemStack.EmptyStack.ToNbt()
+ }, NbtTagType.Compound)
+ });
+ }
+
+ protected void FurnaceWindowChanged(object sender, WindowChangeEventArgs e, IWorld world)
+ {
+ if (Handling)
+ return;
+ var window = sender as FurnaceWindow;
+ var index = e.SlotIndex;
+ if (index >= FurnaceWindow.MainIndex)
+ return;
+
+ Handling = true;
+ window[index] = e.Value;
+
+ var entity = world.GetTileEntity(window.Coordinates);
+ if (entity == null)
+ entity = CreateTileEntity();
+
+ entity["Items"] = new NbtList("Items", new NbtTag[]
+ {
+ window[0].ToNbt(), window[1].ToNbt(), window[2].ToNbt()
+ }, NbtTagType.Compound);
+
+ world.SetTileEntity(window.Coordinates, entity);
+
+ UpdateFurnaceState(window.EventScheduler, world, entity, window.ItemRepository, window.Coordinates, window, TimeSpan.Zero);
+
+ Handling = false;
+ }
+
+ private void UpdateFurnaceState(IEventScheduler scheduler, IWorld world, NbtCompound tileEntity,
+ IItemRepository itemRepository, Coordinates3D coords, FurnaceWindow window, TimeSpan elapsed)
+ {
+ if (world.GetBlockID(coords) != FurnaceBlock.BlockID && world.GetBlockID(coords) != LitFurnaceBlock.BlockID)
+ {
+ if (window != null && !window.IsDisposed)
+ window.Dispose();
+ return;
+ }
+ // TODO
+ }
+
public override Tuple GetTextureMap(byte metadata)
{
return new Tuple(13, 2);
diff --git a/TrueCraft.Core/Logic/Blocks/IronOreBlock.cs b/TrueCraft.Core/Logic/Blocks/IronOreBlock.cs
index 7fb28df..6f3ff44 100644
--- a/TrueCraft.Core/Logic/Blocks/IronOreBlock.cs
+++ b/TrueCraft.Core/Logic/Blocks/IronOreBlock.cs
@@ -1,10 +1,11 @@
using System;
using TrueCraft.API.Logic;
using TrueCraft.API;
+using TrueCraft.Core.Logic.Items;
namespace TrueCraft.Core.Logic.Blocks
{
- public class IronOreBlock : BlockProvider
+ public class IronOreBlock : BlockProvider, ISmeltableItem
{
public static readonly byte BlockID = 0x0F;
@@ -38,5 +39,7 @@ namespace TrueCraft.Core.Logic.Blocks
return ToolType.Pickaxe;
}
}
+
+ public ItemStack SmeltingOutput { get { return new ItemStack(IronIngotItem.ItemID); } }
}
}
\ No newline at end of file
diff --git a/TrueCraft.Core/Logic/Items/CoalItem.cs b/TrueCraft.Core/Logic/Items/CoalItem.cs
index fa30aba..dabe0c8 100644
--- a/TrueCraft.Core/Logic/Items/CoalItem.cs
+++ b/TrueCraft.Core/Logic/Items/CoalItem.cs
@@ -3,7 +3,7 @@ using TrueCraft.API.Logic;
namespace TrueCraft.Core.Logic.Items
{
- public class CoalItem : ItemProvider
+ public class CoalItem : ItemProvider, IBurnableItem
{
public static readonly short ItemID = 0x107;
@@ -15,5 +15,7 @@ namespace TrueCraft.Core.Logic.Items
}
public override string DisplayName { get { return "Coal"; } }
+
+ public TimeSpan BurnTime { get { return TimeSpan.FromSeconds(80); } }
}
}
\ No newline at end of file
diff --git a/TrueCraft.Core/Networking/Packets/UpdateProgressPacket.cs b/TrueCraft.Core/Networking/Packets/UpdateProgressPacket.cs
index e63c5ef..317bcc7 100644
--- a/TrueCraft.Core/Networking/Packets/UpdateProgressPacket.cs
+++ b/TrueCraft.Core/Networking/Packets/UpdateProgressPacket.cs
@@ -16,6 +16,13 @@ namespace TrueCraft.Core.Networking.Packets
public byte ID { get { return 0x69; } }
+ public UpdateProgressPacket(sbyte windowID, ProgressTarget target, short value)
+ {
+ WindowID = windowID;
+ Target = target;
+ Value = value;
+ }
+
public sbyte WindowID;
public ProgressTarget Target;
///
diff --git a/TrueCraft.Core/TrueCraft.Core.csproj b/TrueCraft.Core/TrueCraft.Core.csproj
index e60e235..71d0ded 100644
--- a/TrueCraft.Core/TrueCraft.Core.csproj
+++ b/TrueCraft.Core/TrueCraft.Core.csproj
@@ -352,6 +352,7 @@
+
diff --git a/TrueCraft.Core/Windows/FurnaceWindow.cs b/TrueCraft.Core/Windows/FurnaceWindow.cs
new file mode 100644
index 0000000..537442f
--- /dev/null
+++ b/TrueCraft.Core/Windows/FurnaceWindow.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using TrueCraft.API.Windows;
+using TrueCraft.API.Logic;
+using TrueCraft.API;
+using TrueCraft.API.Server;
+
+namespace TrueCraft.Core.Windows
+{
+ public class FurnaceWindow : Window
+ {
+ public IItemRepository ItemRepository { get; set; }
+ public IEventScheduler EventScheduler { get; set; }
+ public Coordinates3D Coordinates { get; set; }
+
+ public FurnaceWindow(IEventScheduler scheduler, Coordinates3D coordinates,
+ IItemRepository itemRepository, InventoryWindow inventory)
+ {
+ ItemRepository = itemRepository;
+ EventScheduler = scheduler;
+ Coordinates = coordinates;
+
+ WindowAreas = new[]
+ {
+ new WindowArea(IngredientIndex, 1, 1, 1),
+ new WindowArea(FuelIndex, 1, 1, 1),
+ new WindowArea(OutputIndex, 1, 1, 1),
+ new WindowArea(MainIndex, 27, 9, 3),
+ new WindowArea(HotbarIndex, 9, 9, 1)
+ };
+ inventory.MainInventory.CopyTo(MainInventory);
+ inventory.Hotbar.CopyTo(Hotbar);
+ foreach (var area in WindowAreas)
+ area.WindowChange += (s, e) => OnWindowChange(new WindowChangeEventArgs(
+ (s as WindowArea).StartIndex + e.SlotIndex, e.Value));
+ Copying = false;
+ inventory.WindowChange += (sender, e) =>
+ {
+ if (Copying) return;
+ if ((e.SlotIndex >= InventoryWindow.MainIndex && e.SlotIndex < InventoryWindow.MainIndex + inventory.MainInventory.Length)
+ || (e.SlotIndex >= InventoryWindow.HotbarIndex && e.SlotIndex < InventoryWindow.HotbarIndex + inventory.Hotbar.Length))
+ {
+ inventory.MainInventory.CopyTo(MainInventory);
+ inventory.Hotbar.CopyTo(Hotbar);
+ }
+ };
+ }
+
+ private bool Copying { get; set; }
+
+ public const int IngredientIndex = 0;
+ public const int FuelIndex = 1;
+ public const int OutputIndex = 2;
+ public const int MainIndex = 3;
+ public const int HotbarIndex = 30;
+
+ public override string Name
+ {
+ get
+ {
+ return "Furnace";
+ }
+ }
+
+ public override sbyte Type
+ {
+ get
+ {
+ return 2;
+ }
+ }
+
+ public override IWindowArea[] WindowAreas { get; protected set; }
+
+ public IWindowArea Ingredient
+ {
+ get { return WindowAreas[0]; }
+ }
+
+ public IWindowArea Fuel
+ {
+ get { return WindowAreas[1]; }
+ }
+
+ public IWindowArea Output
+ {
+ get { return WindowAreas[2]; }
+ }
+
+ public IWindowArea MainInventory
+ {
+ get { return WindowAreas[3]; }
+ }
+
+ public IWindowArea Hotbar
+ {
+ get { return WindowAreas[4]; }
+ }
+
+ public override ItemStack[] GetSlots()
+ {
+ var relevantAreas = new[] { Ingredient, Fuel, Output };
+ int length = relevantAreas.Sum(area => area.Length);
+ var slots = new ItemStack[length];
+ foreach (var windowArea in relevantAreas)
+ Array.Copy(windowArea.Items, 0, slots, windowArea.StartIndex, windowArea.Length);
+ return slots;
+ }
+
+ public override void CopyToInventory(IWindow inventoryWindow)
+ {
+ var window = (InventoryWindow)inventoryWindow;
+ Copying = true;
+ MainInventory.CopyTo(window.MainInventory);
+ Hotbar.CopyTo(window.Hotbar);
+ Copying = false;
+ }
+
+ protected override IWindowArea GetLinkedArea(int index, ItemStack slot)
+ {
+ if (index < MainIndex)
+ return MainInventory;
+ return Hotbar;
+ }
+
+ public override bool PickUpStack(ItemStack slot)
+ {
+ var area = MainInventory;
+ foreach (var item in Hotbar.Items)
+ {
+ if (item.Empty || (slot.ID == item.ID && slot.Metadata == item.Metadata))
+ //&& item.Count + slot.Count < Item.GetMaximumStackSize(new ItemDescriptor(item.Id, item.Metadata)))) // TODO
+ {
+ area = Hotbar;
+ break;
+ }
+ }
+ int index = area.MoveOrMergeItem(-1, slot, null);
+ return index != -1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TrueCraft.Core/Windows/Window.cs b/TrueCraft.Core/Windows/Window.cs
index 706d07f..7192fa1 100644
--- a/TrueCraft.Core/Windows/Window.cs
+++ b/TrueCraft.Core/Windows/Window.cs
@@ -4,15 +4,20 @@ using System.Linq;
using System.Text;
using TrueCraft.API.Windows;
using TrueCraft.API;
+using TrueCraft.API.Networking;
namespace TrueCraft.Core.Windows
{
- public abstract class Window : IWindow, IDisposable
+ public abstract class Window : IWindow, IDisposable, IEventSubject
{
public abstract IWindowArea[] WindowAreas { get; protected set; }
public event EventHandler WindowChange;
+ public bool IsDisposed { get; private set; }
+
+ public IRemoteClient Client { get; set; }
+
public virtual void MoveToAlternateArea(int index)
{
int fromIndex = GetAreaIndex(index);
@@ -142,6 +147,8 @@ namespace TrueCraft.Core.Windows
WindowChange(this, e);
}
+ public event EventHandler Disposed;
+
public virtual void Dispose()
{
for (int i = 0; i < WindowAreas.Length; i++)
@@ -149,6 +156,10 @@ namespace TrueCraft.Core.Windows
WindowAreas[i].Dispose();
}
WindowChange = null;
+ if (Disposed != null)
+ Disposed(this, null);
+ Client = null;
+ IsDisposed = true;
}
}
}
diff --git a/TrueCraft/RemoteClient.cs b/TrueCraft/RemoteClient.cs
index 1b7d0a9..dfc0198 100644
--- a/TrueCraft/RemoteClient.cs
+++ b/TrueCraft/RemoteClient.cs
@@ -208,6 +208,7 @@ namespace TrueCraft
public void OpenWindow(IWindow window)
{
CurrentWindow = window;
+ window.Client = this;
window.ID = NextWindowID++;
if (NextWindowID < 0) NextWindowID = 1;
QueuePacket(new OpenWindowPacket(window.ID, window.Type, window.Name, (sbyte)window.MinecraftWasWrittenByFuckingIdiotsLength));