This commit is contained in:
parent
8164f8f8b9
commit
42b73413bf
@ -30,7 +30,7 @@ Glowing Obsidian heals monsters and hurts everything else. It naturally generate
|
||||
</table>
|
||||
|
||||
## 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.
|
||||
|
@ -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() {
|
||||
|
@ -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<AbstractCriterionConditions> {
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<ItemStack> inv;
|
||||
|
||||
public ItemInventory(int size, PlayerEntity player, Hand hand) {
|
||||
this.size = size;
|
||||
this.stack = player.getStackInHand(hand);
|
||||
this.hand = hand;
|
||||
inv = getInv();
|
||||
}
|
||||
|
||||
private DefaultedList<ItemStack> getInv() {
|
||||
DefaultedList<ItemStack> list = DefaultedList.ofSize(size, ItemStack.EMPTY);
|
||||
Inventories.fromTag(stack.getOrCreateTag(), list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private void setInv(DefaultedList<ItemStack> 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) {
|
||||
|
@ -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<Integer> 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<Boolean> 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<Entity> 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<ActionResult> 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<Entity> 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);
|
||||
}
|
||||
setChestMode(newMode);
|
||||
updateInventory();
|
||||
}
|
||||
ItemStack newStack;
|
||||
if (!player.isCreative()) {
|
||||
stack.decrement(1);
|
||||
newStack = itemStack.split(1);
|
||||
} else {
|
||||
newStack = itemStack.copy();
|
||||
newStack.setCount(1);
|
||||
}
|
||||
|
||||
setChestItem(newStack);
|
||||
|
||||
newMode.interact((ServerPlayerEntity) player);
|
||||
}
|
||||
info.setReturnValue(ActionResult.SUCCESS);
|
||||
} else if (player.isSneaking()) {
|
||||
@ -87,19 +92,26 @@ 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)) {
|
||||
BoatChestMode mode = getChestMode();
|
||||
if (mode.containsItems()) {
|
||||
((BoatEntity) (Object) this).dropStack(stack.copy());
|
||||
} else {
|
||||
if (!isCreative) {
|
||||
((BoatEntity) (Object) this).dropStack(new ItemStack(getChestMode().getItem()));
|
||||
((BoatEntity) (Object) this).dropStack(getStackWithoutItems());
|
||||
}
|
||||
dropItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/vehicle/BoatEntity;remove()V"), method = "damage")
|
||||
public void damage(DamageSource source, float amount, CallbackInfoReturnable<Boolean> info) {
|
||||
@ -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)));
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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<Boolean> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<Boolean> info) {
|
||||
if (inventory instanceof BackpackItem.ItemInventory) {
|
||||
if (inventory instanceof BackpackInventory) {
|
||||
if (stack.getItem() instanceof BackpackItem) {
|
||||
info.setReturnValue(false);
|
||||
return;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
143
src/main/java/com/thebrokenrail/twine/util/BoatChestMode.java
Normal file
143
src/main/java/com/thebrokenrail/twine/util/BoatChestMode.java
Normal file
@ -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<BoatChestMode> 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<ItemStack, Text> 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<DyeColor> 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<ItemStack, Text> screenHandlerNameFactory;
|
||||
|
||||
private final Consumer<ServerPlayerEntity> 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<ItemStack, Text> screenHandlerNameFactory, Consumer<ServerPlayerEntity> 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<ItemStack, Text> screenHandlerNameFactory, Consumer<ServerPlayerEntity> 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<String> 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);
|
||||
|
@ -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
|
||||
|
@ -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<ItemStack> inv;
|
||||
|
||||
public ItemInventory(int size, ItemStack stack) {
|
||||
this.size = size;
|
||||
this.stack = stack;
|
||||
inv = load();
|
||||
}
|
||||
|
||||
protected ItemStack getHolder() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
private DefaultedList<ItemStack> load() {
|
||||
DefaultedList<ItemStack> list = DefaultedList.ofSize(size, ItemStack.EMPTY);
|
||||
Inventories.fromTag(stack.getOrCreateTag().getCompound("BlockEntityTag"), list);
|
||||
return list;
|
||||
}
|
||||
|
||||
private void save(DefaultedList<ItemStack> 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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",
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user