diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java index 4c41421207..8397292b9d 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java @@ -4,20 +4,19 @@ import com.llamalad7.mixinextras.injector.WrapWithCondition; import com.llamalad7.mixinextras.sugar.Local; import de.hysky.skyblocker.skyblock.FishingHelper; -import de.hysky.skyblocker.skyblock.end.TheEnd; import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; import de.hysky.skyblocker.skyblock.end.BeaconHighlighter; +import de.hysky.skyblocker.skyblock.end.TheEnd; import de.hysky.skyblocker.skyblock.waypoint.MythologicalRitual; import de.hysky.skyblocker.utils.SlayerUtils; import de.hysky.skyblocker.utils.Utils; import net.minecraft.block.Blocks; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityStatuses; import net.minecraft.entity.ItemEntity; -import net.minecraft.entity.LivingEntity; import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.EntityStatusS2CPacket; import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; @@ -25,6 +24,7 @@ import net.minecraft.util.Identifier; import org.slf4j.Logger; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyVariable; @@ -32,6 +32,9 @@ @Mixin(ClientPlayNetworkHandler.class) public abstract class ClientPlayNetworkHandlerMixin { + @Shadow + private ClientWorld world; + @Inject(method = "onBlockUpdate", at = @At("RETURN")) private void skyblocker$onBlockUpdate(BlockUpdateS2CPacket packet, CallbackInfo ci) { if (Utils.isInTheEnd() && SlayerUtils.isInSlayer()) { @@ -42,9 +45,16 @@ public abstract class ClientPlayNetworkHandlerMixin { } } - @ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "STORE", ordinal = 0)) - private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity, @Local LivingEntity collector) { - DungeonManager.onItemPickup(itemEntity, collector, collector == MinecraftClient.getInstance().player); + @Inject(method = "method_37472", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V")) + private void skyblocker$onItemDestroy(int entityId, CallbackInfo ci) { + if (world.getEntityById(entityId) instanceof ItemEntity itemEntity) { + DungeonManager.onItemPickup(itemEntity); + } + } + + @ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V", ordinal = 0)) + private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity) { + DungeonManager.onItemPickup(itemEntity); return itemEntity; } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java index c1ab226dc1..d5d57e705f 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java @@ -515,10 +515,7 @@ private static Room newDebugRoom(String roomName, Room.Direction direction, Play */ @SuppressWarnings("JavadocReference") private static void update() { - if (!Utils.isInDungeons()) { - if (mapEntrancePos != null) { - reset(); - } + if (!Utils.isInDungeons() || isInBoss()) { return; } MinecraftClient client = MinecraftClient.getInstance(); @@ -613,7 +610,7 @@ private static void onChatMessage(Text text, boolean overlay) { String message = text.getString(); - if (overlay && isCurrentRoomMatched()) { + if (isCurrentRoomMatched()) { currentRoom.onChatMessage(message); } @@ -663,7 +660,7 @@ private static void onChatMessage(Text text, boolean overlay) { @SuppressWarnings("JavadocReference") private static ActionResult onUseBlock(World world, BlockHitResult hitResult) { if (isCurrentRoomMatched()) { - currentRoom.onUseBlock(world, hitResult); + currentRoom.onUseBlock(world, hitResult.getBlockPos()); } return ActionResult.PASS; } @@ -674,16 +671,10 @@ private static ActionResult onUseBlock(World world, BlockHitResult hitResult) { * If the collector is the player, {@link #currentRoom} is used as an optimization. */ @SuppressWarnings("JavadocReference") - public static void onItemPickup(ItemEntity itemEntity, LivingEntity collector, boolean isPlayer) { - if (isPlayer) { - if (isCurrentRoomMatched()) { - currentRoom.onItemPickup(itemEntity, collector); - } - } else { - Room room = getRoomAtPhysical(collector.getPos()); - if (isRoomMatched(room)) { - room.onItemPickup(itemEntity, collector); - } + public static void onItemPickup(ItemEntity itemEntity) { + Room room = getRoomAtPhysical(itemEntity.getPos()); + if (isRoomMatched(room)) { + room.onItemPickup(itemEntity); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java index a1bafc208d..5b20b48f3c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java @@ -29,12 +29,10 @@ import net.minecraft.client.world.ClientWorld; import net.minecraft.command.argument.EnumArgumentType; import net.minecraft.entity.ItemEntity; -import net.minecraft.entity.LivingEntity; import net.minecraft.entity.mob.AmbientEntity; import net.minecraft.registry.Registries; import net.minecraft.text.Text; import net.minecraft.util.StringIdentifiable; -import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; @@ -54,6 +52,7 @@ public class Room implements Tickable, Renderable { private static final Pattern SECRET_INDEX = Pattern.compile("^(\\d+)"); private static final Pattern SECRETS = Pattern.compile("ยง7(\\d{1,2})/(\\d{1,2}) Secrets"); + private static final String LOCKED_CHEST = "That chest is locked!"; private static final Vec3d DOOR_SIZE = new Vec3d(3, 4, 3); protected static final float[] RED_COLOR_COMPONENTS = {1, 0, 0}; protected static final float[] GREEN_COLOR_COMPONENTS = {0, 1, 0}; @@ -99,6 +98,8 @@ public class Room implements Tickable, Renderable { protected List tickables = new ArrayList<>(); protected List renderables = new ArrayList<>(); + private BlockPos lastChestSecret; + private long lastChestSecretTime; /** * Stores the next room in the dungeon. Currently only used if the next room is the fairy room. */ @@ -207,7 +208,7 @@ protected void addCustomWaypoint(CommandContext conte SecretWaypoint.Category category = SecretWaypoint.Category.CategoryArgumentType.getCategory(context, "category"); Text waypointName = context.getArgument("name", Text.class); addCustomWaypoint(secretIndex, category, waypointName, pos); - context.getSource().sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.customWaypointAdded", pos.getX(), pos.getY(), pos.getZ(), name, secretIndex, category, waypointName))); + context.getSource().sendFeedback(Constants.PREFIX.get().append(Text.stringifiedTranslatable("skyblocker.dungeons.secrets.customWaypointAdded", pos.getX(), pos.getY(), pos.getZ(), name, secretIndex, category, waypointName))); } /** @@ -241,7 +242,7 @@ private void addCustomWaypoint(SecretWaypoint relativeWaypoint) { protected void removeCustomWaypoint(CommandContext context, BlockPos pos) { SecretWaypoint waypoint = removeCustomWaypoint(pos); if (waypoint != null) { - context.getSource().sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.customWaypointRemoved", pos.getX(), pos.getY(), pos.getZ(), name, waypoint.secretIndex, waypoint.category, waypoint.name))); + context.getSource().sendFeedback(Constants.PREFIX.get().append(Text.stringifiedTranslatable("skyblocker.dungeons.secrets.customWaypointRemoved", pos.getX(), pos.getY(), pos.getZ(), name, waypoint.secretIndex, waypoint.category, waypoint.name))); } else { context.getSource().sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.customWaypointNotFound", pos.getX(), pos.getY(), pos.getZ(), name))); } @@ -549,11 +550,14 @@ public void render(WorldRenderContext context) { } /** - * Sets all secrets as found if {@link #isAllSecretsFound(String)}. + * Sets all secrets as found if {@link #isAllSecretsFound(String)} and sets {@link #lastChestSecret} as missing if message equals {@link #LOCKED_CHEST}. */ protected void onChatMessage(String message) { if (isAllSecretsFound(message)) { secretWaypoints.values().forEach(SecretWaypoint::setFound); + } else if (LOCKED_CHEST.equals(message) && lastChestSecretTime + 1000 > System.currentTimeMillis() && lastChestSecret != null) { + secretWaypoints.column(lastChestSecret).values().stream().filter(SecretWaypoint::needsInteraction).findAny() + .ifPresent(secretWaypoint -> markSecretsAndLogInfo(secretWaypoint, false, "[Skyblocker Dungeon Secrets] Detected locked chest interaction, setting secret #{} as missing", secretWaypoint.secretIndex)); } } @@ -572,58 +576,72 @@ protected static boolean isAllSecretsFound(String message) { } /** - * Marks the secret at the interaction position as found when the player interacts with a chest or a player head, - * if there is a secret at the interaction position. + * Marks the secret at the interaction position as found when the player interacts with a chest, player head, or lever + * if there is a secret at the interaction position and saves the position to {@link #lastChestSecret} if the block is a chest. * - * @param world the world to get the block from - * @param hitResult the block being interacted with - * @see #onSecretFound(SecretWaypoint, String, Object...) + * @param world the world to get the block from + * @param pos the position of the block being interacted with + * @see #markSecretsFoundAndLogInfo(SecretWaypoint, String, Object...) */ - protected void onUseBlock(World world, BlockHitResult hitResult) { - BlockState state = world.getBlockState(hitResult.getBlockPos()); - if (state.isOf(Blocks.CHEST) || state.isOf(Blocks.PLAYER_HEAD) || state.isOf(Blocks.PLAYER_WALL_HEAD)) { - secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::needsInteraction).findAny() - .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker Dungeon Secrets] Detected {} interaction, setting secret #{} as found", secretWaypoint.category, secretWaypoint.secretIndex)); + protected void onUseBlock(World world, BlockPos pos) { + BlockState state = world.getBlockState(pos); + if ((state.isOf(Blocks.CHEST) || state.isOf(Blocks.TRAPPED_CHEST)) && lastChestSecretTime + 1000 < System.currentTimeMillis() || state.isOf(Blocks.PLAYER_HEAD) || state.isOf(Blocks.PLAYER_WALL_HEAD)) { + secretWaypoints.column(pos).values().stream().filter(SecretWaypoint::needsInteraction).findAny() + .ifPresent(secretWaypoint -> markSecretsFoundAndLogInfo(secretWaypoint, "[Skyblocker Dungeon Secrets] Detected {} interaction, setting secret #{} as found", secretWaypoint.category, secretWaypoint.secretIndex)); + if (state.isOf(Blocks.CHEST) || state.isOf(Blocks.TRAPPED_CHEST)) { + lastChestSecret = pos; + lastChestSecretTime = System.currentTimeMillis(); + } } else if (state.isOf(Blocks.LEVER)) { - secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::isLever).forEach(SecretWaypoint::setFound); + secretWaypoints.column(pos).values().stream().filter(SecretWaypoint::isLever).forEach(SecretWaypoint::setFound); } } /** - * Marks the closest secret that requires item pickup no greater than 6 blocks away as found when the player picks up a secret item. + * Marks the closest secret that requires item pickup no greater than 6 blocks away as found when a secret item is removed from the world. * * @param itemEntity the item entity being picked up - * @param collector the collector of the item - * @see #onSecretFound(SecretWaypoint, String, Object...) + * @see #markSecretsFoundAndLogInfo(SecretWaypoint, String, Object...) */ - protected void onItemPickup(ItemEntity itemEntity, LivingEntity collector) { + protected void onItemPickup(ItemEntity itemEntity) { if (SecretWaypoint.SECRET_ITEMS.stream().noneMatch(itemEntity.getStack().getName().getString()::contains)) { return; } - secretWaypoints.values().stream().filter(SecretWaypoint::needsItemPickup).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(collector))).filter(SecretWaypoint.getRangePredicate(collector)) - .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker Dungeon Secrets] Detected {} picked up a {} from a {} secret, setting secret #{} as found", collector.getName().getString(), itemEntity.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex)); + secretWaypoints.values().stream().filter(SecretWaypoint::needsItemPickup).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(itemEntity))).filter(SecretWaypoint.getRangePredicate(itemEntity)) + .ifPresent(secretWaypoint -> markSecretsFoundAndLogInfo(secretWaypoint, "[Skyblocker Dungeon Secrets] Detected item {} removed from a {} secret, setting secret #{} as found", itemEntity.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex)); } /** * Marks the closest bat secret as found when a bat is killed. * * @param bat the bat being killed - * @see #onSecretFound(SecretWaypoint, String, Object...) + * @see #markSecretsFoundAndLogInfo(SecretWaypoint, String, Object...) */ protected void onBatRemoved(AmbientEntity bat) { secretWaypoints.values().stream().filter(SecretWaypoint::isBat).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(bat))) - .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker Dungeon Secrets] Detected {} killed for a {} secret, setting secret #{} as found", bat.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex)); + .ifPresent(secretWaypoint -> markSecretsFoundAndLogInfo(secretWaypoint, "[Skyblocker Dungeon Secrets] Detected {} killed for a {} secret, setting secret #{} as found", bat.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex)); } /** - * Marks all secret waypoints with the same index as the given {@link SecretWaypoint} as found. + * Marks all secret waypoints with the same index as the given {@link SecretWaypoint} as found and logs the given message. * * @param secretWaypoint the secret waypoint to read the index from. * @param msg the message to log * @param args the args for the {@link org.slf4j.Logger#info(String, Object...) Logger#info(String, Object...)} call */ - private void onSecretFound(SecretWaypoint secretWaypoint, String msg, Object... args) { - secretWaypoints.row(secretWaypoint.secretIndex).values().forEach(SecretWaypoint::setFound); + private void markSecretsFoundAndLogInfo(SecretWaypoint secretWaypoint, String msg, Object... args) { + markSecretsAndLogInfo(secretWaypoint, true, msg, args); + } + + /** + * Marks all secret waypoints with the same index as the given {@link SecretWaypoint} as found or missing and logs the given message. + * @param secretWaypoint the secret waypoint to read the index from. + * @param found whether to mark the secret as found or missing + * @param msg the message to log + * @param args the args for the {@link org.slf4j.Logger#info(String, Object...) Logger#info(String, Object...)} call + */ + private void markSecretsAndLogInfo(SecretWaypoint secretWaypoint, boolean found, String msg, Object... args) { + markSecrets(secretWaypoint.secretIndex, found); DungeonManager.LOGGER.info(msg, args); } diff --git a/src/main/resources/assets/skyblocker/dungeons/secretlocations.json b/src/main/resources/assets/skyblocker/dungeons/secretlocations.json index 0f22f59719..760e72d996 100644 --- a/src/main/resources/assets/skyblocker/dungeons/secretlocations.json +++ b/src/main/resources/assets/skyblocker/dungeons/secretlocations.json @@ -4730,7 +4730,7 @@ "z":15 }, { - "secretName":"2 - Stonk", + "secretName":"1 - Stonk", "category":"stonk", "x":26, "y":112,