Unique stacking rule (#844)

This commit is contained in:
TheMode 2022-04-01 01:00:18 +02:00 committed by GitHub
parent 8df0d37107
commit 4e6c92e2c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 64 additions and 97 deletions

View File

@ -77,7 +77,7 @@ public class ItemEntity extends Entity {
if (getDistance(itemEntity) > mergeRange) return;
final ItemStack itemStackEntity = itemEntity.getItemStack();
final StackingRule stackingRule = itemStack.getStackingRule();
final StackingRule stackingRule = StackingRule.get();
final boolean canStack = stackingRule.canBeStacked(itemStack, itemStackEntity);
if (!canStack) return;

View File

@ -21,7 +21,7 @@ public interface TransactionType {
*/
TransactionType ADD = (inventory, itemStack, slotPredicate, start, end, step) -> {
Int2ObjectMap<ItemStack> itemChangesMap = new Int2ObjectOpenHashMap<>();
final StackingRule stackingRule = itemStack.getStackingRule();
final StackingRule stackingRule = StackingRule.get();
// Check filled slot (not air)
for (int i = start; i < end; i += step) {
ItemStack inventoryItem = inventory.getItemStack(i);
@ -73,7 +73,7 @@ public interface TransactionType {
*/
TransactionType TAKE = (inventory, itemStack, slotPredicate, start, end, step) -> {
Int2ObjectMap<ItemStack> itemChangesMap = new Int2ObjectOpenHashMap<>();
final StackingRule stackingRule = itemStack.getStackingRule();
final StackingRule stackingRule = StackingRule.get();
for (int i = start; i < end; i += step) {
final ItemStack inventoryItem = inventory.getItemStack(i);
if (inventoryItem.isAir()) continue;

View File

@ -38,20 +38,19 @@ public final class InventoryClickProcessor {
if (result.isCancel()) return result;
clicked = result.getClicked();
cursor = result.getCursor();
final StackingRule cursorRule = cursor.getStackingRule();
final StackingRule clickedRule = clicked.getStackingRule();
if (cursorRule.canBeStacked(cursor, clicked)) {
final StackingRule rule = StackingRule.get();
if (rule.canBeStacked(cursor, clicked)) {
// Try to stack items
final int totalAmount = cursorRule.getAmount(cursor) + clickedRule.getAmount(clicked);
final int maxSize = cursorRule.getMaxSize(cursor);
if (!clickedRule.canApply(clicked, totalAmount)) {
final int totalAmount = rule.getAmount(cursor) + rule.getAmount(clicked);
final int maxSize = rule.getMaxSize(cursor);
if (!rule.canApply(clicked, totalAmount)) {
// Size is too big, stack as much as possible into clicked
result.setCursor(cursorRule.apply(cursor, totalAmount - maxSize));
result.setClicked(clickedRule.apply(clicked, maxSize));
result.setCursor(rule.apply(cursor, totalAmount - maxSize));
result.setClicked(rule.apply(clicked, maxSize));
} else {
// Merge cursor item clicked
result.setCursor(cursorRule.apply(cursor, 0));
result.setClicked(clickedRule.apply(clicked, totalAmount));
result.setCursor(rule.apply(cursor, 0));
result.setClicked(rule.apply(clicked, totalAmount));
}
} else {
// Items are not compatible, swap them
@ -68,31 +67,30 @@ public final class InventoryClickProcessor {
if (result.isCancel()) return result;
clicked = result.getClicked();
cursor = result.getCursor();
final StackingRule cursorRule = cursor.getStackingRule();
final StackingRule clickedRule = clicked.getStackingRule();
if (clickedRule.canBeStacked(clicked, cursor)) {
final StackingRule rule = StackingRule.get();
if (rule.canBeStacked(clicked, cursor)) {
// Items can be stacked
final int amount = clickedRule.getAmount(clicked) + 1;
if (!clickedRule.canApply(clicked, amount)) {
final int amount = rule.getAmount(clicked) + 1;
if (!rule.canApply(clicked, amount)) {
// Size too large, stop here
return result;
} else {
// Add 1 to clicked
result.setCursor(cursorRule.apply(cursor, operand -> operand - 1));
result.setClicked(clickedRule.apply(clicked, amount));
result.setCursor(rule.apply(cursor, operand -> operand - 1));
result.setClicked(rule.apply(clicked, amount));
}
} else {
// Items cannot be stacked
if (cursor.isAir()) {
// Take half of clicked
final int amount = (int) Math.ceil((double) clickedRule.getAmount(clicked) / 2d);
result.setCursor(cursorRule.apply(clicked, amount));
result.setClicked(clickedRule.apply(clicked, operand -> operand / 2));
final int amount = (int) Math.ceil((double) rule.getAmount(clicked) / 2d);
result.setCursor(rule.apply(clicked, amount));
result.setClicked(rule.apply(clicked, operand -> operand / 2));
} else {
if (clicked.isAir()) {
// Put 1 to clicked
result.setCursor(cursorRule.apply(cursor, operand -> operand - 1));
result.setClicked(clickedRule.apply(cursor, 1));
result.setCursor(rule.apply(cursor, operand -> operand - 1));
result.setClicked(rule.apply(cursor, 1));
} else {
// Swap items
result.setCursor(clicked);
@ -171,7 +169,7 @@ public final class InventoryClickProcessor {
int slot, int button,
@NotNull ItemStack clicked, @NotNull ItemStack cursor) {
InventoryClickResult clickResult = null;
final StackingRule stackingRule = cursor.getStackingRule();
final StackingRule stackingRule = StackingRule.get();
if (slot != -999) {
// Add slot
if (button == 1) {
@ -219,8 +217,7 @@ public final class InventoryClickProcessor {
final var inv = dragData.inventory;
final int s = dragData.slot;
ItemStack slotItem = inv.getItemStack(s);
final StackingRule slotItemRule = slotItem.getStackingRule();
final int amount = slotItemRule.getAmount(slotItem);
final int amount = stackingRule.getAmount(slotItem);
if (stackingRule.canBeStacked(cursor, slotItem)) {
if (stackingRule.canApply(slotItem, amount + slotSize)) {
// Append divided amount to slot
@ -270,10 +267,9 @@ public final class InventoryClickProcessor {
final var inv = dragData.inventory;
final int s = dragData.slot;
ItemStack slotItem = inv.getItemStack(s);
StackingRule slotItemRule = slotItem.getStackingRule();
if (stackingRule.canBeStacked(cursor, slotItem)) {
// Compatible item in the slot, increment by 1
final int amount = slotItemRule.getAmount(slotItem) + 1;
final int amount = stackingRule.getAmount(slotItem) + 1;
if (stackingRule.canApply(slotItem, amount)) {
slotItem = stackingRule.apply(slotItem, amount);
finalCursorAmount -= 1;
@ -299,9 +295,9 @@ public final class InventoryClickProcessor {
if (clickResult.isCancel()) return clickResult;
if (cursor.isAir()) return clickResult.cancelled();
final StackingRule cursorRule = cursor.getStackingRule();
final int amount = cursorRule.getAmount(cursor);
final int maxSize = cursorRule.getMaxSize(cursor);
final StackingRule rule = StackingRule.get();
final int amount = rule.getAmount(cursor);
final int maxSize = rule.getMaxSize(cursor);
final int remainingAmount = maxSize - amount;
if (remainingAmount == 0) {
// Item is already full
@ -323,7 +319,7 @@ public final class InventoryClickProcessor {
return itemResult;
};
ItemStack remain = cursorRule.apply(cursor, remainingAmount);
ItemStack remain = rule.apply(cursor, remainingAmount);
final var playerInventory = player.getInventory();
// Retrieve remain
if (Objects.equals(clickedInventory, inventory)) {
@ -346,10 +342,10 @@ public final class InventoryClickProcessor {
// Update cursor based on the remaining
if (remain.isAir()) {
// Item has been filled
clickResult.setCursor(cursorRule.apply(cursor, maxSize));
clickResult.setCursor(rule.apply(cursor, maxSize));
} else {
final int tookAmount = remainingAmount - cursorRule.getAmount(remain);
clickResult.setCursor(cursorRule.apply(cursor, amount + tookAmount));
final int tookAmount = remainingAmount - rule.getAmount(remain);
clickResult.setCursor(rule.apply(cursor, amount + tookAmount));
}
return clickResult;
}
@ -360,8 +356,7 @@ public final class InventoryClickProcessor {
final InventoryClickResult clickResult = startCondition(player, inventory, slot, ClickType.DROP, clicked, cursor);
if (clickResult.isCancel()) return clickResult;
final StackingRule clickedRule = clicked.getStackingRule();
final StackingRule cursorRule = cursor.getStackingRule();
final StackingRule rule = StackingRule.get();
ItemStack resultClicked = clicked;
ItemStack resultCursor = cursor;
@ -370,44 +365,44 @@ public final class InventoryClickProcessor {
// Click outside
if (button == 0) {
// Left (drop all)
final int amount = cursorRule.getAmount(resultCursor);
final ItemStack dropItem = cursorRule.apply(resultCursor, amount);
final int amount = rule.getAmount(resultCursor);
final ItemStack dropItem = rule.apply(resultCursor, amount);
final boolean dropResult = player.dropItem(dropItem);
clickResult.setCancel(!dropResult);
if (dropResult) {
resultCursor = cursorRule.apply(resultCursor, 0);
resultCursor = rule.apply(resultCursor, 0);
}
} else if (button == 1) {
// Right (drop 1)
final ItemStack dropItem = cursorRule.apply(resultCursor, 1);
final ItemStack dropItem = rule.apply(resultCursor, 1);
final boolean dropResult = player.dropItem(dropItem);
clickResult.setCancel(!dropResult);
if (dropResult) {
final int amount = cursorRule.getAmount(resultCursor);
final int amount = rule.getAmount(resultCursor);
final int newAmount = amount - 1;
resultCursor = cursorRule.apply(resultCursor, newAmount);
resultCursor = rule.apply(resultCursor, newAmount);
}
}
} else if (!all) {
if (button == 0) {
// Drop key Q (drop 1)
final ItemStack dropItem = cursorRule.apply(resultClicked, 1);
final ItemStack dropItem = rule.apply(resultClicked, 1);
final boolean dropResult = player.dropItem(dropItem);
clickResult.setCancel(!dropResult);
if (dropResult) {
final int amount = clickedRule.getAmount(resultClicked);
final int amount = rule.getAmount(resultClicked);
final int newAmount = amount - 1;
resultClicked = cursorRule.apply(resultClicked, newAmount);
resultClicked = rule.apply(resultClicked, newAmount);
}
} else if (button == 1) {
// Ctrl + Drop key Q (drop all)
final int amount = cursorRule.getAmount(resultClicked);
final ItemStack dropItem = clickedRule.apply(resultClicked, amount);
final int amount = rule.getAmount(resultClicked);
final ItemStack dropItem = rule.apply(resultClicked, amount);
final boolean dropResult = player.dropItem(dropItem);
clickResult.setCancel(!dropResult);
if (dropResult) {
resultClicked = cursorRule.apply(resultClicked, 0);
resultClicked = rule.apply(resultClicked, 0);
}
}
}

View File

@ -27,26 +27,22 @@ import java.util.function.UnaryOperator;
*/
public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent.ShowItem> {
private static final @NotNull VanillaStackingRule DEFAULT_STACKING_RULE = new VanillaStackingRule();
static final @NotNull VanillaStackingRule DEFAULT_STACKING_RULE = new VanillaStackingRule();
/**
* Constant AIR item. Should be used instead of 'null'.
*/
public static final @NotNull ItemStack AIR = ItemStack.of(Material.AIR);
private final StackingRule stackingRule;
private final Material material;
private final int amount;
private final ItemMeta meta;
ItemStack(@NotNull Material material, int amount,
@NotNull ItemMeta meta,
@Nullable StackingRule stackingRule) {
@NotNull ItemMeta meta) {
this.material = material;
this.amount = amount;
this.meta = meta;
this.stackingRule = Objects.requireNonNullElse(stackingRule, DEFAULT_STACKING_RULE);
}
@Contract(value = "_ -> new", pure = true)
@ -68,7 +64,7 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
public static @NotNull ItemStack fromNBT(@NotNull Material material, @Nullable NBTCompound nbtCompound, int amount) {
ItemMetaBuilder builder = ItemStackBuilder.getMetaBuilder(material);
if (nbtCompound != null) ItemMetaBuilder.resetMeta(builder, nbtCompound);
return new ItemStack(material, amount, builder.build(), null);
return new ItemStack(material, amount, builder.build());
}
@Contract(value = "_, _ -> new", pure = true)
@ -114,7 +110,7 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
@Contract(value = "_, -> new", pure = true)
public @NotNull ItemStack withAmount(int amount) {
if (amount < 1) return AIR;
return new ItemStack(material, amount, meta, stackingRule);
return new ItemStack(material, amount, meta);
}
@Contract(value = "_, -> new", pure = true)
@ -125,7 +121,7 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
@ApiStatus.Experimental
@Contract(value = "_, -> new", pure = true)
public @NotNull ItemStack consume(int amount) {
return stackingRule.apply(this, currentAmount -> currentAmount - amount);
return DEFAULT_STACKING_RULE.apply(this, currentAmount -> currentAmount - amount);
}
@Contract(value = "_, _ -> new", pure = true)
@ -141,7 +137,7 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
@ApiStatus.Experimental
@Contract(value = "_ -> new", pure = true)
public @NotNull ItemStack withMeta(@NotNull ItemMeta meta) {
return new ItemStack(material, amount, meta, stackingRule);
return new ItemStack(material, amount, meta);
}
@Contract(pure = true)
@ -174,11 +170,6 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
return withLore(loreUnaryOperator.apply(getLore()));
}
@Contract(pure = true)
public @NotNull StackingRule getStackingRule() {
return stackingRule;
}
@Contract(pure = true)
public @NotNull ItemMeta getMeta() {
return meta;
@ -198,30 +189,19 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ItemStack itemStack = (ItemStack) o;
if (amount != itemStack.amount) return false;
if (!stackingRule.equals(itemStack.stackingRule)) return false;
if (material != itemStack.material) return false;
return meta.equals(itemStack.meta);
if (!(o instanceof ItemStack itemStack)) return false;
return amount == itemStack.amount && material.equals(itemStack.material) && meta.equals(itemStack.meta);
}
@Override
public int hashCode() {
int result = stackingRule.hashCode();
result = 31 * result + material.hashCode();
result = 31 * result + amount;
result = 31 * result + meta.hashCode();
return result;
return Objects.hash(material, amount, meta);
}
@Override
public String toString() {
return "ItemStack{" +
"stackingRule=" + stackingRule +
", material=" + material +
"material=" + material +
", amount=" + amount +
", meta=" + meta +
'}';
@ -259,8 +239,6 @@ public final class ItemStack implements TagReadable, HoverEventSource<HoverEvent
@Contract(value = "-> new", pure = true)
private @NotNull ItemStackBuilder builder() {
return new ItemStackBuilder(material, meta.builder())
.amount(amount)
.stackingRule(stackingRule);
return new ItemStackBuilder(material, meta.builder()).amount(amount);
}
}

View File

@ -2,7 +2,6 @@ package net.minestom.server.item;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.metadata.*;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -20,8 +19,6 @@ public final class ItemStackBuilder {
private int amount;
private ItemMetaBuilder metaBuilder;
private StackingRule stackingRule;
ItemStackBuilder(@NotNull Material material, @NotNull ItemMetaBuilder metaBuilder) {
this.material = material;
this.amount = 1;
@ -106,17 +103,10 @@ public final class ItemStackBuilder {
return this;
}
@ApiStatus.Experimental
@Contract(value = "_ -> this")
public @NotNull ItemStackBuilder stackingRule(@Nullable StackingRule stackingRule) {
this.stackingRule = stackingRule;
return this;
}
@Contract(value = "-> new", pure = true)
public @NotNull ItemStack build() {
if (amount < 1) return ItemStack.AIR;
return new ItemStack(material, amount, metaBuilder.build(), stackingRule);
return new ItemStack(material, amount, metaBuilder.build());
}
private static final class DefaultMeta extends ItemMetaBuilder {

View File

@ -12,6 +12,10 @@ import java.util.function.IntUnaryOperator;
*/
public interface StackingRule {
static @NotNull StackingRule get() {
return ItemStack.DEFAULT_STACKING_RULE;
}
/**
* Used to know if two {@link ItemStack} can be stacked together.
*

View File

@ -135,7 +135,7 @@ public class BlockPlacementListener {
// Block consuming
if (playerBlockPlaceEvent.doesConsumeBlock()) {
// Consume the block in the player's hand
final ItemStack newUsedItem = usedItem.getStackingRule().apply(usedItem, usedItem.getAmount() - 1);
final ItemStack newUsedItem = usedItem.consume(1);
playerInventory.setItemInHand(hand, newUsedItem);
}
}

View File

@ -97,7 +97,7 @@ public final class PlayerDiggingListener {
private static void dropSingle(Player player) {
final ItemStack handItem = player.getInventory().getItemInMainHand();
final StackingRule stackingRule = handItem.getStackingRule();
final StackingRule stackingRule = StackingRule.get();
final int handAmount = stackingRule.getAmount(handItem);
if (handAmount <= 1) {
// Drop the whole item without copy