فهرست منبع

slot drag impl

JasonWang 6 سال پیش
والد
کامیت
5a3eafce8f

+ 5 - 5
src/MineCase.Core/Block.cs

@@ -310,7 +310,7 @@ namespace MineCase
     /// If FallingFlag is set, the lower bits are essentially ignored,
     /// since this block is then at its highest fluid level.
     /// Level1 is the highest fluid level(not necessarily filling the block -
-    /// this depends on the neighboring fluid blocks above each upper corner of the block)
+    /// this depends on the neighboring fluid blocks above each upper corner of the block).
     /// </summary>
     [Flags]
     public enum FluidType : uint
@@ -741,7 +741,7 @@ namespace MineCase
     }
 
     /// <summary>
-    /// For Activator Rails, Detector Rails, and Powered Rails
+    /// For Activator Rails, Detector Rails, and Powered Rails.
     /// </summary>
     public enum RailExType : uint
     {
@@ -754,7 +754,7 @@ namespace MineCase
     }
 
     /// <summary>
-    /// For Ladders, Furnaces, Chests, Trapped Chests
+    /// For Ladders, Furnaces, Chests, Trapped Chests.
     /// </summary>
     public enum FacingDirectionType : uint
     {
@@ -793,7 +793,7 @@ namespace MineCase
     }
 
     /// <summary>
-    /// 0x1 ~ 0x4 bits specifying which direction the dispenser is facing
+    /// 0x1 ~ 0x4 bits specifying which direction the dispenser is facing.
     /// </summary>
     [Flags]
     public enum DispenserType : uint
@@ -810,7 +810,7 @@ namespace MineCase
     }
 
     /// <summary>
-    /// 0x1 ~ 0x4 bits specifying which direction the dropper is facing
+    /// 0x1 ~ 0x4 bits specifying which direction the dropper is facing.
     /// </summary>
     [Flags]
     public enum DropperType : uint

+ 11 - 0
src/MineCase.Core/ClickAction.cs

