diff --git a/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java b/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java index 2feb7e6..3b78898 100644 --- a/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java +++ b/src/main/java/ru/windcorp/progressia/common/state/StatefulObject.java @@ -235,7 +235,7 @@ public abstract class StatefulObject extends Namespaced implements Encodable { StatefulObject statefulObj = (StatefulObject) obj; - if (statefulObj.getId().equals(this.getId())) + if (!statefulObj.getId().equals(this.getId())) return false; return true; diff --git a/src/main/java/ru/windcorp/progressia/common/world/item/ItemSlot.java b/src/main/java/ru/windcorp/progressia/common/world/item/ItemSlot.java index 390266c..c8cfa1b 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/item/ItemSlot.java +++ b/src/main/java/ru/windcorp/progressia/common/world/item/ItemSlot.java @@ -94,6 +94,21 @@ public class ItemSlot implements Encodable { return amount == 0; } + public synchronized boolean canInsert(ItemData contents, int amount) { + + // Ignore amount + + if (this.contents == null) { + return true; + } + + return this.contents.equals(contents); + } + + public synchronized boolean canRemove(int amount) { + return this.amount >= amount; + } + private synchronized void checkState() { if ((contents == null) != (amount == 0)) { if (contents == null) { diff --git a/src/main/java/ru/windcorp/progressia/common/world/item/Items.java b/src/main/java/ru/windcorp/progressia/common/world/item/Items.java new file mode 100644 index 0000000..e8ab5c6 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/item/Items.java @@ -0,0 +1,118 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.common.world.item; + +public class Items { + + /** + * Attempts to transfer as many items as possible, but no more than + * {@code max}, between two slots. The origin of the items is {@code from}, + * the destination is {@code into}. This method does nothing if the items + * could not be removed from origin or could not be inserted into + * destination, such as when {@code into} contains different items. + * + * @param from the slot to transfer item from + * @param into the destination of the items + * @param max the maximum amount of items to transfer. Use + * {@code Integer.MAX_VALUE} to remove the limit + * @return the actual amount of items moved between slots + */ + public static int pour(ItemSlot from, ItemSlot into, int max) { + synchronized (from) { + synchronized (into) { + + ItemData item = from.getContents(); + + int originalAmount = from.getAmount(); + int transferAmount = Math.min(originalAmount, max); + + while (transferAmount > 0 && !into.canInsert(item, transferAmount)) { + transferAmount--; + } + + if (transferAmount == 0) { + return 0; + } + + if (!from.canRemove(transferAmount)) { + return 0; + } + + into.setContents(item, into.getAmount() + transferAmount); + from.setAmount(originalAmount - transferAmount); + + return transferAmount; + + } + } + } + + /** + * Attempts to transfer as many items as possible between two slots. The + * origin of the items is {@code from}, the destination is {@code into}. + * This method does nothing if the items could not be removed from origin or + * could not be inserted into destination, such as when {@code into} + * contains different items. + * + * @param from the slot to transfer item from + * @param into the destination of the items + * @return the actual amount of items moved between slots + */ + public static int pour(ItemSlot from, ItemSlot into) { + return pour(from, into, Integer.MAX_VALUE); + } + + /** + * Attempts to swap the contents of the two slots. + * + * @param a one of the slots + * @param b the other slot + * + * @return whether the swap succeeded + */ + public static boolean swap(ItemSlot a, ItemSlot b) { + synchronized (a) { + synchronized (b) { + + ItemData aItem = a.getContents(); + int aAmount = a.getAmount(); + + ItemData bItem = b.getContents(); + int bAmount = b.getAmount(); + + a.clear(); + b.clear(); + + if (a.canInsert(bItem, bAmount) && b.canInsert(aItem, aAmount)) { + a.setContents(bItem, bAmount); + b.setContents(aItem, aAmount); + return true; + } else { + a.setContents(aItem, aAmount); + b.setContents(bItem, bAmount); + return false; + } + + } + } + } + + private Items() { + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java index dd07d60..1953184 100644 --- a/src/main/java/ru/windcorp/progressia/server/PlayerManager.java +++ b/src/main/java/ru/windcorp/progressia/server/PlayerManager.java @@ -65,7 +65,8 @@ public class PlayerManager { player.getInventory().addSlots(10); - player.getInventory().getSlot(3).setContents(ItemDataRegistry.getInstance().create("Test:MoonTypeIceCream"), 5); + player.getInventory().getSlot(3).setContents(ItemDataRegistry.getInstance().create("Test:Stick"), 5); + player.getInventory().getSlot(5).setContents(ItemDataRegistry.getInstance().create("Test:Stick"), 4); player.getInventory().getSlot(6).setContents(ItemDataRegistry.getInstance().create("Test:MoonTypeIceCream"), 1); player.setPosition(getServer().getWorld().getGenerator().suggestSpawnLocation()); diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 2582fb8..a265d21 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -106,7 +106,7 @@ public class TestContent { register(new BlockRenderNone("Test:Air")); register(new TestBlockLogicAir("Test:Air")); placeableBlacklist.add("Test:Air"); - + registerSimplestBlock("Dirt"); registerSimplestBlock("Chernozem"); registerSimplestBlock("Stone"); @@ -244,6 +244,7 @@ public class TestContent { private static void registerItems() { registerSimplestItem("MoonTypeIceCream", Units.get("200 g"), Units.get("1 L")); + registerSimplestItem("Stick", Units.get("260 g"), Units.get("0.5 L")); } private static void registerSimplestBlock(String name) { diff --git a/src/main/java/ru/windcorp/progressia/test/inv/InventoryScreen.java b/src/main/java/ru/windcorp/progressia/test/inv/InventoryScreen.java index dbbdce1..08aa9fa 100644 --- a/src/main/java/ru/windcorp/progressia/test/inv/InventoryScreen.java +++ b/src/main/java/ru/windcorp/progressia/test/inv/InventoryScreen.java @@ -18,6 +18,7 @@ package ru.windcorp.progressia.test.inv; import java.util.function.Consumer; +import java.util.function.Supplier; import org.lwjgl.glfw.GLFW; @@ -33,11 +34,13 @@ import ru.windcorp.progressia.client.graphics.gui.Layout; import ru.windcorp.progressia.client.graphics.gui.Panel; import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill; +import ru.windcorp.progressia.client.graphics.input.KeyEvent; +import ru.windcorp.progressia.client.graphics.input.bus.InputListener; import ru.windcorp.progressia.client.graphics.model.Renderable; import ru.windcorp.progressia.common.world.entity.EntityDataPlayer; import ru.windcorp.progressia.common.world.item.ItemContainer; -import ru.windcorp.progressia.common.world.item.ItemData; import ru.windcorp.progressia.common.world.item.ItemSlot; +import ru.windcorp.progressia.common.world.item.Items; public class InventoryScreen extends Component { @@ -131,34 +134,72 @@ public class InventoryScreen extends Component { } private void addListeners(InventoryComponent mainInventory) { - Consumer pickIntoLeft = createPickAction(leftHand.getSlot(0), rightHand.getSlot(0)); - - for (DecoratedSlotComponent component : mainInventory.getSlots()) { - component.addAction(pickIntoLeft); - } - } - - private Consumer createPickAction(ItemSlot toWithoutCtrl, ItemSlot toWithCtrl) { - return button -> { - + + ItemSlot left = leftHand.getSlot(0); + ItemSlot right = rightHand.getSlot(0); + + Supplier handSlot = () -> { boolean hasCtrl = InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL) || InputTracker.isKeyPressed(GLFW.GLFW_KEY_RIGHT_CONTROL); - ItemSlot to = hasCtrl ? toWithCtrl : toWithoutCtrl; - ItemSlot from = ((DecoratedSlotComponent) button).getSlot(); + return hasCtrl ? left : right; + }; + + Consumer pickAll = createPickAllAction(handSlot); - ItemData fromData = from.getContents(); - int fromAmount = from.getAmount(); + for (DecoratedSlotComponent component : mainInventory.getSlots()) { + component.addAction(pickAll); + component.addListener(KeyEvent.class, createRMBAction(component.getSlot(), handSlot)); + } + } + + private Consumer createPickAllAction(Supplier handSlotChooser) { + return button -> { - ItemData toData = to.getContents(); - int toAmount = to.getAmount(); + ItemSlot handSlot = handSlotChooser.get(); + ItemSlot invSlot = ((DecoratedSlotComponent) button).getSlot(); - from.setContents(toData, toAmount); - to.setContents(fromData, fromAmount); + if (Items.pour(handSlot, invSlot) == 0) { + if (!Items.swap(handSlot, invSlot)) { + return; + } + } requestReassembly(); }; } + private InputListener createRMBAction(ItemSlot invSlot, Supplier handSlotChooser) { + return input -> { + + if (input.isPress() && input.isRightMouseButton()) { + ItemSlot handSlot = handSlotChooser.get(); + + boolean success = false; + + if (handSlot.isEmpty()) { + success = Items.pour(invSlot, handSlot, invSlot.getAmount() / 2) != 0; + } + + if (!success) { + success = Items.pour(handSlot, invSlot, 1) != 0; + } + + if (!success) { + success = Items.swap(handSlot, invSlot); + } + + if (success) { + requestReassembly(); + } + + return true; + } + + return false; + + }; + } + } diff --git a/src/main/resources/assets/textures/items/Stick.png b/src/main/resources/assets/textures/items/Stick.png new file mode 100644 index 0000000..7271702 Binary files /dev/null and b/src/main/resources/assets/textures/items/Stick.png differ