From 19f67c23c6f15a0138c484304fa270a287e62b79 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sun, 21 Apr 2024 18:51:30 +0100 Subject: [PATCH] Incorporate the player's luck and allow it to turn back if left unattended --- .../com/minelittlepony/unicopia/UTags.java | 1 + .../render/entity/MimicEntityRenderer.java | 5 + .../providers/tag/UBlockTagProvider.java | 4 + .../unicopia/entity/mob/MimicEntity.java | 100 ++++++++++++------ .../MixinLootableContainerBlockEntity.java | 58 +++++++--- 5 files changed, 116 insertions(+), 52 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/UTags.java b/src/main/java/com/minelittlepony/unicopia/UTags.java index 83e9cb37..0dc7cec4 100644 --- a/src/main/java/com/minelittlepony/unicopia/UTags.java +++ b/src/main/java/com/minelittlepony/unicopia/UTags.java @@ -104,6 +104,7 @@ public interface UTags { TagKey KICKS_UP_DUST = block("kicks_up_dust"); TagKey POLEARM_MINEABLE = block("mineable/polearm"); + TagKey MIMIC_CHESTS = block("mimic_chests"); TagKey BUTTERFLIES_SPAWNABLE_ON = block("butterflies_spawn_on"); diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/MimicEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/MimicEntityRenderer.java index 0fc1d1ab..1195e6c1 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/MimicEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/MimicEntityRenderer.java @@ -36,6 +36,11 @@ public class MimicEntityRenderer extends MobEntityRenderer observingPlayers = new HashSet<>(); - @SuppressWarnings("deprecation") - public static TypedActionResult spawnFromChest(World world, BlockPos pos, PlayerEntity player) { - if (world.getBlockState(pos).isOf(Blocks.CHEST) && world.getBlockEntity(pos) instanceof ChestBlockEntity be) { - int difficulty = world.getDifficulty().ordinal() - 1; - if (difficulty < 0) { - return TypedActionResult.pass(null); - } - float spawnChance = (difficulty / 3F) * 0.25F; - float roll = world.random.nextFloat(); - - if (roll >= spawnChance) { - return TypedActionResult.pass(null); - } - - BlockState state = be.getCachedState(); - if (state.getOrEmpty(ChestBlock.CHEST_TYPE).orElse(ChestType.SINGLE) != ChestType.SINGLE) { - return TypedActionResult.fail(null); - } - - world.removeBlockEntity(pos); - world.setBlockState(pos, Blocks.AIR.getDefaultState()); - MimicEntity mimic = UEntities.MIMIC.create(world); - Direction facing = state.getOrEmpty(ChestBlock.FACING).orElse(null); - float yaw = facing.asRotation(); - be.setCachedState(be.getCachedState().getBlock().getDefaultState()); - mimic.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, yaw, 0); - mimic.setHeadYaw(yaw); - mimic.setBodyYaw(yaw); - mimic.setYaw(yaw); - mimic.setChest(be); - world.spawnEntity(mimic); - return TypedActionResult.success(mimic); + public static boolean shouldConvert(World world, BlockPos pos, PlayerEntity player, Identifier lootTable) { + if (!shouldGenerateMimic(lootTable) + || !world.getBlockState(pos).isIn(UTags.Blocks.MIMIC_CHESTS) + || !(world.getBlockEntity(pos) instanceof ChestBlockEntity be) + || be.getCachedState().getOrEmpty(ChestBlock.CHEST_TYPE).orElse(ChestType.SINGLE) != ChestType.SINGLE) { + return false; } - return TypedActionResult.fail(null); + + int difficulty = world.getDifficulty().ordinal() - 1; + float threshold = 0.35F * ((EnchantmentUtil.getLuck(0, player) / 20F) + 0.5F); + return difficulty > 0 && world.random.nextFloat() < (difficulty / 3F) * threshold; + } + + @SuppressWarnings("deprecation") + @Nullable + public static MimicEntity spawnFromChest(World world, BlockPos pos) { + if (!(world.getBlockEntity(pos) instanceof ChestBlockEntity be)) { + return null; + } + world.removeBlockEntity(pos); + world.setBlockState(pos, Blocks.AIR.getDefaultState()); + MimicEntity mimic = UEntities.MIMIC.create(world); + BlockState state = be.getCachedState(); + Direction facing = state.getOrEmpty(ChestBlock.FACING).orElse(null); + float yaw = facing.asRotation(); + be.setCachedState(be.getCachedState().getBlock().getDefaultState()); + mimic.updatePositionAndAngles(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, yaw, 0); + mimic.setHeadYaw(yaw); + mimic.setBodyYaw(yaw); + mimic.setYaw(yaw); + mimic.setChest(be); + world.spawnEntity(mimic); + return mimic; + } + + public static boolean shouldGenerateMimic(@Nullable Identifier lootTable) { + return lootTable != null + && lootTable.getPath().indexOf("village") == -1 + && lootTable.getPath().indexOf("bastion") == -1 + && lootTable.getPath().indexOf("underwater") == -1 + && lootTable.getPath().indexOf("shipwreck") == -1; } MimicEntity(EntityType type, World world) { @@ -141,6 +150,7 @@ public class MimicEntity extends PathAwareEntity { public void setChest(ChestBlockEntity chestData) { this.chestData = chestData; + ((MimicGeneratable)chestData).setAllowMimics(false); chestData.setWorld(getWorld()); if (!getWorld().isClient) { dataTracker.set(CHEST_DATA, writeChestData(chestData)); @@ -194,8 +204,21 @@ public class MimicEntity extends PathAwareEntity { setHeadYaw(MathHelper.floor(getHeadYaw() / 90) * 90); if (getHealth() < getMaxHealth() && getWorld().random.nextInt(20) == 0) { heal(1); + } else if (age % 150 == 0 && chestData != null && !isMouthOpen()) { + if (getWorld().getClosestPlayer(this, 15) == null) { + getWorld().setBlockState(getBlockPos(), chestData.getCachedState().withIfExists(ChestBlock.FACING, getHorizontalFacing())); + if (getWorld().getBlockEntity(getBlockPos()) instanceof ChestBlockEntity be) { + InventoryUtil.copyInto(chestData, be); + ((MimicGeneratable)be).setMimic(true); + discard(); + } + } } } + + if (!observingPlayers.isEmpty()) { + setMouthOpen(true); + } } if (chestData == null) { @@ -245,7 +268,7 @@ public class MimicEntity extends PathAwareEntity { @Override public void onOpen(PlayerEntity player) { observingPlayers.add(player); - setMouthOpen(true); + //setMouthOpen(true); } @Override @@ -311,6 +334,7 @@ public class MimicEntity extends PathAwareEntity { BlockState state = BlockState.CODEC.decode(NbtOps.INSTANCE, nbt.getCompound("state")).result().get().getFirst(); if (BlockEntity.createFromNbt(getBlockPos(), state, nbt.getCompound("data")) instanceof ChestBlockEntity data) { data.setWorld(getWorld()); + ((MimicGeneratable)data).setAllowMimics(false); return data; } return null; @@ -370,4 +394,10 @@ public class MimicEntity extends PathAwareEntity { } } } + + public interface MimicGeneratable { + void setAllowMimics(boolean allowMimics); + + void setMimic(boolean mimic); + } } diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLootableContainerBlockEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLootableContainerBlockEntity.java index 4cb375ca..dcf21efe 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinLootableContainerBlockEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinLootableContainerBlockEntity.java @@ -14,25 +14,49 @@ import net.minecraft.block.entity.LockableContainerBlockEntity; import net.minecraft.block.entity.LootableContainerBlockEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.nbt.NbtCompound; import net.minecraft.screen.ScreenHandler; -import net.minecraft.util.ActionResult; +import net.minecraft.util.Identifier; @Mixin(LootableContainerBlockEntity.class) -abstract class MixinLootableContainerBlockEntity extends LockableContainerBlockEntity { - private boolean generateMimic; +abstract class MixinLootableContainerBlockEntity extends LockableContainerBlockEntity implements MimicEntity.MimicGeneratable { + private Identifier mimicLootTable; + private boolean allowMimics = true; + private boolean isMimic; + + @Shadow + @Nullable + private Identifier lootTableId; MixinLootableContainerBlockEntity() { super(null, null, null); } - @Inject( - method = "checkLootInteraction", - at = @At( - value = "INVOKE", - target = "net/minecraft/loot/LootTable.supplyInventory(Lnet/minecraft/inventory/Inventory;Lnet/minecraft/loot/context/LootContextParameterSet;J)V", - shift = Shift.AFTER - )) + @Inject(method = "deserializeLootTable", at = @At("HEAD")) + private void deserializeMimic(NbtCompound nbt, CallbackInfoReturnable info) { + isMimic = nbt.getBoolean("mimic"); + } + + @Inject(method = "serializeLootTable", at = @At("HEAD")) + private void serializeMimic(NbtCompound nbt, CallbackInfoReturnable info) { + nbt.putBoolean("mimic", isMimic); + } + + @Override + public void setAllowMimics(boolean allowMimics) { + this.allowMimics = allowMimics; + this.isMimic &= allowMimics; + markDirty(); + } + + @Override + public void setMimic(boolean mimic) { + isMimic = mimic; + markDirty(); + } + + @Inject(method = "checkLootInteraction", at = @At("HEAD")) private void onCheckLootInteraction(@Nullable PlayerEntity player, CallbackInfo info) { - if (player != null) { - generateMimic = true; + if (player != null && allowMimics && lootTableId != null) { + mimicLootTable = lootTableId; } } @@ -44,12 +68,12 @@ abstract class MixinLootableContainerBlockEntity extends LockableContainerBlockE shift = Shift.AFTER ), cancellable = true) private void onCreateMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player, CallbackInfoReturnable info) { - if (generateMimic) { - generateMimic = false; - var mimic = MimicEntity.spawnFromChest(player.getWorld(), getPos(), player); - if (mimic.getResult() == ActionResult.SUCCESS) { - info.setReturnValue(mimic.getValue().createScreenHandler(syncId, playerInventory, player)); + if (player != null && (isMimic || (allowMimics && MimicEntity.shouldConvert(player.getWorld(), getPos(), player, mimicLootTable)))) { + var mimic = MimicEntity.spawnFromChest(player.getWorld(), getPos()); + if (mimic != null) { + info.setReturnValue(mimic.createScreenHandler(syncId, playerInventory, player)); } + mimicLootTable = null; } } }