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));