From 42b73413bf35bede2d46347de62238bf02d1fc03 Mon Sep 17 00:00:00 2001 From: TheBrokenRail Date: Tue, 16 Jun 2020 12:21:17 -0400 Subject: [PATCH] Shulker Box Boats --- README.md | 2 +- .../java/com/thebrokenrail/twine/Twine.java | 2 + .../advancement/ShulkerBoxBoatCriterion.java | 29 ++++ .../twine/item/BackpackItem.java | 80 ---------- .../twine/mixin/MixinBoatEntity.java | 147 +++++++++--------- .../twine/mixin/MixinBoatEntityRenderer.java | 3 +- .../MixinClientPlayerInteractionManager.java | 3 +- .../mixin/MixinServerPlayNetworkHandler.java | 3 +- .../thebrokenrail/twine/mixin/MixinSlot.java | 3 +- .../twine/util/BackpackInventory.java | 18 +++ .../twine/util/BackpackScreenHandler.java | 3 +- .../twine/util/BoatChestMode.java | 143 +++++++++++++++++ .../twine/util/BoatInventory.java | 21 ++- .../thebrokenrail/twine/util/BoatUtil.java | 42 +---- .../util/EnderChestInventoryWrapper.java | 3 +- .../twine/util/ItemInventory.java | 84 ++++++++++ .../resources/assets/twine/lang/en_us.json | 3 + .../twine/advancements/shulker_box_boat.json | 19 +++ 18 files changed, 396 insertions(+), 212 deletions(-) create mode 100644 src/main/java/com/thebrokenrail/twine/advancement/ShulkerBoxBoatCriterion.java create mode 100644 src/main/java/com/thebrokenrail/twine/util/BackpackInventory.java create mode 100644 src/main/java/com/thebrokenrail/twine/util/BoatChestMode.java create mode 100644 src/main/java/com/thebrokenrail/twine/util/ItemInventory.java create mode 100644 src/main/resources/data/twine/advancements/shulker_box_boat.json diff --git a/README.md b/README.md index 0e1fb34..ddb188b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Glowing Obsidian heals monsters and hurts everything else. It naturally generate ## Chest Boats -Right-Click a Boat with a Chest to place it in the Boat, you can also use an Ender Chest or Trapped Chest. Shift-Right-Click the Boat to open the Chest. You can also open the Chest inside the Boat by opening your inventory. +Right-Click a Boat with a Chest to place it in the Boat, you can also use an Ender Chest, Trapped Chest, or Shulker Box. Shift-Right-Click the Boat to open the Chest. You can also open the Chest inside the Boat by opening your inventory. ## Difficulty Stages Each player has a "personal" stage of an area, the highest online player's difficulty stage in an area will be the "effective" difficulty stage of that area. diff --git a/src/main/java/com/thebrokenrail/twine/Twine.java b/src/main/java/com/thebrokenrail/twine/Twine.java index af429df..30c48e1 100644 --- a/src/main/java/com/thebrokenrail/twine/Twine.java +++ b/src/main/java/com/thebrokenrail/twine/Twine.java @@ -2,6 +2,7 @@ package com.thebrokenrail.twine; import com.thebrokenrail.twine.advancement.ChestBoatCriterion; import com.thebrokenrail.twine.advancement.EnderChestBoatCriterion; +import com.thebrokenrail.twine.advancement.ShulkerBoxBoatCriterion; import com.thebrokenrail.twine.block.CreativeItemSpawnerBlock; import com.thebrokenrail.twine.block.GlowingObsidianBlock; import com.thebrokenrail.twine.item.BackpackItem; @@ -47,6 +48,7 @@ public class Twine implements ModInitializer { public static ChestBoatCriterion CHEST_BOAT_CRITERION = CriteriaHook.callRegister(new ChestBoatCriterion()); public static EnderChestBoatCriterion ENDER_CHEST_BOAT_CRITERION = CriteriaHook.callRegister(new EnderChestBoatCriterion()); + public static ShulkerBoxBoatCriterion SHULKER_BOX_BOAT_CRITERION = CriteriaHook.callRegister(new ShulkerBoxBoatCriterion()); @Override public void onInitialize() { diff --git a/src/main/java/com/thebrokenrail/twine/advancement/ShulkerBoxBoatCriterion.java b/src/main/java/com/thebrokenrail/twine/advancement/ShulkerBoxBoatCriterion.java new file mode 100644 index 0000000..de9fe34 --- /dev/null +++ b/src/main/java/com/thebrokenrail/twine/advancement/ShulkerBoxBoatCriterion.java @@ -0,0 +1,29 @@ +package com.thebrokenrail.twine.advancement; + +import com.google.gson.JsonObject; +import com.thebrokenrail.twine.Twine; +import net.minecraft.advancement.criterion.AbstractCriterion; +import net.minecraft.advancement.criterion.AbstractCriterionConditions; +import net.minecraft.predicate.entity.AdvancementEntityPredicateDeserializer; +import net.minecraft.predicate.entity.EntityPredicate; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.Identifier; + +public class ShulkerBoxBoatCriterion extends AbstractCriterion { + private static final Identifier ID = new Identifier(Twine.NAMESPACE, "shulker_box_boat"); + + public void trigger(ServerPlayerEntity player) { + this.test(player, conditions -> true); + } + + @Override + protected AbstractCriterionConditions conditionsFromJson(JsonObject obj, EntityPredicate.Extended playerPredicate, AdvancementEntityPredicateDeserializer predicateDeserializer) { + return new AbstractCriterionConditions(ID, playerPredicate) { + }; + } + + @Override + public Identifier getId() { + return ID; + } +} diff --git a/src/main/java/com/thebrokenrail/twine/item/BackpackItem.java b/src/main/java/com/thebrokenrail/twine/item/BackpackItem.java index 139584c..167d6bc 100644 --- a/src/main/java/com/thebrokenrail/twine/item/BackpackItem.java +++ b/src/main/java/com/thebrokenrail/twine/item/BackpackItem.java @@ -5,8 +5,6 @@ import com.thebrokenrail.twine.util.BackpackScreenHandler; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.inventory.Inventories; -import net.minecraft.inventory.Inventory; import net.minecraft.item.DyeableItem; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -17,87 +15,9 @@ import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.TypedActionResult; -import net.minecraft.util.collection.DefaultedList; import net.minecraft.world.World; public class BackpackItem extends Item implements DyeableItem { - public static class ItemInventory implements Inventory { - private final int size; - private final ItemStack stack; - private final Hand hand; - private final DefaultedList inv; - - public ItemInventory(int size, PlayerEntity player, Hand hand) { - this.size = size; - this.stack = player.getStackInHand(hand); - this.hand = hand; - inv = getInv(); - } - - private DefaultedList getInv() { - DefaultedList list = DefaultedList.ofSize(size, ItemStack.EMPTY); - Inventories.fromTag(stack.getOrCreateTag(), list); - return list; - } - - private void setInv(DefaultedList list) { - Inventories.toTag(stack.getOrCreateTag(), list); - } - - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - for (ItemStack item : inv) { - if (item != ItemStack.EMPTY) { - return false; - } - } - return true; - } - - @Override - public ItemStack getStack(int slot) { - return inv.get(slot); - } - - @Override - public ItemStack removeStack(int slot, int amount) { - return inv.get(slot).split(amount); - } - - @Override - public ItemStack removeStack(int slot) { - ItemStack item = inv.get(slot); - return item.split(item.getCount()); - } - - @Override - public void setStack(int slot, ItemStack stack) { - inv.set(slot, stack); - } - - @Override - public void markDirty() { - setInv(inv); - } - - @Override - public boolean canPlayerUse(PlayerEntity player) { - return player.getStackInHand(hand) == stack; - } - - @Override - public void clear() { - for (int i = 0; i < size; i++) { - inv.set(i, ItemStack.EMPTY); - } - } - } - private final boolean large; public BackpackItem(boolean large) { diff --git a/src/main/java/com/thebrokenrail/twine/mixin/MixinBoatEntity.java b/src/main/java/com/thebrokenrail/twine/mixin/MixinBoatEntity.java index de72496..8cb7e19 100644 --- a/src/main/java/com/thebrokenrail/twine/mixin/MixinBoatEntity.java +++ b/src/main/java/com/thebrokenrail/twine/mixin/MixinBoatEntity.java @@ -1,21 +1,21 @@ package com.thebrokenrail.twine.mixin; -import com.thebrokenrail.twine.Twine; +import com.thebrokenrail.twine.util.BoatChestMode; import com.thebrokenrail.twine.util.BoatInventory; import com.thebrokenrail.twine.util.BoatUtil; -import com.thebrokenrail.twine.util.EnderChestInventoryWrapper; +import net.minecraft.block.Block; import net.minecraft.block.BlockState; -import net.minecraft.block.EnderChestBlock; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.entity.Entity; import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.data.DataTracker; +import net.minecraft.entity.data.TrackedData; +import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.vehicle.BoatEntity; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.screen.GenericContainerScreenHandler; import net.minecraft.screen.SimpleNamedScreenHandlerFactory; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.ActionResult; @@ -34,21 +34,23 @@ import java.util.List; @Mixin(BoatEntity.class) public class MixinBoatEntity implements BoatUtil { + private static final TrackedData CHEST_MODE = DataTracker.registerData(BoatEntity.class, TrackedDataHandlerRegistry.INTEGER); + @Inject(at = @At("RETURN"), method = "initDataTracker") public void initDataTracker(CallbackInfo info) { - ((BoatEntity) (Object) this).getDataTracker().startTracking(BoatUtil.HAS_CHEST, ChestMode.NONE.name()); + ((BoatEntity) (Object) this).getDataTracker().startTracking(CHEST_MODE, BoatChestMode.NONE.getID()); } @Inject(at = @At("HEAD"), method = "canAddPassenger", cancellable = true) public void canAddPassenger(Entity passenger, CallbackInfoReturnable info) { - if (((BoatEntity) (Object) this).getPassengerList().size() > 0 && getChestMode() != ChestMode.NONE) { + if (((BoatEntity) (Object) this).getPassengerList().size() > 0 && getChestMode() != BoatChestMode.NONE) { info.setReturnValue(false); } } @Redirect(at = @At(value = "INVOKE", target = "Ljava/util/List;size()I"), method = "updatePassengerPosition", allow = 2, require = 2) public int updatePassengerPosition(List list) { - if (getChestMode() != ChestMode.NONE) { + if (getChestMode() != BoatChestMode.NONE) { return list.size() + 1; } else { return list.size(); @@ -57,25 +59,28 @@ public class MixinBoatEntity implements BoatUtil { @Inject(at = @At("HEAD"), method = "interact", cancellable = true) public void interact(PlayerEntity player, Hand hand, CallbackInfoReturnable info) { - ChestMode hasChest = getChestMode(); - ItemStack stack = player.getStackInHand(hand); - ChestMode newMode = ChestMode.valueOF(stack.getItem()); - if (newMode != ChestMode.NONE && hasChest == ChestMode.NONE) { + BoatChestMode mode = getChestMode(); + ItemStack itemStack = player.getStackInHand(hand); + + BoatChestMode newMode = BoatChestMode.valueOf(Block.getBlockFromItem(itemStack.getItem())); + + if (newMode != BoatChestMode.NONE && mode == BoatChestMode.NONE) { List passengers = ((BoatEntity) (Object) this).getPassengerList(); for (int i = 1; i < passengers.size(); i++) { passengers.get(i).stopRiding(); } if (!player.getEntityWorld().isClient()) { - if (newMode.hasItems()) { - Twine.CHEST_BOAT_CRITERION.trigger((ServerPlayerEntity) player); - } else if (newMode == ChestMode.ENDER_CHEST) { - Twine.ENDER_CHEST_BOAT_CRITERION.trigger((ServerPlayerEntity) player); + ItemStack newStack; + if (!player.isCreative()) { + newStack = itemStack.split(1); + } else { + newStack = itemStack.copy(); + newStack.setCount(1); } - setChestMode(newMode); - updateInventory(); - } - if (!player.isCreative()) { - stack.decrement(1); + + setChestItem(newStack); + + newMode.interact((ServerPlayerEntity) player); } info.setReturnValue(ActionResult.SUCCESS); } else if (player.isSneaking()) { @@ -87,17 +92,24 @@ public class MixinBoatEntity implements BoatUtil { } @Unique - private void setChestMode(ChestMode mode) { - ((BoatEntity) (Object) this).getDataTracker().set(BoatUtil.HAS_CHEST, mode.name()); + private void updateChestMode() { + BoatChestMode mode = BoatChestMode.valueOf(Block.getBlockFromItem(stack.getItem())); + + ((BoatEntity) (Object) this).getDataTracker().set(CHEST_MODE, mode.getID()); } @Unique private void dropItemsOnDestroy(boolean isCreative) { if (((BoatEntity) (Object) this).getEntityWorld().getGameRules().getBoolean(GameRules.DO_ENTITY_DROPS)) { - if (!isCreative) { - ((BoatEntity) (Object) this).dropStack(new ItemStack(getChestMode().getItem())); + BoatChestMode mode = getChestMode(); + if (mode.containsItems()) { + ((BoatEntity) (Object) this).dropStack(stack.copy()); + } else { + if (!isCreative) { + ((BoatEntity) (Object) this).dropStack(getStackWithoutItems()); + } + dropItems(); } - dropItems(); } } @@ -116,14 +128,15 @@ public class MixinBoatEntity implements BoatUtil { @Unique private void updateInventory() { - items = getChestMode().hasItems() ? new BoatInventory((BoatEntity) (Object) this, 27) : null; + BoatChestMode mode = getChestMode(); + items = mode.hasItems() ? new BoatInventory((BoatEntity) (Object) this, mode.getSize(), stack, mode.getOpenSound(), mode.getCloseSound()) : null; } @Unique private void dropItems() { if (items != null) { for (int i = 0; i < items.size(); ++i) { - ItemStack itemStack = items.getStack(i); + ItemStack itemStack = items.getStack(i).copy(); if (!itemStack.isEmpty() && !EnchantmentHelper.hasVanishingCurse(itemStack)) { ((BoatEntity) (Object) this).dropStack(itemStack); } @@ -131,57 +144,46 @@ public class MixinBoatEntity implements BoatUtil { } } + private void setChestItem(ItemStack newStack) { + stack = newStack; + + updateChestMode(); + updateInventory(); + } + + @Unique + private ItemStack stack; + + @Unique + private ItemStack getStackWithoutItems() { + ItemStack newStack = stack.copy(); + CompoundTag tag = newStack.getOrCreateTag(); + + CompoundTag entityTag = tag.getCompound("BlockEntityTag"); + entityTag.remove("Items"); + if (entityTag.getSize() < 1) { + tag.remove("BlockEntityTag"); + } + + return newStack; + } + @Inject(at = @At("RETURN"), method = "writeCustomDataToTag") public void writeCustomDataToTag(CompoundTag tag, CallbackInfo info) { - ChestMode hasChest = getChestMode(); - tag.putString("HasChest", hasChest.name()); - if (hasChest.hasItems()) { - ListTag listTag = new ListTag(); - - for (int i = 2; i < this.items.size(); ++i) { - ItemStack itemStack = this.items.getStack(i); - if (!itemStack.isEmpty()) { - CompoundTag compoundTag = new CompoundTag(); - compoundTag.putByte("Slot", (byte) i); - itemStack.toTag(compoundTag); - listTag.add(compoundTag); - } - } - - tag.put("Items", listTag); - } + tag.put("ChestItem", stack.toTag(new CompoundTag())); } @Inject(at = @At("RETURN"), method = "readCustomDataFromTag") public void readCustomDataFromTag(CompoundTag tag, CallbackInfo info) { - ChestMode hasChest; - try { - hasChest = ChestMode.valueOf(tag.getString("HasChest")); - } catch (IllegalArgumentException e) { - hasChest = ChestMode.NONE; - } - setChestMode(hasChest); - updateInventory(); - if (hasChest.hasItems()) { - ListTag listTag = tag.getList("Items", 10); + CompoundTag itemTag = tag.getCompound("ChestItem"); + ItemStack newStack = ItemStack.fromTag(itemTag); - for (int i = 0; i < listTag.size(); ++i) { - CompoundTag compoundTag = listTag.getCompound(i); - int j = compoundTag.getByte("Slot") & 255; - if (j >= 2 && j < items.size()) { - items.setStack(j, ItemStack.fromTag(compoundTag)); - } - } - } + setChestItem(newStack); } @Override - public ChestMode getChestMode() { - try { - return ChestMode.valueOf(((BoatEntity) (Object) this).getDataTracker().get(BoatUtil.HAS_CHEST)); - } catch (IllegalArgumentException e) { - return ChestMode.NONE; - } + public BoatChestMode getChestMode() { + return BoatChestMode.valueOf(((BoatEntity) (Object) this).getDataTracker().get(CHEST_MODE)); } @Override @@ -191,10 +193,7 @@ public class MixinBoatEntity implements BoatUtil { @Override public void openInventory(PlayerEntity player) { - if (getChestMode().hasItems()) { - player.openHandledScreen(new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> GenericContainerScreenHandler.createGeneric9x3(i, playerInventory, getChestInventory()), ((BoatEntity) (Object) this).getDisplayName())); - } else if (getChestMode() == ChestMode.ENDER_CHEST) { - player.openHandledScreen(new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> GenericContainerScreenHandler.createGeneric9x3(i, playerInventory, new EnderChestInventoryWrapper((BoatEntity) (Object) this, player.getEnderChestInventory())), EnderChestBlock.CONTAINER_NAME)); - } + BoatChestMode mode = getChestMode(); + player.openHandledScreen(new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> mode.getScreenHandlerFactory().createMenu(i, playerInventory, playerEntity, MixinBoatEntity.this), mode.getScreenHandlerName(stack))); } } diff --git a/src/main/java/com/thebrokenrail/twine/mixin/MixinBoatEntityRenderer.java b/src/main/java/com/thebrokenrail/twine/mixin/MixinBoatEntityRenderer.java index ecdf1b8..0898d98 100644 --- a/src/main/java/com/thebrokenrail/twine/mixin/MixinBoatEntityRenderer.java +++ b/src/main/java/com/thebrokenrail/twine/mixin/MixinBoatEntityRenderer.java @@ -3,7 +3,6 @@ package com.thebrokenrail.twine.mixin; import com.thebrokenrail.twine.util.BoatUtil; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.minecraft.block.Block; import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; @@ -23,7 +22,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public class MixinBoatEntityRenderer { @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/MatrixStack;pop()V"), method = "render", allow = 1) public void render(BoatEntity boatEntity, float f, float g, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo info) { - BlockState blockState = Block.getBlockFromItem(((BoatUtil) boatEntity).getChestMode().getItem()).getDefaultState(); + BlockState blockState = ((BoatUtil) boatEntity).getChestMode().getBlock().getDefaultState(); if (blockState.getRenderType() != BlockRenderType.INVISIBLE) { matrixStack.push(); matrixStack.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(180.0f)); diff --git a/src/main/java/com/thebrokenrail/twine/mixin/MixinClientPlayerInteractionManager.java b/src/main/java/com/thebrokenrail/twine/mixin/MixinClientPlayerInteractionManager.java index 3faa503..3d1627c 100644 --- a/src/main/java/com/thebrokenrail/twine/mixin/MixinClientPlayerInteractionManager.java +++ b/src/main/java/com/thebrokenrail/twine/mixin/MixinClientPlayerInteractionManager.java @@ -1,5 +1,6 @@ package com.thebrokenrail.twine.mixin; +import com.thebrokenrail.twine.util.BoatChestMode; import com.thebrokenrail.twine.util.BoatUtil; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -23,7 +24,7 @@ public class MixinClientPlayerInteractionManager { @Inject(at = @At("HEAD"), method = "hasRidingInventory", cancellable = true) public void hasRidingInventory(CallbackInfoReturnable info) { assert client.player != null; - if (client.player.hasVehicle() && client.player.getVehicle() instanceof BoatEntity && ((BoatUtil) client.player.getVehicle()).getChestMode() != BoatUtil.ChestMode.NONE) { + if (client.player.hasVehicle() && client.player.getVehicle() instanceof BoatEntity && ((BoatUtil) client.player.getVehicle()).getChestMode() != BoatChestMode.NONE) { info.setReturnValue(true); } } diff --git a/src/main/java/com/thebrokenrail/twine/mixin/MixinServerPlayNetworkHandler.java b/src/main/java/com/thebrokenrail/twine/mixin/MixinServerPlayNetworkHandler.java index 3c07f79..9782a6a 100644 --- a/src/main/java/com/thebrokenrail/twine/mixin/MixinServerPlayNetworkHandler.java +++ b/src/main/java/com/thebrokenrail/twine/mixin/MixinServerPlayNetworkHandler.java @@ -1,5 +1,6 @@ package com.thebrokenrail.twine.mixin; +import com.thebrokenrail.twine.util.BoatChestMode; import com.thebrokenrail.twine.util.BoatUtil; import net.minecraft.entity.vehicle.BoatEntity; import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket; @@ -18,7 +19,7 @@ public class MixinServerPlayNetworkHandler { @Inject(at = @At("TAIL"), method = "onClientCommand") public void onClientCommand(ClientCommandC2SPacket packet, CallbackInfo info) { - if (packet.getMode() == ClientCommandC2SPacket.Mode.OPEN_INVENTORY && player.hasVehicle() && player.getVehicle() instanceof BoatEntity && ((BoatUtil) player.getVehicle()).getChestMode() != BoatUtil.ChestMode.NONE) { + if (packet.getMode() == ClientCommandC2SPacket.Mode.OPEN_INVENTORY && player.hasVehicle() && player.getVehicle() instanceof BoatEntity && ((BoatUtil) player.getVehicle()).getChestMode() != BoatChestMode.NONE) { ((BoatUtil) player.getVehicle()).openInventory(player); } } diff --git a/src/main/java/com/thebrokenrail/twine/mixin/MixinSlot.java b/src/main/java/com/thebrokenrail/twine/mixin/MixinSlot.java index 98fceb6..647fc4f 100644 --- a/src/main/java/com/thebrokenrail/twine/mixin/MixinSlot.java +++ b/src/main/java/com/thebrokenrail/twine/mixin/MixinSlot.java @@ -1,6 +1,7 @@ package com.thebrokenrail.twine.mixin; import com.thebrokenrail.twine.item.BackpackItem; +import com.thebrokenrail.twine.util.BackpackInventory; import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; import net.minecraft.screen.slot.Slot; @@ -19,7 +20,7 @@ public class MixinSlot { @Inject(at = @At("HEAD"), method = "canInsert", cancellable = true) public void canInsert(ItemStack stack, CallbackInfoReturnable info) { - if (inventory instanceof BackpackItem.ItemInventory) { + if (inventory instanceof BackpackInventory) { if (stack.getItem() instanceof BackpackItem) { info.setReturnValue(false); return; diff --git a/src/main/java/com/thebrokenrail/twine/util/BackpackInventory.java b/src/main/java/com/thebrokenrail/twine/util/BackpackInventory.java new file mode 100644 index 0000000..aee63ab --- /dev/null +++ b/src/main/java/com/thebrokenrail/twine/util/BackpackInventory.java @@ -0,0 +1,18 @@ +package com.thebrokenrail.twine.util; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.Hand; + +public class BackpackInventory extends ItemInventory { + private final Hand hand; + + public BackpackInventory(int size, PlayerEntity player, Hand hand) { + super(size, player.getStackInHand(hand)); + this.hand = hand; + } + + @Override + public boolean canPlayerUse(PlayerEntity player) { + return player.getStackInHand(hand) == getHolder(); + } +} diff --git a/src/main/java/com/thebrokenrail/twine/util/BackpackScreenHandler.java b/src/main/java/com/thebrokenrail/twine/util/BackpackScreenHandler.java index dcc2728..7ed7759 100644 --- a/src/main/java/com/thebrokenrail/twine/util/BackpackScreenHandler.java +++ b/src/main/java/com/thebrokenrail/twine/util/BackpackScreenHandler.java @@ -1,7 +1,6 @@ package com.thebrokenrail.twine.util; import com.thebrokenrail.twine.Twine; -import com.thebrokenrail.twine.item.BackpackItem; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.Inventory; import net.minecraft.network.PacketByteBuf; @@ -15,7 +14,7 @@ public class BackpackScreenHandler extends GenericContainerScreenHandler { } public BackpackScreenHandler(int i, PlayerInventory playerInventory, boolean large, Hand hand) { - this(Twine.BACKPACK_SCREEN_TYPE, i, playerInventory, new BackpackItem.ItemInventory(9 * (large ? 6 : 3), playerInventory.player, hand)); + this(Twine.BACKPACK_SCREEN_TYPE, i, playerInventory, new BackpackInventory(9 * (large ? 6 : 3), playerInventory.player, hand)); } public BackpackScreenHandler(ScreenHandlerType type, int syncId, PlayerInventory playerInventory, Inventory inventory) { diff --git a/src/main/java/com/thebrokenrail/twine/util/BoatChestMode.java b/src/main/java/com/thebrokenrail/twine/util/BoatChestMode.java new file mode 100644 index 0000000..3995f43 --- /dev/null +++ b/src/main/java/com/thebrokenrail/twine/util/BoatChestMode.java @@ -0,0 +1,143 @@ +package com.thebrokenrail.twine.util; + +import com.google.common.collect.Lists; +import com.thebrokenrail.twine.Twine; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.EnderChestBlock; +import net.minecraft.block.ShulkerBoxBlock; +import net.minecraft.block.entity.ChestBlockEntity; +import net.minecraft.block.entity.ShulkerBoxBlockEntity; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.ShulkerBoxScreenHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.sound.SoundEvent; +import net.minecraft.sound.SoundEvents; +import net.minecraft.text.Text; +import net.minecraft.util.DyeColor; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +public class BoatChestMode { + public interface BoatScreenHandlerFactory { + ScreenHandler createMenu(int syncId, PlayerInventory inv, PlayerEntity player, BoatUtil boat); + } + + private static final ArrayList values = new ArrayList<>(); + + public static final BoatChestMode NONE = new BoatChestMode(Blocks.AIR, null, null, null); + + static { + BoatScreenHandlerFactory chestScreenHandler = (i, playerInventory, playerEntity, boat) -> GenericContainerScreenHandler.createGeneric9x3(i, playerInventory, boat.getChestInventory()); + ChestBlockEntity chest = new ChestBlockEntity(); + Function chestScreenHandlerName = stack -> stack.hasCustomName() ? stack.getName() : chest.getName(); + + new BoatChestMode(Blocks.ENDER_CHEST, (i, playerInventory, playerEntity, boat) -> GenericContainerScreenHandler.createGeneric9x3(i, playerInventory, new EnderChestInventoryWrapper((Entity) boat, playerEntity.getEnderChestInventory())), stack -> EnderChestBlock.CONTAINER_NAME, player -> Twine.ENDER_CHEST_BOAT_CRITERION.trigger(player)); + new BoatChestMode(Blocks.CHEST, true, false, 27, chestScreenHandler, chestScreenHandlerName, player -> Twine.CHEST_BOAT_CRITERION.trigger(player), SoundEvents.BLOCK_CHEST_OPEN, SoundEvents.BLOCK_CHEST_CLOSE); + new BoatChestMode(Blocks.TRAPPED_CHEST, true, false, 27, chestScreenHandler, chestScreenHandlerName, player -> Twine.CHEST_BOAT_CRITERION.trigger(player), SoundEvents.BLOCK_CHEST_OPEN, SoundEvents.BLOCK_CHEST_CLOSE); + + ShulkerBoxBlockEntity shulker = new ShulkerBoxBlockEntity(); + List colors = Lists.asList(null, DyeColor.values()); + for (DyeColor value : colors) { + new BoatChestMode(ShulkerBoxBlock.get(value), true, true, 27, (i, playerInventory, playerEntity, boat) -> new ShulkerBoxScreenHandler(i, playerInventory, boat.getChestInventory()), stack -> stack.hasCustomName() ? stack.getName() : shulker.getName(), player -> Twine.SHULKER_BOX_BOAT_CRITERION.trigger(player), SoundEvents.BLOCK_SHULKER_BOX_OPEN, SoundEvents.BLOCK_SHULKER_BOX_CLOSE); + } + } + + private final Block block; + private final boolean hasItems; + private final boolean containsItems; + + private final int size; + + private final BoatScreenHandlerFactory screenHandlerFactory; + private final Function screenHandlerNameFactory; + + private final Consumer onInteract; + + private final int id; + + private final SoundEvent openSound; + private final SoundEvent closeSound; + + private BoatChestMode(Block block, boolean hasItems, boolean containsItems, int size, BoatScreenHandlerFactory screenHandlerFactory, Function screenHandlerNameFactory, Consumer onInteract, SoundEvent openSound, SoundEvent closeSound) { + this.screenHandlerFactory = screenHandlerFactory; + this.size = size; + this.screenHandlerNameFactory = screenHandlerNameFactory; + this.onInteract = onInteract; + this.openSound = openSound; + this.closeSound = closeSound; + this.id = values.size(); + this.block = block; + this.hasItems = hasItems; + this.containsItems = containsItems; + values.add(this); + } + + private BoatChestMode(Block block, BoatScreenHandlerFactory screenHandlerFactory, Function screenHandlerNameFactory, Consumer onInteract) { + this(block,false, false, 0, screenHandlerFactory, screenHandlerNameFactory, onInteract, null, null); + } + + public int getSize() { + return size; + } + + public BoatScreenHandlerFactory getScreenHandlerFactory() { + return screenHandlerFactory; + } + + public Text getScreenHandlerName(ItemStack stack) { + return screenHandlerNameFactory.apply(stack); + } + + public void interact(ServerPlayerEntity player) { + if (onInteract != null) { + onInteract.accept(player); + } + } + + public SoundEvent getOpenSound() { + return openSound; + } + + public SoundEvent getCloseSound() { + return closeSound; + } + + public Block getBlock() { + return block; + } + + public boolean hasItems() { + return hasItems; + } + + public boolean containsItems() { + return containsItems; + } + + public int getID() { + return id; + } + + public static BoatChestMode valueOf(Block block) { + for (BoatChestMode mode : values) { + if (mode.getBlock() == block) { + return mode; + } + } + return NONE; + } + + public static BoatChestMode valueOf(int index) { + BoatChestMode mode = values.get(index); + return mode != null ? mode : NONE; + } +} diff --git a/src/main/java/com/thebrokenrail/twine/util/BoatInventory.java b/src/main/java/com/thebrokenrail/twine/util/BoatInventory.java index 58e21fd..2aad26a 100644 --- a/src/main/java/com/thebrokenrail/twine/util/BoatInventory.java +++ b/src/main/java/com/thebrokenrail/twine/util/BoatInventory.java @@ -2,29 +2,34 @@ package com.thebrokenrail.twine.util; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.inventory.SimpleInventory; -import net.minecraft.sound.SoundEvents; +import net.minecraft.item.ItemStack; +import net.minecraft.sound.SoundEvent; -public class BoatInventory extends SimpleInventory { +public class BoatInventory extends ItemInventory { private final Entity entity; - public BoatInventory(Entity entity, int size) { - super(size); + private final SoundEvent openSound; + private final SoundEvent closeSound; + + public BoatInventory(Entity entity, int size, ItemStack stack, SoundEvent openSound, SoundEvent closeSound) { + super(size, stack); this.entity = entity; + this.openSound = openSound; + this.closeSound = closeSound; } @Override public boolean canPlayerUse(PlayerEntity player) { - return BoatUtil.canReachEntity(player, entity) && entity.isAlive() && ((BoatUtil) entity).getChestMode() == BoatUtil.ChestMode.CHEST && super.canPlayerUse(player); + return BoatUtil.canReachEntity(player, entity) && entity.isAlive() && ((BoatUtil) entity).getChestMode().hasItems(); } @Override public void onOpen(PlayerEntity player) { - BoatUtil.playSound(entity, SoundEvents.BLOCK_CHEST_OPEN); + BoatUtil.playSound(entity, openSound); } @Override public void onClose(PlayerEntity player) { - BoatUtil.playSound(entity, SoundEvents.BLOCK_CHEST_CLOSE); + BoatUtil.playSound(entity, closeSound); } } \ No newline at end of file diff --git a/src/main/java/com/thebrokenrail/twine/util/BoatUtil.java b/src/main/java/com/thebrokenrail/twine/util/BoatUtil.java index 81399a9..573dcf5 100644 --- a/src/main/java/com/thebrokenrail/twine/util/BoatUtil.java +++ b/src/main/java/com/thebrokenrail/twine/util/BoatUtil.java @@ -1,22 +1,14 @@ package com.thebrokenrail.twine.util; import net.minecraft.entity.Entity; -import net.minecraft.entity.data.DataTracker; -import net.minecraft.entity.data.TrackedData; -import net.minecraft.entity.data.TrackedDataHandlerRegistry; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.vehicle.BoatEntity; import net.minecraft.inventory.Inventory; -import net.minecraft.item.Item; -import net.minecraft.item.Items; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvent; import net.minecraft.util.math.Vec3d; public interface BoatUtil { - TrackedData HAS_CHEST = DataTracker.registerData(BoatEntity.class, TrackedDataHandlerRegistry.STRING); - - ChestMode getChestMode(); + BoatChestMode getChestMode(); Inventory getChestInventory(); @@ -26,38 +18,6 @@ public interface BoatUtil { return player.squaredDistanceTo(entity.getPos().getX(), entity.getPos().getY(), entity.getPos().getZ()) <= 64d; } - enum ChestMode { - ENDER_CHEST(Items.ENDER_CHEST, false), - CHEST(Items.CHEST, true), - TRAPPED_CHEST(Items.TRAPPED_CHEST, true), - NONE(Items.AIR, false); - - private final Item item; - private final boolean hasItems; - - ChestMode(Item item, boolean hasItems) { - this.item = item; - this.hasItems = hasItems; - } - - public Item getItem() { - return item; - } - - public boolean hasItems() { - return hasItems; - } - - public static ChestMode valueOF(Item item) { - for (ChestMode mode : values()) { - if (mode.getItem() == item) { - return mode; - } - } - return NONE; - } - } - static void playSound(Entity vehicle, SoundEvent sound) { Vec3d pos = vehicle.getPos(); vehicle.getEntityWorld().playSound(null, pos.getX(), pos.getY(), pos.getZ(), sound, SoundCategory.BLOCKS, 0.5F, vehicle.getEntityWorld().random.nextFloat() * 0.1F + 0.9F); diff --git a/src/main/java/com/thebrokenrail/twine/util/EnderChestInventoryWrapper.java b/src/main/java/com/thebrokenrail/twine/util/EnderChestInventoryWrapper.java index 5aac8f0..3fc9234 100644 --- a/src/main/java/com/thebrokenrail/twine/util/EnderChestInventoryWrapper.java +++ b/src/main/java/com/thebrokenrail/twine/util/EnderChestInventoryWrapper.java @@ -1,5 +1,6 @@ package com.thebrokenrail.twine.util; +import net.minecraft.block.Blocks; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.EnderChestInventory; @@ -53,7 +54,7 @@ public class EnderChestInventoryWrapper implements Inventory { @Override public boolean canPlayerUse(PlayerEntity player) { - return BoatUtil.canReachEntity(player, entity) && entity.isAlive() && ((BoatUtil) entity).getChestMode() == BoatUtil.ChestMode.ENDER_CHEST; + return BoatUtil.canReachEntity(player, entity) && entity.isAlive() && ((BoatUtil) entity).getChestMode().getBlock() == Blocks.ENDER_CHEST; } @Override diff --git a/src/main/java/com/thebrokenrail/twine/util/ItemInventory.java b/src/main/java/com/thebrokenrail/twine/util/ItemInventory.java new file mode 100644 index 0000000..7048aea --- /dev/null +++ b/src/main/java/com/thebrokenrail/twine/util/ItemInventory.java @@ -0,0 +1,84 @@ +package com.thebrokenrail.twine.util; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventories; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.collection.DefaultedList; + +public abstract class ItemInventory implements Inventory { + private final int size; + private final ItemStack stack; + private final DefaultedList inv; + + public ItemInventory(int size, ItemStack stack) { + this.size = size; + this.stack = stack; + inv = load(); + } + + protected ItemStack getHolder() { + return stack; + } + + private DefaultedList load() { + DefaultedList list = DefaultedList.ofSize(size, ItemStack.EMPTY); + Inventories.fromTag(stack.getOrCreateTag().getCompound("BlockEntityTag"), list); + return list; + } + + private void save(DefaultedList list) { + Inventories.toTag(stack.getOrCreateTag().getCompound("BlockEntityTag"), list); + } + + @Override + public int size() { + return size; + } + + @Override + public boolean isEmpty() { + for (ItemStack item : inv) { + if (item != ItemStack.EMPTY) { + return false; + } + } + return true; + } + + @Override + public ItemStack getStack(int slot) { + return inv.get(slot); + } + + @Override + public ItemStack removeStack(int slot, int amount) { + return inv.get(slot).split(amount); + } + + @Override + public ItemStack removeStack(int slot) { + ItemStack item = inv.get(slot); + return item.split(item.getCount()); + } + + @Override + public void setStack(int slot, ItemStack stack) { + inv.set(slot, stack); + } + + @Override + public void markDirty() { + save(inv); + } + + @Override + public abstract boolean canPlayerUse(PlayerEntity player); + + @Override + public void clear() { + for (int i = 0; i < size; i++) { + inv.set(i, ItemStack.EMPTY); + } + } +} diff --git a/src/main/resources/assets/twine/lang/en_us.json b/src/main/resources/assets/twine/lang/en_us.json index 058e02f..7f7ae29 100644 --- a/src/main/resources/assets/twine/lang/en_us.json +++ b/src/main/resources/assets/twine/lang/en_us.json @@ -17,6 +17,9 @@ "advancements.twine.ender_chest_boat.title": "Why not?", "advancements.twine.ender_chest_boat.description": "Put an Ender Chest in a Boat", + "advancements.twine.shulker_box_boat.title": "Why, just why?", + "advancements.twine.shulker_box_boat.description": "Put a Shulker Box in a Boat", + "advancements.twine.small_backpack.title": "On the go!", "advancements.twine.small_backpack.description": "Construct a Small Backpack with a Crafting Table", diff --git a/src/main/resources/data/twine/advancements/shulker_box_boat.json b/src/main/resources/data/twine/advancements/shulker_box_boat.json new file mode 100644 index 0000000..85abf88 --- /dev/null +++ b/src/main/resources/data/twine/advancements/shulker_box_boat.json @@ -0,0 +1,19 @@ +{ + "display": { + "icon": { + "item": "minecraft:oak_boat" + }, + "title": { + "translate": "advancements.twine.shulker_box_boat.title" + }, + "description": { + "translate": "advancements.twine.shulker_box_boat.description" + } + }, + "parent": "twine:chest_boat", + "criteria": { + "chest_boat": { + "trigger": "twine:shulker_box_boat" + } + } +} \ No newline at end of file