@@ -6,10 +6,21 @@ namespace MineCase
 {
     public enum ClickAction
     {
+        InvalidClick,
         LeftMouseClick,
         RightMouseClick,
         ShiftLeftMouseClick,
         ShiftRightMouseClick,
+        LeftMouseDragBegin,
+        LeftMouseAddSlot,
+        LeftMouseDragEnd,
+        RightMouseDragBegin,
+        RightMouseAddSlot,
+        RightMouseDragEnd,
+        MiddleMouseDragBegin,
+        MiddleMouseAddSlot,
+        MiddleMouseDragEnd,
+        DoubleClick,
         NumberKey1,
         NumberKey2,
         NumberKey3,

+ 19 - 1
src/MineCase.Server.Grains/Game/Entities/Components/DraggedSlotComponent.cs

@@ -6,13 +6,20 @@ using MineCase.Engine;
 
 namespace MineCase.Server.Game.Entities.Components
 {
-    internal class DraggedSlotComponent : Component, IHandle<SetDraggedSlot>, IHandle<AskDraggedSlot, Slot>
+    internal class DraggedSlotComponent : Component,
+        IHandle<SetDraggedSlot>, IHandle<AskDraggedSlot, Slot>,
+        IHandle<SetDraggedPath>, IHandle<AskDraggedPath, List<int>>
     {
         public static readonly DependencyProperty<Slot> DraggedSlotProperty =
             DependencyProperty.Register("DraggedSlot", typeof(DraggedSlotComponent), new PropertyMetadata<Slot>(Slot.Empty));
 
+        public static readonly DependencyProperty<List<int>> DraggedPathProperty =
+            DependencyProperty.Register("DraggedPath", typeof(DraggedSlotComponent), new PropertyMetadata<List<int>>(new List<int>()));
+
         public Slot DraggedSlot => AttachedObject.GetValue(DraggedSlotProperty);
 
+        public List<int> DraggedPath => AttachedObject.GetValue(DraggedPathProperty);
+
         public DraggedSlotComponent(string name = "draggedSlot")
             : base(name)
         {
@@ -31,5 +38,16 @@ namespace MineCase.Server.Game.Entities.Components
         {
             return Task.FromResult(DraggedSlot);
         }
+
+        Task IHandle<SetDraggedPath>.Handle(SetDraggedPath message)
+        {
+            AttachedObject.SetLocalValue(DraggedPathProperty, message.Path);
+            return Task.CompletedTask;
+        }
+
+        Task<List<int>> IHandle<AskDraggedPath, List<int>>.Handle(AskDraggedPath message)
+        {
+            return Task.FromResult(DraggedPath);
+        }
     }
 }

+ 2 - 2
src/MineCase.Server.Grains/Game/Windows/SlotAreas/CraftingSlotArea.cs

@@ -21,7 +21,7 @@ namespace MineCase.Server.Game.Windows.SlotAreas
             _gridSize = gridSize;
         }
 
-        public override async Task Click(IPlayer player, int slotIndex, ClickAction clickAction, Slot clickedItem)
+        public override async Task Click(IPlayer player, List<SlotArea> slotAreas, int globalIndex, int slotIndex, ClickAction clickAction, Slot clickedItem)
         {
             if (slotIndex == 0)
             {
@@ -57,7 +57,7 @@ namespace MineCase.Server.Game.Windows.SlotAreas
             }
             else
             {
-                await base.Click(player, slotIndex, clickAction, clickedItem);
+                await base.Click(player, slotAreas, globalIndex, slotIndex, clickAction, clickedItem);
             }
 
             await UpdateRecipe(player);

+ 192 - 1
src/MineCase.Server.Grains/Game/Windows/SlotAreas/SlotArea.cs

@@ -85,7 +85,7 @@ namespace MineCase.Server.Game.Windows.SlotAreas
             return Window.BroadcastSlotChanged(this, slotIndex, item);
         }
 
-        public virtual async Task Click(IPlayer player, int slotIndex, ClickAction clickAction, Slot clickedItem)
+        public virtual async Task Click(IPlayer player, List<SlotArea> slotAreas, int globalIndex, int slotIndex, ClickAction clickAction, Slot clickedItem)
         {
             switch (clickAction)
             {
@@ -114,6 +114,29 @@ namespace MineCase.Server.Game.Windows.SlotAreas
                 case ClickAction.NumberKey9:
                     await OnNumberKeyClick(player, slotIndex, clickAction - ClickAction.NumberKey1);
                     break;
+                case ClickAction.LeftMouseDragBegin:
+                    await OnLeftMouseDragBegin(player, slotAreas, globalIndex, slotIndex);
+                    break;
+                case ClickAction.LeftMouseAddSlot:
+                    await OnLeftMouseAddSlot(player, slotAreas, globalIndex, slotIndex);
+                    break;
+                case ClickAction.LeftMouseDragEnd:
+                    await OnLeftMouseDragEnd(player, slotAreas, globalIndex, slotIndex);
+                    break;
+
+                case ClickAction.RightMouseDragBegin:
+                    await OnRightMouseDragBegin(player, slotAreas, globalIndex, slotIndex);
+                    break;
+                case ClickAction.RightMouseAddSlot:
+                    await OnRightMouseAddSlot(player, slotAreas, globalIndex, slotIndex);
+                    break;
+                case ClickAction.RightMouseDragEnd:
+                    await OnRightMouseDragEnd(player, slotAreas, globalIndex, slotIndex);
+                    break;
+
+                case ClickAction.DoubleClick:
+                    await OnDoubleClick(player, slotAreas, globalIndex, slotIndex);
+                    break;
                 default:
                     await Window.BroadcastWholeWindow();
                     break;
@@ -226,6 +249,161 @@ namespace MineCase.Server.Game.Windows.SlotAreas
             }
         }
 
+        protected virtual async Task OnLeftMouseDragBegin(IPlayer player, List<SlotArea> slotAreas, int globalIndex, int slotIndex)
+        {
+            // begin left mouse drag, clear player drag path data
+            await player.Tell(new SetDraggedPath { Path = new List<int>() });
+        }
+
+        protected virtual async Task OnLeftMouseAddSlot(IPlayer player, List<SlotArea> slotAreas, int globalIndex, int slotIndex)
+        {
+            var slot = await GetSlot(player, slotIndex);
+            var draggedSlot = await player.Ask(AskDraggedSlot.Default);
+            var dragSlotPath = await player.Ask(AskDraggedPath.Default);
+
+            if (slot.IsEmpty || slot.BlockId == draggedSlot.BlockId)
+            {
+                dragSlotPath.Add(globalIndex);
+            }
+
+            await player.Tell(new SetDraggedPath { Path = dragSlotPath });
+        }
+
+        protected virtual async Task OnLeftMouseDragEnd(IPlayer player, List<SlotArea> slotAreas, int globalIndex, int slotIndex)
+        {
+            var draggedSlot = await player.Ask(AskDraggedSlot.Default);
+            var dragSlotPath = await player.Ask(AskDraggedPath.Default);
+            var slot = draggedSlot;
+            slot.ItemCount = 0;
+
+            int leftNum = draggedSlot.ItemCount;
+            int averageNum = draggedSlot.ItemCount / dragSlotPath.Count;
+            foreach (var eachSlotIndex in dragSlotPath)
+            {
+                var localSlot = GlobalSlotIndexToLocal(slotAreas, eachSlotIndex);
+                var eachSlot = await localSlot.slotArea.GetSlot(player, localSlot.slotIndex);
+
+                slot.ItemCount = (byte)Math.Min(MaxStackCount, eachSlot.ItemCount + averageNum);
+
+                await localSlot.slotArea.SetSlot(player, localSlot.slotIndex, slot);
+
+                if (eachSlot.IsEmpty)
+                {
+                    leftNum -= Math.Min(MaxStackCount - 0, averageNum);
+                }
+                else
+                {
+                    leftNum -= Math.Min(MaxStackCount - eachSlot.ItemCount, averageNum);
+                }
+            }
+
+            // now set left item for that slot is full
+            slot.ItemCount = (byte)leftNum;
+            slot.MakeEmptyIfZero();
+            await player.Tell(new SetDraggedSlot { Slot = slot });
+
+            // last, clear dragged path
+            await player.Tell(new SetDraggedPath { Path = new List<int>() });
+        }
+
+        protected virtual async Task OnRightMouseDragBegin(IPlayer player, List<SlotArea> slotAreas, int globalIndex, int slotIndex)
+        {
+            await player.Tell(new SetDraggedPath { Path = new List<int>() });
+        }
+
+        protected virtual async Task OnRightMouseAddSlot(IPlayer player, List<SlotArea> slotAreas, int globalIndex, int slotIndex)
+        {
+            var slot = await GetSlot(player, slotIndex);
+            var draggedSlot = await player.Ask(AskDraggedSlot.Default);
+            var dragSlotPath = await player.Ask(AskDraggedPath.Default);
+
+            if (slot.IsEmpty || slot.BlockId == draggedSlot.BlockId)
+            {
+                dragSlotPath.Add(globalIndex);
+            }
+
+            await player.Tell(new SetDraggedPath { Path = dragSlotPath });
+        }
+
+        protected virtual async Task OnRightMouseDragEnd(IPlayer player, List<SlotArea> slotAreas, int globalIndex, int slotIndex)
+        {
+            var draggedSlot = await player.Ask(AskDraggedSlot.Default);
+            var dragSlotPath = await player.Ask(AskDraggedPath.Default);
+            var slot = draggedSlot;
+            slot.ItemCount = 0;
+
+            int leftNum = draggedSlot.ItemCount;
+            int averageNum = 1;
+            foreach (var eachSlotIndex in dragSlotPath)
+            {
+                var localSlot = GlobalSlotIndexToLocal(slotAreas, eachSlotIndex);
+                var eachSlot = await localSlot.slotArea.GetSlot(player, localSlot.slotIndex);
+
+                slot.ItemCount = (byte)Math.Min(MaxStackCount, eachSlot.ItemCount + averageNum);
+
+                await localSlot.slotArea.SetSlot(player, localSlot.slotIndex, slot);
+
+                if (eachSlot.IsEmpty)
+                {
+                    leftNum -= Math.Min(MaxStackCount - 0, averageNum);
+                }
+                else
+                {
+                    leftNum -= Math.Min(MaxStackCount - eachSlot.ItemCount, averageNum);
+                }
+            }
+
+            // now set left item for that slot is full
+            slot.ItemCount = (byte)leftNum;
+            slot.MakeEmptyIfZero();
+            await player.Tell(new SetDraggedSlot { Slot = slot });
+
+            // last, clear dragged path
+            await player.Tell(new SetDraggedPath { Path = new List<int>() });
+        }
+
+        protected virtual async Task OnDoubleClick(IPlayer player, List<SlotArea> slotAreas, int globalIndex, int slotIndex)
+        {
+            var slot = await GetSlot(player, slotIndex);
+            var draggedSlot = await player.Ask(AskDraggedSlot.Default);
+            short blockId = -1;
+
+            if (slot.BlockId != -1) blockId = slot.BlockId;
+            else if (draggedSlot.BlockId != -1) blockId = draggedSlot.BlockId;
+
+            int dragCount = draggedSlot.ItemCount;
+
+            foreach (var eachSlotArea in slotAreas)
+            {
+                for (int eachSlotIndex = 0; eachSlotIndex < eachSlotArea.SlotsCount; ++eachSlotIndex)
+                {
+                    var eachSlot = await eachSlotArea.GetSlot(player, eachSlotIndex);
+                    if (eachSlot.BlockId == blockId && eachSlot.ItemCount != 0)
+                    {
+                        // This count means to move how many block to your hand
+                        int moveCount = Math.Min(MaxStackCount - dragCount, eachSlot.ItemCount);
+                        eachSlot.ItemCount -= (byte)moveCount;
+                        dragCount += moveCount;
+                        await eachSlotArea.SetSlot(player, eachSlotIndex, eachSlot);
+                        if (dragCount >= MaxStackCount)
+                        {
+                            await player.Tell(new SetDraggedSlot
+                            {
+                                Slot = new Slot { ItemCount = (byte)dragCount, BlockId = blockId }
+                            });
+                            return;
+                        }
+                    }
+                }
+            }
+
+            await player.Tell(new SetDraggedSlot
+            {
+                Slot = new Slot { ItemCount = (byte)dragCount, BlockId = blockId }
+            });
+            return;
+        }
+
         protected bool TryStackSlot(ref Slot source, ref Slot target)
         {
             if (target.ItemCount <= MaxStackCount && target.CanStack(source))
@@ -240,6 +418,19 @@ namespace MineCase.Server.Game.Windows.SlotAreas
             return false;
         }
 
+        // TODO merge method in games/windows/WindowGrain.cs
+        protected (SlotArea slotArea, int slotIndex) GlobalSlotIndexToLocal(List<SlotArea> slotAreas, int slotIndex)
+        {
+            for (int i = 0; i < slotAreas.Count; i++)
+            {
+                if (slotIndex < slotAreas[i].SlotsCount)
+                    return (slotAreas[i], slotIndex);
+                slotIndex -= slotAreas[i].SlotsCount;
+            }
+
+            throw new ArgumentOutOfRangeException(nameof(slotIndex));
+        }
+
         public virtual Task Close(IPlayer player)
         {
             return Task.CompletedTask;

+ 11 - 1
src/MineCase.Server.Grains/Game/Windows/WindowGrain.cs

@@ -130,8 +130,18 @@ namespace MineCase.Server.Game.Windows
             {
                 case ClickAction.LeftMouseClick:
                 case ClickAction.RightMouseClick:
+
+                case ClickAction.LeftMouseDragBegin:
+                case ClickAction.LeftMouseAddSlot:
+                case ClickAction.LeftMouseDragEnd:
+
+                case ClickAction.RightMouseDragBegin:
+                case ClickAction.RightMouseAddSlot:
+                case ClickAction.RightMouseDragEnd:
+
+                case ClickAction.DoubleClick:
                     var slot = GlobalSlotIndexToLocal(slotIndex);
-                    await slot.slotArea.Click(player, slot.slotIndex, clickAction, clickedItem);
+                    await slot.slotArea.Click(player, SlotAreas, slotIndex, slot.slotIndex, clickAction, clickedItem);
                     break;
                 default:
                     break;

+ 38 - 0
src/MineCase.Server.Grains/Network/Play/ServerboundPacketComponent.cs

@@ -377,6 +377,44 @@ namespace MineCase.Server.Network.Play
                             break;
                     }
 
+                    break;
+
+                case 5:
+                    switch (button)
+                    {
+                        case 0:
+                            return ClickAction.LeftMouseDragBegin;
+                        case 4:
+                            return ClickAction.RightMouseDragBegin;
+                        case 8:
+                            return ClickAction.MiddleMouseDragBegin;
+                        case 1:
+                            return ClickAction.LeftMouseAddSlot;
+                        case 5:
+                            return ClickAction.RightMouseAddSlot;
+                        case 9:
+                            return ClickAction.MiddleMouseAddSlot;
+                        case 2:
+                            return ClickAction.LeftMouseDragEnd;
+                        case 6:
+                            return ClickAction.RightMouseDragEnd;
+                        case 10:
+                            return ClickAction.MiddleMouseDragEnd;
+                        default:
+                            break;
+                    }
+
+                    break;
+
+                case 6:
+                    switch (button)
+                    {
+                        case 0:
+                            return ClickAction.DoubleClick;
+                        default:
+                            break;
+                    }
+
                     break;
                 default:
                     break;

+ 12 - 0
src/MineCase.Server.Interfaces/Game/Entities/EntityMessages.cs

@@ -70,6 +70,18 @@ namespace MineCase.Server.Game.Entities.Components
         public static readonly AskDraggedSlot Default = new AskDraggedSlot();
     }
 
+    [Immutable]
+    public sealed class SetDraggedPath : IEntityMessage
+    {
+        public List<int> Path { get; set; }
+    }
+
+    [Immutable]
+    public sealed class AskDraggedPath : IEntityMessage<List<int>>
+    {
+        public static readonly AskDraggedPath Default = new AskDraggedPath();
+    }
+
     [Immutable]
     public sealed class AskPlayerDescription : IEntityMessage<PlayerDescription>
     {