Added stack merging, RMB actions, Sticks and fixed an unholy bug
- Added Test:Stick - When left-clicking, stacks that can merge will - Added right-clicking behavior straight from Minecraft - hooooooly sh*t man StatefulObject.equals was totally broken omfg how tf i did notice :o
This commit is contained in:
		| @@ -235,7 +235,7 @@ public abstract class StatefulObject extends Namespaced implements Encodable { | |||||||
|  |  | ||||||
| 		StatefulObject statefulObj = (StatefulObject) obj; | 		StatefulObject statefulObj = (StatefulObject) obj; | ||||||
|  |  | ||||||
| 		if (statefulObj.getId().equals(this.getId())) | 		if (!statefulObj.getId().equals(this.getId())) | ||||||
| 			return false; | 			return false; | ||||||
|  |  | ||||||
| 		return true; | 		return true; | ||||||
|   | |||||||
| @@ -94,6 +94,21 @@ public class ItemSlot implements Encodable { | |||||||
| 		return amount == 0; | 		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() { | 	private synchronized void checkState() { | ||||||
| 		if ((contents == null) != (amount == 0)) { | 		if ((contents == null) != (amount == 0)) { | ||||||
| 			if (contents == null) { | 			if (contents == null) { | ||||||
|   | |||||||
| @@ -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 <https://www.gnu.org/licenses/>. | ||||||
|  |  */ | ||||||
|  | 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() { | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -65,7 +65,8 @@ public class PlayerManager { | |||||||
| 		 | 		 | ||||||
| 		player.getInventory().addSlots(10); | 		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.getInventory().getSlot(6).setContents(ItemDataRegistry.getInstance().create("Test:MoonTypeIceCream"), 1); | ||||||
|  |  | ||||||
| 		player.setPosition(getServer().getWorld().getGenerator().suggestSpawnLocation()); | 		player.setPosition(getServer().getWorld().getGenerator().suggestSpawnLocation()); | ||||||
|   | |||||||
| @@ -106,7 +106,7 @@ public class TestContent { | |||||||
| 		register(new BlockRenderNone("Test:Air")); | 		register(new BlockRenderNone("Test:Air")); | ||||||
| 		register(new TestBlockLogicAir("Test:Air")); | 		register(new TestBlockLogicAir("Test:Air")); | ||||||
| 		placeableBlacklist.add("Test:Air"); | 		placeableBlacklist.add("Test:Air"); | ||||||
|  | 		 | ||||||
| 		registerSimplestBlock("Dirt"); | 		registerSimplestBlock("Dirt"); | ||||||
| 		registerSimplestBlock("Chernozem"); | 		registerSimplestBlock("Chernozem"); | ||||||
| 		registerSimplestBlock("Stone"); | 		registerSimplestBlock("Stone"); | ||||||
| @@ -244,6 +244,7 @@ public class TestContent { | |||||||
| 	 | 	 | ||||||
| 	private static void registerItems() { | 	private static void registerItems() { | ||||||
| 		registerSimplestItem("MoonTypeIceCream", Units.get("200 g"), Units.get("1 L")); | 		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) { | 	private static void registerSimplestBlock(String name) { | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ | |||||||
| package ru.windcorp.progressia.test.inv; | package ru.windcorp.progressia.test.inv; | ||||||
|  |  | ||||||
| import java.util.function.Consumer; | import java.util.function.Consumer; | ||||||
|  | import java.util.function.Supplier; | ||||||
|  |  | ||||||
| import org.lwjgl.glfw.GLFW; | 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.Panel; | ||||||
| import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; | import ru.windcorp.progressia.client.graphics.gui.layout.LayoutAlign; | ||||||
| import ru.windcorp.progressia.client.graphics.gui.layout.LayoutFill; | 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.client.graphics.model.Renderable; | ||||||
| import ru.windcorp.progressia.common.world.entity.EntityDataPlayer; | import ru.windcorp.progressia.common.world.entity.EntityDataPlayer; | ||||||
| import ru.windcorp.progressia.common.world.item.ItemContainer; | 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.ItemSlot; | ||||||
|  | import ru.windcorp.progressia.common.world.item.Items; | ||||||
|  |  | ||||||
| public class InventoryScreen extends Component { | public class InventoryScreen extends Component { | ||||||
|  |  | ||||||
| @@ -131,34 +134,72 @@ public class InventoryScreen extends Component { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	private void addListeners(InventoryComponent mainInventory) { | 	private void addListeners(InventoryComponent mainInventory) { | ||||||
| 		Consumer<BasicButton> pickIntoLeft = createPickAction(leftHand.getSlot(0), rightHand.getSlot(0)); | 		 | ||||||
|  | 		ItemSlot left = leftHand.getSlot(0); | ||||||
| 		for (DecoratedSlotComponent component : mainInventory.getSlots()) { | 		ItemSlot right = rightHand.getSlot(0); | ||||||
| 			component.addAction(pickIntoLeft); | 		 | ||||||
| 		} | 		Supplier<ItemSlot> handSlot = () -> { | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	private Consumer<BasicButton> createPickAction(ItemSlot toWithoutCtrl, ItemSlot toWithCtrl) { |  | ||||||
| 		return button -> { |  | ||||||
|  |  | ||||||
| 			boolean hasCtrl = InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL) | 			boolean hasCtrl = InputTracker.isKeyPressed(GLFW.GLFW_KEY_LEFT_CONTROL) | ||||||
| 				|| InputTracker.isKeyPressed(GLFW.GLFW_KEY_RIGHT_CONTROL); | 				|| InputTracker.isKeyPressed(GLFW.GLFW_KEY_RIGHT_CONTROL); | ||||||
| 			 | 			 | ||||||
| 			ItemSlot to = hasCtrl ? toWithCtrl : toWithoutCtrl; | 			return hasCtrl ? left : right; | ||||||
| 			ItemSlot from = ((DecoratedSlotComponent) button).getSlot(); | 		}; | ||||||
|  | 		 | ||||||
|  | 		Consumer<BasicButton> pickAll = createPickAllAction(handSlot); | ||||||
|  |  | ||||||
| 			ItemData fromData = from.getContents(); | 		for (DecoratedSlotComponent component : mainInventory.getSlots()) { | ||||||
| 			int fromAmount = from.getAmount(); | 			component.addAction(pickAll); | ||||||
|  | 			component.addListener(KeyEvent.class, createRMBAction(component.getSlot(), handSlot)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	private Consumer<BasicButton> createPickAllAction(Supplier<ItemSlot> handSlotChooser) { | ||||||
|  | 		return button -> { | ||||||
| 			 | 			 | ||||||
| 			ItemData toData = to.getContents(); | 			ItemSlot handSlot = handSlotChooser.get(); | ||||||
| 			int toAmount = to.getAmount(); | 			ItemSlot invSlot = ((DecoratedSlotComponent) button).getSlot(); | ||||||
|  |  | ||||||
| 			from.setContents(toData, toAmount); | 			if (Items.pour(handSlot, invSlot) == 0) { | ||||||
| 			to.setContents(fromData, fromAmount); | 				if (!Items.swap(handSlot, invSlot)) { | ||||||
|  | 					return; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			requestReassembly(); | 			requestReassembly(); | ||||||
|  |  | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	private InputListener<KeyEvent> createRMBAction(ItemSlot invSlot, Supplier<ItemSlot> 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; | ||||||
|  | 			 | ||||||
|  | 		}; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/assets/textures/items/Stick.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/assets/textures/items/Stick.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 775 B | 
		Reference in New Issue
	
	Block a user