package com.thebrokenrail.twine.mixin; import com.thebrokenrail.twine.util.boat.BoatChestMode; import com.thebrokenrail.twine.util.boat.BoatChestModes; import com.thebrokenrail.twine.util.boat.BoatUtil; import com.thebrokenrail.twine.util.boat.inventory.BoatInventory; import com.thebrokenrail.twine.util.boat.inventory.BoatInventoryWrapper; import net.minecraft.block.Block; import net.minecraft.block.BlockState; 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.screen.SimpleNamedScreenHandlerFactory; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.GameRules; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 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(CHEST_MODE, BoatChestModes.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() != BoatChestModes.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() != BoatChestModes.NONE) { return list.size() + 1; } else { return list.size(); } } @Inject(at = @At("HEAD"), method = "interact", cancellable = true) public void interact(PlayerEntity player, Hand hand, CallbackInfoReturnable info) { BoatChestMode mode = getChestMode(); ItemStack itemStack = player.getStackInHand(hand); BoatChestMode newMode = BoatChestMode.valueOf(Block.getBlockFromItem(itemStack.getItem())); if (newMode != BoatChestModes.NONE && mode == BoatChestModes.NONE) { List passengers = ((BoatEntity) (Object) this).getPassengerList(); for (int i = 1; i < passengers.size(); i++) { passengers.get(i).stopRiding(); } if (!player.getEntityWorld().isClient()) { ItemStack newStack; if (!player.isCreative()) { newStack = itemStack.split(1); } else { newStack = itemStack.copy(); newStack.setCount(1); } setChestItem(newStack); newMode.triggerAdvancement((ServerPlayerEntity) player); } info.setReturnValue(ActionResult.SUCCESS); } else if (player.isSneaking()) { if (!player.getEntityWorld().isClient()) { openInventory(player); } info.setReturnValue(ActionResult.SUCCESS); } } @Unique 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(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 info) { dropItemsOnDestroy(source.getAttacker() instanceof PlayerEntity && ((PlayerEntity) source.getAttacker()).isCreative()); } @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/vehicle/BoatEntity;remove()V"), method = "fall") public void fall(double heightDifference, boolean onGround, BlockState landedState, BlockPos landedPosition, CallbackInfo info) { dropItemsOnDestroy(false); } @Unique private BoatInventory items; @Unique private void updateInventory() { BoatChestMode mode = getChestMode(); items = mode.hasItems() ? new BoatInventory((BoatEntity) (Object) this, mode.getSize(), stack, mode) : null; } @Unique private void dropItems() { if (items != null) { for (int i = 0; i < items.size(); ++i) { ItemStack itemStack = items.getStack(i).copy(); if (!itemStack.isEmpty() && !EnchantmentHelper.hasVanishingCurse(itemStack)) { ((BoatEntity) (Object) this).dropStack(itemStack); } } } } 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) { tag.put("ChestItem", stack.toTag(new CompoundTag())); } @Inject(at = @At("RETURN"), method = "readCustomDataFromTag") public void readCustomDataFromTag(CompoundTag tag, CallbackInfo info) { CompoundTag itemTag = tag.getCompound("ChestItem"); ItemStack newStack = ItemStack.fromTag(itemTag); setChestItem(newStack); } @Override public BoatChestMode getChestMode() { return BoatChestMode.valueOf(((BoatEntity) (Object) this).getDataTracker().get(CHEST_MODE)); } @Override public Inventory getChestInventory() { return items; } @Override public void openInventory(PlayerEntity player) { BoatChestMode mode = getChestMode(); player.openHandledScreen(new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> mode.getScreenHandlerFactory().createMenu(i, playerInventory, playerEntity, MixinBoatEntity.this, inventory -> new BoatInventoryWrapper((Entity) (Object) this, inventory, mode)), mode.getScreenHandlerName(stack))); } }