From 175ab9f7bb885f485a695963e5ef9543a104fd9a Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sun, 17 Aug 2025 15:53:14 -0500 Subject: [PATCH 01/30] Adding initial build recorder and replay prototype --- src/main/java/com/owlmaddie/ModInit.java | 4 + .../com/owlmaddie/buildrec/BuildRecorder.java | 289 ++++++++++++++++++ .../com/owlmaddie/commands/BuildCommands.java | 98 ++++++ 3 files changed, 391 insertions(+) create mode 100644 src/main/java/com/owlmaddie/buildrec/BuildRecorder.java create mode 100644 src/main/java/com/owlmaddie/commands/BuildCommands.java diff --git a/src/main/java/com/owlmaddie/ModInit.java b/src/main/java/com/owlmaddie/ModInit.java index 187fec39..59b83506 100644 --- a/src/main/java/com/owlmaddie/ModInit.java +++ b/src/main/java/com/owlmaddie/ModInit.java @@ -4,6 +4,8 @@ package com.owlmaddie; import com.owlmaddie.commands.CreatureChatCommands; +import com.owlmaddie.commands.BuildCommands; +import com.owlmaddie.buildrec.BuildRecorder; import com.owlmaddie.inventory.ModMenus; import com.owlmaddie.network.ServerPackets; import net.fabricmc.api.ModInitializer; @@ -26,6 +28,8 @@ public void onInitialize() { // Register server commands CreatureChatCommands.register(); + BuildCommands.register(); + BuildRecorder.init(); // Register menus and events ModMenus.register(); diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java new file mode 100644 index 00000000..96047bcb --- /dev/null +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -0,0 +1,289 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.buildrec; + +import com.google.gson.Gson; +import com.google.gson.stream.JsonReader; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Mth; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.core.BlockPos; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.animal.Pig; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +/** + * Utility to record and replay player build actions. + */ +public class BuildRecorder { + private static final Gson GSON = new Gson(); + private static final Map RECORDINGS = new ConcurrentHashMap<>(); + private static final List REPLAYS = new ArrayList<>(); + + static { + UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { + if (world.isClientSide || !(player instanceof ServerPlayer sp)) return net.minecraft.world.InteractionResult.PASS; + BlockPos pos = hitResult.getBlockPos().relative(hitResult.getDirection()); + double px = player.getX(), py = player.getY(), pz = player.getZ(); + float yaw = player.getYRot(), pitch = player.getXRot(); + world.getServer().execute(() -> { + BlockState state = world.getBlockState(pos); + if (!state.isAir()) recordPlace(sp, pos, state, px, py, pz, yaw, pitch); + }); + return net.minecraft.world.InteractionResult.PASS; + }); + PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, blockEntity) -> { + if (player instanceof ServerPlayer sp) { + double px = player.getX(), py = player.getY(), pz = player.getZ(); + float yaw = player.getYRot(), pitch = player.getXRot(); + recordBreak(sp, (ServerLevel) world, pos, state, px, py, pz, yaw, pitch); + } + }); + ServerTickEvents.START_SERVER_TICK.register(BuildRecorder::tick); + } + + public static void init() { + // Ensure static initializer runs + } + + public static boolean start(ServerPlayer player) { + if (RECORDINGS.containsKey(player.getUUID())) { + return false; + } + RECORDINGS.put(player.getUUID(), new Recording(player)); + return true; + } + + public static Summary stop(ServerPlayer player) { + Recording rec = RECORDINGS.remove(player.getUUID()); + if (rec == null) return null; + return rec.save(); + } + + public static boolean startReplay(ServerPlayer player, String fileName, int speed) { + Path dir = buildDir(); + Path file = dir.resolve(fileName); + if (!Files.exists(file)) return false; + try (JsonReader reader = new JsonReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(file)), StandardCharsets.UTF_8))) { + List actions = new ArrayList<>(); + reader.beginArray(); + while (reader.hasNext()) { + actions.add(GSON.fromJson(reader, Action.class)); + } + reader.endArray(); + int interval = Math.max(1, 20 / speed); + ServerLevel level = (ServerLevel) player.level(); + Pig pig = new Pig(EntityType.PIG, level); + pig.teleportTo(player.getX(), player.getY(), player.getZ()); + pig.setYRot(player.getYRot()); + pig.setXRot(player.getXRot()); + pig.yHeadRot = player.getYRot(); + pig.yBodyRot = player.getYRot(); + pig.setNoAi(true); + pig.setInvulnerable(true); + level.addFreshEntity(pig); + REPLAYS.add(new Replay(pig, actions, interval)); + return true; + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + private static void recordPlace(ServerPlayer player, BlockPos pos, BlockState state, + double px, double py, double pz, float yaw, float pitch) { + Recording rec = RECORDINGS.get(player.getUUID()); + if (rec != null) { + rec.addAction("place", pos, state, px, py, pz, yaw, pitch); + } + } + + private static void recordBreak(ServerPlayer player, ServerLevel world, BlockPos pos, BlockState state, + double px, double py, double pz, float yaw, float pitch) { + if (state.getDestroySpeed(world, pos) <= 0.2F) return; + Recording rec = RECORDINGS.get(player.getUUID()); + if (rec != null) { + rec.addAction("break", pos, state, px, py, pz, yaw, pitch); + } + } + + private static void tick(MinecraftServer server) { + Iterator it = REPLAYS.iterator(); + while (it.hasNext()) { + Replay r = it.next(); + if (r.action == null) { + if (r.index >= r.actions.size()) { + r.pig.discard(); + it.remove(); + continue; + } + r.action = r.actions.get(r.index++); + r.progress = 0; + r.sx = r.pig.getX(); + r.sy = r.pig.getY(); + r.sz = r.pig.getZ(); + r.syaw = r.pig.getYRot(); + r.spitch = r.pig.getXRot(); + r.tx = r.baseX + r.action.px; + r.ty = r.baseY + r.action.py; + r.tz = r.baseZ + r.action.pz; + r.tyaw = r.action.yaw; + r.tpitch = r.action.pitch; + } + r.progress++; + double t = Math.min(1.0, r.progress / (double) r.interval); + double px = Mth.lerp(t, r.sx, r.tx); + double py = Mth.lerp(t, r.sy, r.ty); + double pz = Mth.lerp(t, r.sz, r.tz); + r.pig.teleportTo(px, py, pz); + r.pig.setYRot(r.syaw); + r.pig.setXRot(r.spitch); + r.pig.yHeadRot = r.syaw; + r.pig.yBodyRot = r.syaw; + if (r.progress >= r.interval) { + BlockPos bpos = new BlockPos(Mth.floor(r.baseX + r.action.bx), Mth.floor(r.baseY + r.action.by), Mth.floor(r.baseZ + r.action.bz)); + if (r.pig.distanceToSqr(r.tx, r.ty, r.tz) > 0.25) { + r.pig.teleportTo(r.tx, r.ty, r.tz); + } + r.pig.teleportTo(r.tx, r.ty, r.tz); + r.pig.setYRot(r.tyaw); + r.pig.setXRot(r.tpitch); + r.pig.yHeadRot = r.tyaw; + r.pig.yBodyRot = r.tyaw; + r.pig.level().getChunkAt(bpos); + if ("place".equals(r.action.action)) { + BlockState state = Block.stateById(r.action.stateId); + r.pig.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(state.getBlock())); + r.pig.level().setBlock(bpos, state, 3); + } else { + r.pig.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); + r.pig.level().removeBlock(bpos, false); + } + r.action = null; + } + } + } + + private static Path buildDir() { + Path dir = FabricLoader.getInstance().getConfigDir().resolve("creaturechat").resolve("builds"); + try { + Files.createDirectories(dir); + } catch (IOException ignored) { + } + return dir; + } + + + public static class Summary { + public final String id; + public final int total; + public final int additions; + public final int destroys; + + public Summary(String id, int total, int additions, int destroys) { + this.id = id; + this.total = total; + this.additions = additions; + this.destroys = destroys; + } + } + + private static class Recording { + final int ox, oy, oz; + final List actions = new ArrayList<>(); + int additions = 0; + int destroys = 0; + + Recording(ServerPlayer player) { + BlockPos p = player.blockPosition(); + this.ox = p.getX(); + this.oy = p.getY(); + this.oz = p.getZ(); + } + + void addAction(String type, BlockPos pos, BlockState state, + double px, double py, double pz, float yaw, float pitch) { + Action a = new Action(); + a.action = type; + a.stateId = Block.getId(state); + a.bx = pos.getX() - ox; + a.by = pos.getY() - oy; + a.bz = pos.getZ() - oz; + a.px = px - ox; + a.py = py - oy; + a.pz = pz - oz; + a.yaw = yaw; + a.pitch = pitch; + actions.add(a); + if ("place".equals(type)) additions++; else destroys++; + } + + Summary save() { + String id = UUID.randomUUID().toString().split("-")[0] + ".json.gz"; + Path file = buildDir().resolve(id); + try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(file)), StandardCharsets.UTF_8))) { + w.write("[\n"); + for (int i = 0; i < actions.size(); i++) { + w.write(GSON.toJson(actions.get(i))); + if (i < actions.size() - 1) w.write(",\n"); + } + w.write("\n]"); + } catch (IOException e) { + e.printStackTrace(); + } + return new Summary(id, actions.size(), additions, destroys); + } + } + + private static class Replay { + final Pig pig; + final List actions; + final double baseX, baseY, baseZ; + final int interval; + int index = 0; + Action action = null; + int progress = 0; + double sx, sy, sz, tx, ty, tz; + float syaw, spitch, tyaw, tpitch; + + Replay(Pig pig, List actions, int interval) { + this.pig = pig; + this.actions = actions; + this.interval = interval; + BlockPos p = pig.blockPosition(); + this.baseX = p.getX(); + this.baseY = p.getY(); + this.baseZ = p.getZ(); + } + } + + private static class Action { + String action; + int stateId; + int bx, by, bz; + double px, py, pz; + float yaw, pitch; + } +} + diff --git a/src/main/java/com/owlmaddie/commands/BuildCommands.java b/src/main/java/com/owlmaddie/commands/BuildCommands.java new file mode 100644 index 00000000..5342dd3a --- /dev/null +++ b/src/main/java/com/owlmaddie/commands/BuildCommands.java @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import com.owlmaddie.buildrec.BuildRecorder; +import com.owlmaddie.buildrec.BuildRecorder.Summary; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.minecraft.ChatFormatting; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.fabricmc.loader.api.FabricLoader; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +/** + * Commands to record and replay builds. + */ +public class BuildCommands { + public static void register() { + ServerLifecycleEvents.SERVER_STARTING.register(server -> { + CommandDispatcher dispatcher = server.getCommands().getDispatcher(); + dispatcher.register(Commands.literal("creaturechat") + .then(Commands.literal("buildrec") + .then(Commands.literal("start").executes(BuildCommands::start)) + .then(Commands.literal("stop").executes(BuildCommands::stop)) + .then(Commands.literal("replay") + .then(Commands.argument("id", StringArgumentType.string()) + .suggests(BuildCommands::suggest) + .executes(ctx -> replay(ctx, 2)) + .then(Commands.argument("speed", IntegerArgumentType.integer(1, 4)) + .suggests((c, b) -> { + b.suggest("1"); + b.suggest("2"); + b.suggest("4"); + return b.buildFuture(); + }) + .executes(ctx -> replay(ctx, IntegerArgumentType.getInteger(ctx, "speed")))))))); + }); + } + + private static int start(CommandContext context) throws com.mojang.brigadier.exceptions.CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + if (BuildRecorder.start(player)) { + context.getSource().sendSuccess(() -> Component.literal("Recording started"), false); + return 1; + } + context.getSource().sendSuccess(() -> Component.literal("Already recording").withStyle(ChatFormatting.RED), false); + return 0; + } + + private static int stop(CommandContext context) throws com.mojang.brigadier.exceptions.CommandSyntaxException { + ServerPlayer player = context.getSource().getPlayerOrException(); + Summary s = BuildRecorder.stop(player); + if (s != null) { + Component msg = Component.literal("Saved build " + s.id + ". Blocks: " + s.total + ", additions: " + s.additions + ", destroys: " + s.destroys); + context.getSource().sendSuccess(() -> msg, false); + return 1; + } + context.getSource().sendSuccess(() -> Component.literal("No active recording").withStyle(ChatFormatting.RED), false); + return 0; + } + + private static int replay(CommandContext context, int speed) throws com.mojang.brigadier.exceptions.CommandSyntaxException { + int spd = (speed == 1 || speed == 2 || speed == 4) ? speed : 1; + ServerPlayer player = context.getSource().getPlayerOrException(); + String id = StringArgumentType.getString(context, "id"); + if (BuildRecorder.startReplay(player, id, spd)) { + final int fs = spd; + final String fid = id; + context.getSource().sendSuccess(() -> Component.literal("Replaying build " + fid + " at " + fs + "x"), false); + return 1; + } + context.getSource().sendSuccess(() -> Component.literal("Could not replay " + id).withStyle(ChatFormatting.RED), false); + return 0; + } + + private static CompletableFuture suggest(CommandContext context, SuggestionsBuilder builder) { + Path dir = FabricLoader.getInstance().getConfigDir().resolve("creaturechat").resolve("builds"); + try (Stream stream = Files.list(dir)) { + stream.map(p -> p.getFileName().toString()).forEach(builder::suggest); + } catch (Exception ignored) { + } + return builder.buildFuture(); + } +} + From 382601aaff070d701fcaef16f145e52faba9ea92 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sun, 17 Aug 2025 18:06:44 -0500 Subject: [PATCH 02/30] New build recording command - Record, Save, and Replay builds (placing, destroying, interacting with blocks + player poses) - Spawns a pig to replay builds (temporarily placeholder) - Replay speeds can be adjusted between 1X and 32X --- CHANGELOG.md | 8 + .../com/owlmaddie/buildrec/BuildRecorder.java | 249 +++++++++++++----- .../com/owlmaddie/commands/BuildCommands.java | 21 +- 3 files changed, 206 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5ee671a..c79e64ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ All notable changes to **CreatureChat™** are documented in this file. The form ## Unreleased +## Added +- New build recording command + - Record, Save, and Replay builds (placing, destroying, interacting with blocks + player poses) + - Spawns a pig to replay builds (temporarily placeholder) + - Replay speeds can be adjusted between 1X and 32X + +## Unreleased + ### Added - Inventories for all entities with chat data (Shift+Right Click, or Press 'E' while riding) - Random items added to inventory on character creation (biome-specific loot tables for inventories) diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 96047bcb..2ee5fafd 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -9,15 +9,18 @@ import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.core.BlockPos; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.core.BlockPos; -import net.minecraft.server.MinecraftServer; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.animal.Pig; @@ -44,12 +47,22 @@ public class BuildRecorder { static { UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { if (world.isClientSide || !(player instanceof ServerPlayer sp)) return net.minecraft.world.InteractionResult.PASS; - BlockPos pos = hitResult.getBlockPos().relative(hitResult.getDirection()); + BlockPos target = hitResult.getBlockPos(); + BlockPos placePos = target.relative(hitResult.getDirection()); + BlockState beforeTarget = world.getBlockState(target); + BlockState beforePlace = world.getBlockState(placePos); double px = player.getX(), py = player.getY(), pz = player.getZ(); float yaw = player.getYRot(), pitch = player.getXRot(); world.getServer().execute(() -> { - BlockState state = world.getBlockState(pos); - if (!state.isAir()) recordPlace(sp, pos, state, px, py, pz, yaw, pitch); + BlockState afterPlace = world.getBlockState(placePos); + if (!afterPlace.isAir() && !afterPlace.equals(beforePlace)) { + recordPlace(sp, placePos, afterPlace, px, py, pz, yaw, pitch); + } else { + BlockState afterTarget = world.getBlockState(target); + if (!afterTarget.equals(beforeTarget)) { + recordInteract(sp, target, afterTarget, px, py, pz, yaw, pitch); + } + } }); return net.minecraft.world.InteractionResult.PASS; }); @@ -75,10 +88,10 @@ public static boolean start(ServerPlayer player) { return true; } - public static Summary stop(ServerPlayer player) { + public static Summary stop(ServerPlayer player, String name) { Recording rec = RECORDINGS.remove(player.getUUID()); if (rec == null) return null; - return rec.save(); + return rec.save(name); } public static boolean startReplay(ServerPlayer player, String fileName, int speed) { @@ -92,7 +105,6 @@ public static boolean startReplay(ServerPlayer player, String fileName, int spee actions.add(GSON.fromJson(reader, Action.class)); } reader.endArray(); - int interval = Math.max(1, 20 / speed); ServerLevel level = (ServerLevel) player.level(); Pig pig = new Pig(EntityType.PIG, level); pig.teleportTo(player.getX(), player.getY(), player.getZ()); @@ -103,7 +115,7 @@ public static boolean startReplay(ServerPlayer player, String fileName, int spee pig.setNoAi(true); pig.setInvulnerable(true); level.addFreshEntity(pig); - REPLAYS.add(new Replay(pig, actions, interval)); + REPLAYS.add(new Replay(pig, actions, speed)); return true; } catch (IOException e) { e.printStackTrace(); @@ -115,72 +127,133 @@ private static void recordPlace(ServerPlayer player, BlockPos pos, BlockState st double px, double py, double pz, float yaw, float pitch) { Recording rec = RECORDINGS.get(player.getUUID()); if (rec != null) { - rec.addAction("place", pos, state, px, py, pz, yaw, pitch); + long tick = player.level().getServer().getTickCount(); + rec.addAction(tick, "place", pos, state, px, py, pz, yaw, pitch); + if (state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)) { + DoubleBlockHalf half = state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF); + BlockPos otherPos = half == DoubleBlockHalf.LOWER ? pos.above() : pos.below(); + BlockState otherState = player.level().getBlockState(otherPos); + rec.addAction(tick, "place", otherPos, otherState, px, py, pz, yaw, pitch); + } } } private static void recordBreak(ServerPlayer player, ServerLevel world, BlockPos pos, BlockState state, double px, double py, double pz, float yaw, float pitch) { - if (state.getDestroySpeed(world, pos) <= 0.2F) return; Recording rec = RECORDINGS.get(player.getUUID()); if (rec != null) { - rec.addAction("break", pos, state, px, py, pz, yaw, pitch); + long tick = player.level().getServer().getTickCount(); + rec.addAction(tick, "break", pos, state, px, py, pz, yaw, pitch); + if (state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)) { + DoubleBlockHalf half = state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF); + BlockPos otherPos = half == DoubleBlockHalf.LOWER ? pos.above() : pos.below(); + rec.addAction(tick, "break", otherPos, state, px, py, pz, yaw, pitch); + } + } + } + + private static void recordInteract(ServerPlayer player, BlockPos pos, BlockState state, + double px, double py, double pz, float yaw, float pitch) { + Recording rec = RECORDINGS.get(player.getUUID()); + if (rec != null) { + long tick = player.level().getServer().getTickCount(); + rec.addAction(tick, "interact", pos, state, px, py, pz, yaw, pitch); + if (state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF)) { + DoubleBlockHalf half = state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF); + BlockPos otherPos = half == DoubleBlockHalf.LOWER ? pos.above() : pos.below(); + BlockState otherState = player.level().getBlockState(otherPos); + rec.addAction(tick, "interact", otherPos, otherState, px, py, pz, yaw, pitch); + } } } private static void tick(MinecraftServer server) { + RECORDINGS.forEach((uuid, rec) -> { + ServerPlayer p = server.getPlayerList().getPlayer(uuid); + if (p != null) { + rec.tick(p); + } + }); + Iterator it = REPLAYS.iterator(); while (it.hasNext()) { Replay r = it.next(); - if (r.action == null) { - if (r.index >= r.actions.size()) { - r.pig.discard(); - it.remove(); - continue; + double advance = r.speed; + while (advance > 0) { + if (r.action == null) { + if (r.index >= r.actions.size()) { + r.pig.discard(); + it.remove(); + break; + } + r.action = r.actions.get(r.index++); + r.progress = 0; + r.sx = r.pig.getX(); + r.sy = r.pig.getY(); + r.sz = r.pig.getZ(); + r.syaw = r.pig.getYRot(); + r.spitch = r.pig.getXRot(); + r.tx = r.baseX + r.action.px; + r.ty = r.baseY + r.action.py; + r.tz = r.baseZ + r.action.pz; + r.tyaw = r.action.yaw; + r.tpitch = r.action.pitch; } - r.action = r.actions.get(r.index++); - r.progress = 0; - r.sx = r.pig.getX(); - r.sy = r.pig.getY(); - r.sz = r.pig.getZ(); - r.syaw = r.pig.getYRot(); - r.spitch = r.pig.getXRot(); - r.tx = r.baseX + r.action.px; - r.ty = r.baseY + r.action.py; - r.tz = r.baseZ + r.action.pz; - r.tyaw = r.action.yaw; - r.tpitch = r.action.pitch; - } - r.progress++; - double t = Math.min(1.0, r.progress / (double) r.interval); - double px = Mth.lerp(t, r.sx, r.tx); - double py = Mth.lerp(t, r.sy, r.ty); - double pz = Mth.lerp(t, r.sz, r.tz); - r.pig.teleportTo(px, py, pz); - r.pig.setYRot(r.syaw); - r.pig.setXRot(r.spitch); - r.pig.yHeadRot = r.syaw; - r.pig.yBodyRot = r.syaw; - if (r.progress >= r.interval) { - BlockPos bpos = new BlockPos(Mth.floor(r.baseX + r.action.bx), Mth.floor(r.baseY + r.action.by), Mth.floor(r.baseZ + r.action.bz)); - if (r.pig.distanceToSqr(r.tx, r.ty, r.tz) > 0.25) { + double remain = r.action.dt - r.progress; + if (remain <= advance) { + r.progress += remain; + advance -= remain; r.pig.teleportTo(r.tx, r.ty, r.tz); - } - r.pig.teleportTo(r.tx, r.ty, r.tz); - r.pig.setYRot(r.tyaw); - r.pig.setXRot(r.tpitch); - r.pig.yHeadRot = r.tyaw; - r.pig.yBodyRot = r.tyaw; - r.pig.level().getChunkAt(bpos); - if ("place".equals(r.action.action)) { - BlockState state = Block.stateById(r.action.stateId); - r.pig.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(state.getBlock())); - r.pig.level().setBlock(bpos, state, 3); + r.pig.setYRot(r.tyaw); + r.pig.setXRot(r.tpitch); + r.pig.yHeadRot = r.tyaw; + r.pig.yBodyRot = r.tyaw; + if ("place".equals(r.action.action) || "break".equals(r.action.action) || "interact".equals(r.action.action)) { + BlockPos bpos = new BlockPos(Mth.floor(r.baseX + r.action.bx), Mth.floor(r.baseY + r.action.by), Mth.floor(r.baseZ + r.action.bz)); + r.pig.level().getChunkAt(bpos); + if ("place".equals(r.action.action)) { + BlockState state = Block.stateById(r.action.stateId); + boolean upper = state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) && state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER; + r.pig.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(state.getBlock())); + r.pig.level().setBlock(bpos, state, 3); + if (!upper) { + r.pig.level().playSound(null, bpos, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1f, 1f); + r.pig.swing(InteractionHand.MAIN_HAND); + } + } else if ("break".equals(r.action.action)) { + BlockState state = r.pig.level().getBlockState(bpos); + boolean upper = state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) && state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER; + if (!upper) { + r.pig.level().levelEvent(2001, bpos, Block.getId(state)); + r.pig.swing(InteractionHand.MAIN_HAND); + } + r.pig.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); + r.pig.level().removeBlock(bpos, false); + } else { + BlockState state = Block.stateById(r.action.stateId); + boolean upper = state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) && state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER; + r.pig.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); + r.pig.level().setBlock(bpos, state, 3); + if (!upper) { + r.pig.level().playSound(null, bpos, state.getSoundType().getHitSound(), SoundSource.BLOCKS, 1f, 1f); + r.pig.swing(InteractionHand.MAIN_HAND); + } + } + } + r.action = null; } else { - r.pig.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); - r.pig.level().removeBlock(bpos, false); + r.progress += advance; + double t = r.progress / r.action.dt; + double px = Mth.lerp(t, r.sx, r.tx); + double py = Mth.lerp(t, r.sy, r.ty); + double pz = Mth.lerp(t, r.sz, r.tz); + r.pig.teleportTo(px, py, pz); + r.pig.setYRot(r.syaw); + r.pig.setXRot(r.spitch); + r.pig.yHeadRot = r.syaw; + r.pig.yBodyRot = r.syaw; + advance = 0; } - r.action = null; } } } @@ -214,18 +287,59 @@ private static class Recording { final List actions = new ArrayList<>(); int additions = 0; int destroys = 0; + int poseTick = 0; + long lastTick; + double lastPx, lastPy, lastPz; + float lastYaw, lastPitch; + boolean poseInit = false; Recording(ServerPlayer player) { BlockPos p = player.blockPosition(); this.ox = p.getX(); this.oy = p.getY(); this.oz = p.getZ(); + this.lastTick = player.level().getServer().getTickCount(); + } + + void tick(ServerPlayer player) { + if (++poseTick >= 4) { + poseTick = 0; + double px = player.getX(); + double py = player.getY(); + double pz = player.getZ(); + float yaw = player.getYRot(); + float pitch = player.getXRot(); + if (!poseInit || px != lastPx || py != lastPy || pz != lastPz || yaw != lastYaw || pitch != lastPitch) { + addPose(player.level().getServer().getTickCount(), px, py, pz, yaw, pitch); + lastPx = px; + lastPy = py; + lastPz = pz; + lastYaw = yaw; + lastPitch = pitch; + poseInit = true; + } + } + } + + void addPose(long tick, double px, double py, double pz, float yaw, float pitch) { + Action a = new Action(); + a.action = "pose"; + a.dt = (int)(tick - lastTick); + lastTick = tick; + a.px = px - ox; + a.py = py - oy; + a.pz = pz - oz; + a.yaw = yaw; + a.pitch = pitch; + actions.add(a); } - void addAction(String type, BlockPos pos, BlockState state, + void addAction(long tick, String type, BlockPos pos, BlockState state, double px, double py, double pz, float yaw, float pitch) { Action a = new Action(); a.action = type; + a.dt = (int)(tick - lastTick); + lastTick = tick; a.stateId = Block.getId(state); a.bx = pos.getX() - ox; a.by = pos.getY() - oy; @@ -236,11 +350,13 @@ void addAction(String type, BlockPos pos, BlockState state, a.yaw = yaw; a.pitch = pitch; actions.add(a); - if ("place".equals(type)) additions++; else destroys++; + if ("place".equals(type)) additions++; + else if ("break".equals(type)) destroys++; } - Summary save() { - String id = UUID.randomUUID().toString().split("-")[0] + ".json.gz"; + Summary save(String name) { + String base = (name == null || name.isBlank()) ? UUID.randomUUID().toString().split("-")[0] : name.replaceAll("[^a-zA-Z0-9-_]", "_"); + String id = base + ".json.gz"; Path file = buildDir().resolve(id); try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(file)), StandardCharsets.UTF_8))) { w.write("[\n"); @@ -260,17 +376,17 @@ private static class Replay { final Pig pig; final List actions; final double baseX, baseY, baseZ; - final int interval; + final int speed; int index = 0; Action action = null; - int progress = 0; + double progress = 0; double sx, sy, sz, tx, ty, tz; float syaw, spitch, tyaw, tpitch; - Replay(Pig pig, List actions, int interval) { + Replay(Pig pig, List actions, int speed) { this.pig = pig; this.actions = actions; - this.interval = interval; + this.speed = speed; BlockPos p = pig.blockPosition(); this.baseX = p.getX(); this.baseY = p.getY(); @@ -282,6 +398,7 @@ private static class Action { String action; int stateId; int bx, by, bz; + int dt; double px, py, pz; float yaw, pitch; } diff --git a/src/main/java/com/owlmaddie/commands/BuildCommands.java b/src/main/java/com/owlmaddie/commands/BuildCommands.java index 5342dd3a..3e4bb765 100644 --- a/src/main/java/com/owlmaddie/commands/BuildCommands.java +++ b/src/main/java/com/owlmaddie/commands/BuildCommands.java @@ -34,16 +34,22 @@ public static void register() { dispatcher.register(Commands.literal("creaturechat") .then(Commands.literal("buildrec") .then(Commands.literal("start").executes(BuildCommands::start)) - .then(Commands.literal("stop").executes(BuildCommands::stop)) + .then(Commands.literal("stop") + .executes(ctx -> stop(ctx, null)) + .then(Commands.argument("name", StringArgumentType.string()) + .executes(ctx -> stop(ctx, StringArgumentType.getString(ctx, "name"))))) .then(Commands.literal("replay") .then(Commands.argument("id", StringArgumentType.string()) .suggests(BuildCommands::suggest) - .executes(ctx -> replay(ctx, 2)) - .then(Commands.argument("speed", IntegerArgumentType.integer(1, 4)) + .executes(ctx -> replay(ctx, 1)) + .then(Commands.argument("speed", IntegerArgumentType.integer(1, 32)) .suggests((c, b) -> { b.suggest("1"); b.suggest("2"); b.suggest("4"); + b.suggest("8"); + b.suggest("16"); + b.suggest("32"); return b.buildFuture(); }) .executes(ctx -> replay(ctx, IntegerArgumentType.getInteger(ctx, "speed")))))))); @@ -60,9 +66,9 @@ private static int start(CommandContext context) throws com. return 0; } - private static int stop(CommandContext context) throws com.mojang.brigadier.exceptions.CommandSyntaxException { + private static int stop(CommandContext context, String name) throws com.mojang.brigadier.exceptions.CommandSyntaxException { ServerPlayer player = context.getSource().getPlayerOrException(); - Summary s = BuildRecorder.stop(player); + Summary s = BuildRecorder.stop(player, name); if (s != null) { Component msg = Component.literal("Saved build " + s.id + ". Blocks: " + s.total + ", additions: " + s.additions + ", destroys: " + s.destroys); context.getSource().sendSuccess(() -> msg, false); @@ -73,7 +79,10 @@ private static int stop(CommandContext context) throws com.m } private static int replay(CommandContext context, int speed) throws com.mojang.brigadier.exceptions.CommandSyntaxException { - int spd = (speed == 1 || speed == 2 || speed == 4) ? speed : 1; + int spd = switch (speed) { + case 1, 2, 4, 8, 16, 32 -> speed; + default -> 1; + }; ServerPlayer player = context.getSource().getPlayerOrException(); String id = StringArgumentType.getString(context, "id"); if (BuildRecorder.startReplay(player, id, spd)) { From b7610b73a66031b006d0d601c53ffa13d5a14753 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 19 Aug 2025 15:20:09 -0500 Subject: [PATCH 03/30] Replay adjusts eye height (line of sight) so entities look at the same place as the player. Spawns any entity type to replay build --- CHANGELOG.md | 5 +- .../com/owlmaddie/buildrec/BuildRecorder.java | 241 +++++++++++++----- .../com/owlmaddie/buildrec/MobHelper.java | 21 ++ .../com/owlmaddie/commands/BuildCommands.java | 100 +++++--- .../com/owlmaddie/buildrec/MobHelper.java | 22 ++ 5 files changed, 296 insertions(+), 93 deletions(-) create mode 100644 src/main/java/com/owlmaddie/buildrec/MobHelper.java create mode 100644 src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java diff --git a/CHANGELOG.md b/CHANGELOG.md index c79e64ea..65adc097 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,9 @@ All notable changes to **CreatureChat™** are documented in this file. The form ## Added - New build recording command - Record, Save, and Replay builds (placing, destroying, interacting with blocks + player poses) - - Spawns a pig to replay builds (temporarily placeholder) - - Replay speeds can be adjusted between 1X and 32X + - Spawns any entity type to replay build + - Replay speeds can be adjusted (any integer, but defaults to 1X - the original speed) + - Replay adjusts eye height (line of sight) so entities look at the same place as the player ## Unreleased diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 2ee5fafd..5ec55e74 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -4,12 +4,16 @@ package com.owlmaddie.buildrec; import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; import com.google.gson.stream.JsonReader; +import com.owlmaddie.buildrec.MobHelper; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -17,12 +21,19 @@ import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.animal.Pig; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.BufferedWriter; import java.io.IOException; @@ -43,6 +54,7 @@ public class BuildRecorder { private static final Gson GSON = new Gson(); private static final Map RECORDINGS = new ConcurrentHashMap<>(); private static final List REPLAYS = new ArrayList<>(); + private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); static { UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { @@ -80,49 +92,116 @@ public static void init() { // Ensure static initializer runs } - public static boolean start(ServerPlayer player) { + public static boolean start(ServerPlayer player, String name) { if (RECORDINGS.containsKey(player.getUUID())) { + LOGGER.info("[BuildRec] start ignored already recording player={}", player.getGameProfile().getName()); return false; } - RECORDINGS.put(player.getUUID(), new Recording(player)); + RECORDINGS.put(player.getUUID(), new Recording(player, name)); + LOGGER.info("[BuildRec] start name={} player={}", name, player.getGameProfile().getName()); return true; } - public static Summary stop(ServerPlayer player, String name) { + public static Summary stop(ServerPlayer player) { Recording rec = RECORDINGS.remove(player.getUUID()); - if (rec == null) return null; - return rec.save(name); + if (rec == null) { + LOGGER.info("[BuildRec] stop ignored no active recording player={}", player.getGameProfile().getName()); + return null; + } + Summary summary = rec.finish(player); + LOGGER.info("[BuildRec] stop player={} file={} total={} additions={} destroys={}", + player.getGameProfile().getName(), summary.id, summary.total, summary.additions, summary.destroys); + return summary; } - public static boolean startReplay(ServerPlayer player, String fileName, int speed) { + public static boolean startReplay(ServerPlayer player, String fileName, EntityType entityType, int speed) { Path dir = buildDir(); - Path file = dir.resolve(fileName); - if (!Files.exists(file)) return false; + String actual = fileName.endsWith(".json.gz") ? fileName : fileName + ".json.gz"; + Path file = dir.resolve(actual); + LOGGER.info("[BuildRec] replay file={} entity={} speed={} player={}", actual, + entityType != null ? BuiltInRegistries.ENTITY_TYPE.getKey(entityType).toString() : "pig", speed, + player.getGameProfile().getName()); + if (!Files.exists(file)) { + LOGGER.info("[BuildRec] replay missing file={}", file); + return false; + } try (JsonReader reader = new JsonReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(file)), StandardCharsets.UTF_8))) { List actions = new ArrayList<>(); reader.beginArray(); + JsonElement first = GSON.fromJson(reader, JsonElement.class); + RecordingMeta meta; + if (first != null && first.isJsonObject() && first.getAsJsonObject().has("action") && "meta".equals(first.getAsJsonObject().get("action").getAsString())) { + meta = GSON.fromJson(first, RecordingMeta.class); + } else { + meta = new RecordingMeta(); + if (first != null) { + Action firstAction = GSON.fromJson(first, Action.class); + if (firstAction != null) actions.add(firstAction); + } + } while (reader.hasNext()) { - actions.add(GSON.fromJson(reader, Action.class)); + Action a = GSON.fromJson(reader, Action.class); + if (a != null) actions.add(a); } reader.endArray(); + if (actions.isEmpty()) { + LOGGER.info("[BuildRec] replay file={} has no actions", file); + return false; + } + double recEye = meta.eyeHeight > 0 ? meta.eyeHeight : player.getEyeHeight(); + double recWidth = meta.bbWidth; + double recHeight = meta.bbHeight; ServerLevel level = (ServerLevel) player.level(); - Pig pig = new Pig(EntityType.PIG, level); - pig.teleportTo(player.getX(), player.getY(), player.getZ()); - pig.setYRot(player.getYRot()); - pig.setXRot(player.getXRot()); - pig.yHeadRot = player.getYRot(); - pig.yBodyRot = player.getYRot(); - pig.setNoAi(true); - pig.setInvulnerable(true); - level.addFreshEntity(pig); - REPLAYS.add(new Replay(pig, actions, speed)); + Mob actor; + if (entityType == null) { + actor = new Pig(EntityType.PIG, level); + } else { + actor = MobHelper.create(entityType, level); + if (actor == null) { + LOGGER.info("[BuildRec] replay could not create {}", BuiltInRegistries.ENTITY_TYPE.getKey(entityType)); + return false; + } + } + MobHelper.initSpawn(actor, level); + actor.teleportTo(player.getX(), player.getY(), player.getZ()); + actor.setYRot(player.getYRot()); + float sp = adjustPitch(level, player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot(), recEye, actor.getEyeHeight(), actor); + actor.setXRot(sp); + actor.yHeadRot = player.getYRot(); + actor.yBodyRot = player.getYRot(); + actor.setNoAi(true); + actor.setInvulnerable(true); + actor.setPersistenceRequired(); + level.addFreshEntity(actor); + REPLAYS.add(new Replay(actor, actions, speed, recEye, recWidth, recHeight)); + LOGGER.info("[BuildRec] replay loaded actions={} eyeHeight={} bbW={} bbH={}", actions.size(), recEye, recWidth, recHeight); return true; - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException | JsonParseException e) { + LOGGER.error("[BuildRec] replay failed to load {}", file, e); + } catch (Exception e) { + LOGGER.error("[BuildRec] replay runtime error {}", file, e); } return false; } + private static float adjustPitch(ServerLevel level, double x, double y, double z, float yaw, float pitch, + double fromEye, double toEye, Entity ctx) { + if (fromEye <= 0 || toEye <= 0) return pitch; + double yawRad = Math.toRadians(yaw); + double pitchRad = Math.toRadians(pitch); + double dx = -Math.sin(yawRad) * Math.cos(pitchRad); + double dy = -Math.sin(pitchRad); + double dz = Math.cos(yawRad) * Math.cos(pitchRad); + Vec3 start = new Vec3(x, y + fromEye, z); + Vec3 end = start.add(dx * 64, dy * 64, dz * 64); + var hit = level.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, ctx)); + Vec3 target = hit.getType() == HitResult.Type.MISS ? end : hit.getLocation(); + Vec3 newEye = new Vec3(x, y + toEye, z); + Vec3 diff = target.subtract(newEye); + double horiz = Math.sqrt(diff.x * diff.x + diff.z * diff.z); + return (float) -Math.toDegrees(Math.atan2(diff.y, horiz)); + } + private static void recordPlace(ServerPlayer player, BlockPos pos, BlockState state, double px, double py, double pz, float yaw, float pitch) { Recording rec = RECORDINGS.get(player.getUUID()); @@ -182,61 +261,63 @@ private static void tick(MinecraftServer server) { while (advance > 0) { if (r.action == null) { if (r.index >= r.actions.size()) { - r.pig.discard(); + LOGGER.info("[BuildRec] replay finished actor={} actions={} speed={}", + r.actor.getType().toShortString(), r.actions.size(), r.speed); + r.actor.discard(); it.remove(); break; } r.action = r.actions.get(r.index++); r.progress = 0; - r.sx = r.pig.getX(); - r.sy = r.pig.getY(); - r.sz = r.pig.getZ(); - r.syaw = r.pig.getYRot(); - r.spitch = r.pig.getXRot(); + r.sx = r.actor.getX(); + r.sy = r.actor.getY(); + r.sz = r.actor.getZ(); + r.syaw = r.actor.getYRot(); + r.spitch = r.actor.getXRot(); r.tx = r.baseX + r.action.px; r.ty = r.baseY + r.action.py; r.tz = r.baseZ + r.action.pz; r.tyaw = r.action.yaw; - r.tpitch = r.action.pitch; + r.tpitch = adjustPitch((ServerLevel) r.actor.level(), r.baseX + r.action.px, r.baseY + r.action.py, r.baseZ + r.action.pz, r.action.yaw, r.action.pitch, r.recordEyeHeight, r.actor.getEyeHeight(), r.actor); } double remain = r.action.dt - r.progress; if (remain <= advance) { r.progress += remain; advance -= remain; - r.pig.teleportTo(r.tx, r.ty, r.tz); - r.pig.setYRot(r.tyaw); - r.pig.setXRot(r.tpitch); - r.pig.yHeadRot = r.tyaw; - r.pig.yBodyRot = r.tyaw; + r.actor.teleportTo(r.tx, r.ty, r.tz); + r.actor.setYRot(r.tyaw); + r.actor.setXRot(r.tpitch); + r.actor.yHeadRot = r.tyaw; + r.actor.yBodyRot = r.tyaw; if ("place".equals(r.action.action) || "break".equals(r.action.action) || "interact".equals(r.action.action)) { BlockPos bpos = new BlockPos(Mth.floor(r.baseX + r.action.bx), Mth.floor(r.baseY + r.action.by), Mth.floor(r.baseZ + r.action.bz)); - r.pig.level().getChunkAt(bpos); + r.actor.level().getChunkAt(bpos); if ("place".equals(r.action.action)) { BlockState state = Block.stateById(r.action.stateId); boolean upper = state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) && state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER; - r.pig.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(state.getBlock())); - r.pig.level().setBlock(bpos, state, 3); + r.actor.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(state.getBlock())); + r.actor.level().setBlock(bpos, state, 3); if (!upper) { - r.pig.level().playSound(null, bpos, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1f, 1f); - r.pig.swing(InteractionHand.MAIN_HAND); + r.actor.level().playSound(null, bpos, state.getSoundType().getPlaceSound(), SoundSource.BLOCKS, 1f, 1f); + r.actor.swing(InteractionHand.MAIN_HAND); } } else if ("break".equals(r.action.action)) { - BlockState state = r.pig.level().getBlockState(bpos); + BlockState state = r.actor.level().getBlockState(bpos); boolean upper = state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) && state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER; if (!upper) { - r.pig.level().levelEvent(2001, bpos, Block.getId(state)); - r.pig.swing(InteractionHand.MAIN_HAND); + r.actor.level().levelEvent(2001, bpos, Block.getId(state)); + r.actor.swing(InteractionHand.MAIN_HAND); } - r.pig.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); - r.pig.level().removeBlock(bpos, false); + r.actor.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); + r.actor.level().removeBlock(bpos, false); } else { BlockState state = Block.stateById(r.action.stateId); boolean upper = state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) && state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER; - r.pig.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); - r.pig.level().setBlock(bpos, state, 3); + r.actor.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); + r.actor.level().setBlock(bpos, state, 3); if (!upper) { - r.pig.level().playSound(null, bpos, state.getSoundType().getHitSound(), SoundSource.BLOCKS, 1f, 1f); - r.pig.swing(InteractionHand.MAIN_HAND); + r.actor.level().playSound(null, bpos, state.getSoundType().getHitSound(), SoundSource.BLOCKS, 1f, 1f); + r.actor.swing(InteractionHand.MAIN_HAND); } } } @@ -247,11 +328,11 @@ private static void tick(MinecraftServer server) { double px = Mth.lerp(t, r.sx, r.tx); double py = Mth.lerp(t, r.sy, r.ty); double pz = Mth.lerp(t, r.sz, r.tz); - r.pig.teleportTo(px, py, pz); - r.pig.setYRot(r.syaw); - r.pig.setXRot(r.spitch); - r.pig.yHeadRot = r.syaw; - r.pig.yBodyRot = r.syaw; + r.actor.teleportTo(px, py, pz); + r.actor.setYRot(r.syaw); + r.actor.setXRot(r.spitch); + r.actor.yHeadRot = r.syaw; + r.actor.yBodyRot = r.syaw; advance = 0; } } @@ -268,6 +349,7 @@ private static Path buildDir() { } + public static class Summary { public final String id; public final int total; @@ -285,6 +367,10 @@ public Summary(String id, int total, int additions, int destroys) { private static class Recording { final int ox, oy, oz; final List actions = new ArrayList<>(); + final String name; + final double eyeHeight; + final double bbWidth; + final double bbHeight; int additions = 0; int destroys = 0; int poseTick = 0; @@ -293,12 +379,16 @@ private static class Recording { float lastYaw, lastPitch; boolean poseInit = false; - Recording(ServerPlayer player) { + Recording(ServerPlayer player, String name) { + this.name = name; BlockPos p = player.blockPosition(); this.ox = p.getX(); this.oy = p.getY(); this.oz = p.getZ(); this.lastTick = player.level().getServer().getTickCount(); + this.eyeHeight = player.getEyeHeight(); + this.bbWidth = player.getBbWidth(); + this.bbHeight = player.getBbHeight(); } void tick(ServerPlayer player) { @@ -354,46 +444,73 @@ void addAction(long tick, String type, BlockPos pos, BlockState state, else if ("break".equals(type)) destroys++; } - Summary save(String name) { + Summary save() { String base = (name == null || name.isBlank()) ? UUID.randomUUID().toString().split("-")[0] : name.replaceAll("[^a-zA-Z0-9-_]", "_"); String id = base + ".json.gz"; Path file = buildDir().resolve(id); try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(file)), StandardCharsets.UTF_8))) { w.write("[\n"); - for (int i = 0; i < actions.size(); i++) { - w.write(GSON.toJson(actions.get(i))); - if (i < actions.size() - 1) w.write(",\n"); + w.write(GSON.toJson(new RecordingMeta(eyeHeight, bbWidth, bbHeight))); + for (Action a : actions) { + w.write(",\n"); + w.write(GSON.toJson(a)); } w.write("\n]"); } catch (IOException e) { - e.printStackTrace(); + LOGGER.error("[BuildRec] save failed file={}", id, e); } + LOGGER.info("[BuildRec] save file={} actions={} additions={} destroys={}", id, actions.size(), additions, destroys); return new Summary(id, actions.size(), additions, destroys); } + + Summary finish(ServerPlayer player) { + return save(); + } } private static class Replay { - final Pig pig; + final Mob actor; final List actions; final double baseX, baseY, baseZ; final int speed; + final double recordEyeHeight; + final double recordBbWidth; + final double recordBbHeight; int index = 0; Action action = null; double progress = 0; double sx, sy, sz, tx, ty, tz; float syaw, spitch, tyaw, tpitch; - Replay(Pig pig, List actions, int speed) { - this.pig = pig; + Replay(Mob actor, List actions, int speed, double recordEyeHeight, double recordBbWidth, double recordBbHeight) { + this.actor = actor; this.actions = actions; this.speed = speed; - BlockPos p = pig.blockPosition(); + this.recordEyeHeight = recordEyeHeight; + this.recordBbWidth = recordBbWidth; + this.recordBbHeight = recordBbHeight; + BlockPos p = actor.blockPosition(); this.baseX = p.getX(); this.baseY = p.getY(); this.baseZ = p.getZ(); } } + private static class RecordingMeta { + String action = "meta"; + double eyeHeight; + double bbWidth; + double bbHeight; + + RecordingMeta() {} + + RecordingMeta(double eyeHeight, double bbWidth, double bbHeight) { + this.eyeHeight = eyeHeight; + this.bbWidth = bbWidth; + this.bbHeight = bbHeight; + } + } + private static class Action { String action; int stateId; diff --git a/src/main/java/com/owlmaddie/buildrec/MobHelper.java b/src/main/java/com/owlmaddie/buildrec/MobHelper.java new file mode 100644 index 00000000..568071ea --- /dev/null +++ b/src/main/java/com/owlmaddie/buildrec/MobHelper.java @@ -0,0 +1,21 @@ +package com.owlmaddie.buildrec; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; + +/** Utility methods for creating mob instances across versions. */ +public class MobHelper { + private MobHelper() {} + + public static Mob create(EntityType type, ServerLevel level) { + return (Mob) type.create(level); + } + + public static void initSpawn(Mob mob, ServerLevel level) { + if (mob != null) { + mob.finalizeSpawn(level, level.getCurrentDifficultyAt(mob.blockPosition()), MobSpawnType.COMMAND, null, null); + } + } +} diff --git a/src/main/java/com/owlmaddie/commands/BuildCommands.java b/src/main/java/com/owlmaddie/commands/BuildCommands.java index 3e4bb765..3f25347c 100644 --- a/src/main/java/com/owlmaddie/commands/BuildCommands.java +++ b/src/main/java/com/owlmaddie/commands/BuildCommands.java @@ -4,61 +4,68 @@ package com.owlmaddie.commands; import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.owlmaddie.buildrec.BuildRecorder; import com.owlmaddie.buildrec.BuildRecorder.Summary; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.loader.api.FabricLoader; import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.commands.arguments.ResourceLocationArgument; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; -import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobCategory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import java.util.stream.Stream; /** * Commands to record and replay builds. */ public class BuildCommands { + private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); public static void register() { ServerLifecycleEvents.SERVER_STARTING.register(server -> { CommandDispatcher dispatcher = server.getCommands().getDispatcher(); dispatcher.register(Commands.literal("creaturechat") .then(Commands.literal("buildrec") - .then(Commands.literal("start").executes(BuildCommands::start)) - .then(Commands.literal("stop") - .executes(ctx -> stop(ctx, null)) + .then(Commands.literal("start") + .executes(ctx -> start(ctx, null)) .then(Commands.argument("name", StringArgumentType.string()) - .executes(ctx -> stop(ctx, StringArgumentType.getString(ctx, "name"))))) + .executes(ctx -> start(ctx, StringArgumentType.getString(ctx, "name"))))) + .then(Commands.literal("stop") + .executes(BuildCommands::stop)) .then(Commands.literal("replay") .then(Commands.argument("id", StringArgumentType.string()) .suggests(BuildCommands::suggest) - .executes(ctx -> replay(ctx, 1)) - .then(Commands.argument("speed", IntegerArgumentType.integer(1, 32)) - .suggests((c, b) -> { - b.suggest("1"); - b.suggest("2"); - b.suggest("4"); - b.suggest("8"); - b.suggest("16"); - b.suggest("32"); - return b.buildFuture(); - }) - .executes(ctx -> replay(ctx, IntegerArgumentType.getInteger(ctx, "speed")))))))); + .then(Commands.argument("entity", ResourceLocationArgument.id()) + .suggests((c, b) -> SharedSuggestionProvider.suggestResource(getLivingEntityIds(), b)) + .executes(ctx -> replay(ctx, ResourceLocationArgument.getId(ctx, "entity"), 1)) + .then(Commands.argument("speed", IntegerArgumentType.integer(1, 32)) + .executes(ctx -> replay(ctx, ResourceLocationArgument.getId(ctx, "entity"), IntegerArgumentType.getInteger(ctx, "speed"))))))))); }); } - private static int start(CommandContext context) throws com.mojang.brigadier.exceptions.CommandSyntaxException { + private static int start(CommandContext context, String name) throws com.mojang.brigadier.exceptions.CommandSyntaxException { ServerPlayer player = context.getSource().getPlayerOrException(); - if (BuildRecorder.start(player)) { + LOGGER.info("[BuildRec] command start player={} name={}", player.getGameProfile().getName(), name); + if (BuildRecorder.start(player, name)) { context.getSource().sendSuccess(() -> Component.literal("Recording started"), false); return 1; } @@ -66,26 +73,39 @@ private static int start(CommandContext context) throws com. return 0; } - private static int stop(CommandContext context, String name) throws com.mojang.brigadier.exceptions.CommandSyntaxException { + private static int stop(CommandContext context) throws com.mojang.brigadier.exceptions.CommandSyntaxException { ServerPlayer player = context.getSource().getPlayerOrException(); - Summary s = BuildRecorder.stop(player, name); + Summary s = BuildRecorder.stop(player); if (s != null) { Component msg = Component.literal("Saved build " + s.id + ". Blocks: " + s.total + ", additions: " + s.additions + ", destroys: " + s.destroys); context.getSource().sendSuccess(() -> msg, false); + LOGGER.info("[BuildRec] command stop player={} file={} total={} additions={} destroys={}", + player.getGameProfile().getName(), s.id, s.total, s.additions, s.destroys); return 1; } context.getSource().sendSuccess(() -> Component.literal("No active recording").withStyle(ChatFormatting.RED), false); return 0; } - private static int replay(CommandContext context, int speed) throws com.mojang.brigadier.exceptions.CommandSyntaxException { - int spd = switch (speed) { - case 1, 2, 4, 8, 16, 32 -> speed; - default -> 1; - }; + private static int replay(CommandContext context, ResourceLocation entityId, int speed) throws com.mojang.brigadier.exceptions.CommandSyntaxException { + int spd = speed; ServerPlayer player = context.getSource().getPlayerOrException(); String id = StringArgumentType.getString(context, "id"); - if (BuildRecorder.startReplay(player, id, spd)) { + EntityType type = null; + if (entityId != null && !"player".equals(entityId.getPath())) { + EntityType raw = BuiltInRegistries.ENTITY_TYPE.getOptional(entityId).orElse(null); + if (raw != null) { + @SuppressWarnings("unchecked") + EntityType cast = (EntityType) raw; + type = cast; + } + } + var entityStr = (entityId == null ? "null" + : "player".equals(entityId.getPath()) ? "player" + : entityId.toString()); + LOGGER.info("[BuildRec] command replay player={} file={} entity={} speed={}", + player.getGameProfile().getName(), id, entityStr, spd); + if (BuildRecorder.startReplay(player, id, type, spd)) { final int fs = spd; final String fid = id; context.getSource().sendSuccess(() -> Component.literal("Replaying build " + fid + " at " + fs + "x"), false); @@ -98,10 +118,32 @@ private static int replay(CommandContext context, int speed) private static CompletableFuture suggest(CommandContext context, SuggestionsBuilder builder) { Path dir = FabricLoader.getInstance().getConfigDir().resolve("creaturechat").resolve("builds"); try (Stream stream = Files.list(dir)) { - stream.map(p -> p.getFileName().toString()).forEach(builder::suggest); + stream.map(p -> p.getFileName().toString().replaceFirst("\\.json\\.gz$", "")).forEach(builder::suggest); } catch (Exception ignored) { } return builder.buildFuture(); } + + public static List getLivingEntityIds() { + return BuiltInRegistries.ENTITY_TYPE + .keySet() + .stream() + .filter(id -> + // getOptional(...) returns Optional> on all versions + BuiltInRegistries.ENTITY_TYPE + .getOptional(id) + .map(type -> type.getCategory() != MobCategory.MISC + || isIncludedEntity(type)) + .orElse(false) + ) + .collect(Collectors.toList()); + } + + + private static boolean isIncludedEntity(EntityType entityType) { + return entityType == EntityType.VILLAGER + || entityType == EntityType.IRON_GOLEM + || entityType == EntityType.SNOW_GOLEM; + } } diff --git a/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java b/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java new file mode 100644 index 00000000..decb44b3 --- /dev/null +++ b/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java @@ -0,0 +1,22 @@ +package com.owlmaddie.buildrec; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.SpawnGroupData; + +/** Utility methods for creating mob instances across versions (1.21+). */ +public class MobHelper { + private MobHelper() {} + + public static Mob create(EntityType type, ServerLevel level) { + return (Mob) type.create(level, EntitySpawnReason.COMMAND); + } + + public static void initSpawn(Mob mob, ServerLevel level) { + if (mob != null) { + mob.finalizeSpawn(level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.COMMAND, (SpawnGroupData) null); + } + } +} From 9fbd8cad4d5aa8932c2a36c860480fa72d538385 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 20 Aug 2025 22:47:26 -0500 Subject: [PATCH 04/30] Build types and folders are automatically managed now (type and build height: i.e. 1 block tall builds (bee) vs 3 block tall builds (enderman)) --- CHANGELOG.md | 2 + .../com/owlmaddie/buildrec/BuildRecorder.java | 124 +++++++++++++++--- .../com/owlmaddie/commands/BuildCommands.java | 73 +++++++---- 3 files changed, 154 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65adc097..aab764ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Spawns any entity type to replay build - Replay speeds can be adjusted (any integer, but defaults to 1X - the original speed) - Replay adjusts eye height (line of sight) so entities look at the same place as the player + - Record / Stop commands toggle (depending on if recording or not) + - Build types and folders are automatically managed now (type and build height: i.e. 1 block tall builds (bee) vs 3 block tall builds (enderman)) ## Unreleased diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 5ec55e74..5507838a 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -92,16 +92,21 @@ public static void init() { // Ensure static initializer runs } - public static boolean start(ServerPlayer player, String name) { + public static boolean start(ServerPlayer player, String type, String height, String name) { if (RECORDINGS.containsKey(player.getUUID())) { LOGGER.info("[BuildRec] start ignored already recording player={}", player.getGameProfile().getName()); return false; } - RECORDINGS.put(player.getUUID(), new Recording(player, name)); - LOGGER.info("[BuildRec] start name={} player={}", name, player.getGameProfile().getName()); + RECORDINGS.put(player.getUUID(), new Recording(player, type, height, name)); + LOGGER.info("[BuildRec] start type={} height={} name={} player={}", type, height, name, player.getGameProfile().getName()); + player.getServer().getCommands().sendCommands(player); return true; } + public static boolean isRecording(ServerPlayer player) { + return RECORDINGS.containsKey(player.getUUID()); + } + public static Summary stop(ServerPlayer player) { Recording rec = RECORDINGS.remove(player.getUUID()); if (rec == null) { @@ -109,13 +114,15 @@ public static Summary stop(ServerPlayer player) { return null; } Summary summary = rec.finish(player); - LOGGER.info("[BuildRec] stop player={} file={} total={} additions={} destroys={}", - player.getGameProfile().getName(), summary.id, summary.total, summary.additions, summary.destroys); + LOGGER.info("[BuildRec] stop player={} file={} placed={} destroys={} unique={} size={}x{}x{}", + player.getGameProfile().getName(), summary.id, summary.finalBlocks, summary.destroys, summary.uniqueBlocks, + summary.sizeX, summary.sizeZ, summary.sizeY); + player.getServer().getCommands().sendCommands(player); return summary; } public static boolean startReplay(ServerPlayer player, String fileName, EntityType entityType, int speed) { - Path dir = buildDir(); + Path dir = buildRootDir(); String actual = fileName.endsWith(".json.gz") ? fileName : fileName + ".json.gz"; Path file = dir.resolve(actual); LOGGER.info("[BuildRec] replay file={} entity={} speed={} player={}", actual, @@ -339,7 +346,7 @@ private static void tick(MinecraftServer server) { } } - private static Path buildDir() { + private static Path buildRootDir() { Path dir = FabricLoader.getInstance().getConfigDir().resolve("creaturechat").resolve("builds"); try { Files.createDirectories(dir); @@ -348,6 +355,18 @@ private static Path buildDir() { return dir; } + private static Path buildDir(String type, String height) { + Path dir = buildRootDir().resolve(type); + if (height != null && !height.equalsIgnoreCase("any")) { + dir = dir.resolve(height); + } + try { + Files.createDirectories(dir); + } catch (IOException ignored) { + } + return dir; + } + public static class Summary { @@ -355,19 +374,36 @@ public static class Summary { public final int total; public final int additions; public final int destroys; - - public Summary(String id, int total, int additions, int destroys) { + public final Map recipe; + public final int uniqueBlocks; + public final int sizeX; + public final int sizeY; + public final int sizeZ; + public final int finalBlocks; + + public Summary(String id, int total, int additions, int destroys, + Map recipe, int uniqueBlocks, + int sizeX, int sizeY, int sizeZ, int finalBlocks) { this.id = id; this.total = total; this.additions = additions; this.destroys = destroys; + this.recipe = recipe; + this.uniqueBlocks = uniqueBlocks; + this.sizeX = sizeX; + this.sizeY = sizeY; + this.sizeZ = sizeZ; + this.finalBlocks = finalBlocks; } } private static class Recording { final int ox, oy, oz; final List actions = new ArrayList<>(); + final Map finalBlocks = new HashMap<>(); final String name; + final String type; + final String height; final double eyeHeight; final double bbWidth; final double bbHeight; @@ -379,8 +415,10 @@ private static class Recording { float lastYaw, lastPitch; boolean poseInit = false; - Recording(ServerPlayer player, String name) { + Recording(ServerPlayer player, String type, String height, String name) { this.name = name; + this.type = type; + this.height = height; BlockPos p = player.blockPosition(); this.ox = p.getX(); this.oy = p.getY(); @@ -440,27 +478,61 @@ void addAction(long tick, String type, BlockPos pos, BlockState state, a.yaw = yaw; a.pitch = pitch; actions.add(a); - if ("place".equals(type)) additions++; - else if ("break".equals(type)) destroys++; + BlockPos rel = new BlockPos(a.bx, a.by, a.bz); + if ("place".equals(type)) { + additions++; + finalBlocks.put(rel, state); + } else if ("break".equals(type)) { + destroys++; + finalBlocks.remove(rel); + } } - Summary save() { String base = (name == null || name.isBlank()) ? UUID.randomUUID().toString().split("-")[0] : name.replaceAll("[^a-zA-Z0-9-_]", "_"); - String id = base + ".json.gz"; - Path file = buildDir().resolve(id); + String fileName = base + ".json.gz"; + Path file = buildDir(type, height).resolve(fileName); + + Map recipe = new LinkedHashMap<>(); + int minX = 0, minY = 0, minZ = 0, maxX = 0, maxY = 0, maxZ = 0; + boolean first = true; + for (Map.Entry e : finalBlocks.entrySet()) { + BlockPos p = e.getKey(); + BlockState st = e.getValue(); + String name = BuiltInRegistries.BLOCK.getKey(st.getBlock()).getPath(); + recipe.merge(name, 1, Integer::sum); + if (first) { + minX = maxX = p.getX(); + minY = maxY = p.getY(); + minZ = maxZ = p.getZ(); + first = false; + } else { + if (p.getX() < minX) minX = p.getX(); + if (p.getX() > maxX) maxX = p.getX(); + if (p.getY() < minY) minY = p.getY(); + if (p.getY() > maxY) maxY = p.getY(); + if (p.getZ() < minZ) minZ = p.getZ(); + if (p.getZ() > maxZ) maxZ = p.getZ(); + } + } + int sizeX = first ? 0 : (maxX - minX + 1); + int sizeY = first ? 0 : (maxY - minY + 1); + int sizeZ = first ? 0 : (maxZ - minZ + 1); + try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(file)), StandardCharsets.UTF_8))) { w.write("[\n"); - w.write(GSON.toJson(new RecordingMeta(eyeHeight, bbWidth, bbHeight))); + w.write(GSON.toJson(new RecordingMeta(eyeHeight, bbWidth, bbHeight, recipe, recipe.size(), sizeX, sizeY, sizeZ))); for (Action a : actions) { w.write(",\n"); w.write(GSON.toJson(a)); } w.write("\n]"); } catch (IOException e) { - LOGGER.error("[BuildRec] save failed file={}", id, e); + LOGGER.error("[BuildRec] save failed file={}", fileName, e); } - LOGGER.info("[BuildRec] save file={} actions={} additions={} destroys={}", id, actions.size(), additions, destroys); - return new Summary(id, actions.size(), additions, destroys); + LOGGER.info("[BuildRec] save file={} actions={} additions={} destroys={}", fileName, actions.size(), additions, destroys); + String rel = buildRootDir().relativize(file).toString().replace('\\', '/'); + int finalCount = finalBlocks.size(); + return new Summary(rel, actions.size(), additions, destroys, recipe, recipe.size(), sizeX, sizeY, sizeZ, finalCount); } Summary finish(ServerPlayer player) { @@ -501,13 +573,25 @@ private static class RecordingMeta { double eyeHeight; double bbWidth; double bbHeight; + Map recipe = new LinkedHashMap<>(); + int uniqueBlocks; + int sizeX; + int sizeY; + int sizeZ; RecordingMeta() {} - RecordingMeta(double eyeHeight, double bbWidth, double bbHeight) { + RecordingMeta(double eyeHeight, double bbWidth, double bbHeight, + Map recipe, int uniqueBlocks, + int sizeX, int sizeY, int sizeZ) { this.eyeHeight = eyeHeight; this.bbWidth = bbWidth; this.bbHeight = bbHeight; + this.recipe = recipe; + this.uniqueBlocks = uniqueBlocks; + this.sizeX = sizeX; + this.sizeY = sizeY; + this.sizeZ = sizeZ; } } diff --git a/src/main/java/com/owlmaddie/commands/BuildCommands.java b/src/main/java/com/owlmaddie/commands/BuildCommands.java index 3f25347c..1799b5b4 100644 --- a/src/main/java/com/owlmaddie/commands/BuildCommands.java +++ b/src/main/java/com/owlmaddie/commands/BuildCommands.java @@ -30,6 +30,7 @@ import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -43,29 +44,40 @@ public class BuildCommands { public static void register() { ServerLifecycleEvents.SERVER_STARTING.register(server -> { CommandDispatcher dispatcher = server.getCommands().getDispatcher(); - dispatcher.register(Commands.literal("creaturechat") - .then(Commands.literal("buildrec") - .then(Commands.literal("start") - .executes(ctx -> start(ctx, null)) - .then(Commands.argument("name", StringArgumentType.string()) - .executes(ctx -> start(ctx, StringArgumentType.getString(ctx, "name"))))) - .then(Commands.literal("stop") - .executes(BuildCommands::stop)) - .then(Commands.literal("replay") - .then(Commands.argument("id", StringArgumentType.string()) - .suggests(BuildCommands::suggest) - .then(Commands.argument("entity", ResourceLocationArgument.id()) - .suggests((c, b) -> SharedSuggestionProvider.suggestResource(getLivingEntityIds(), b)) - .executes(ctx -> replay(ctx, ResourceLocationArgument.getId(ctx, "entity"), 1)) - .then(Commands.argument("speed", IntegerArgumentType.integer(1, 32)) - .executes(ctx -> replay(ctx, ResourceLocationArgument.getId(ctx, "entity"), IntegerArgumentType.getInteger(ctx, "speed"))))))))); + dispatcher.register( + Commands.literal("creaturechat") + .then(Commands.literal("build") + .then(Commands.literal("record") + .requires(src -> src.getEntity() instanceof ServerPlayer sp && !BuildRecorder.isRecording(sp)) + .then(Commands.argument("type", StringArgumentType.word()) + .suggests((c, b) -> SharedSuggestionProvider.suggest(Arrays.asList("house", "statue", "pond", "garden", "farm", "castle", "mine", "trap", "campfire"), b)) + .then(Commands.argument("height", StringArgumentType.word()) + .suggests((c, b) -> SharedSuggestionProvider.suggest(Arrays.asList("Any", "1", "2", "3", "4"), b)) + .then(Commands.argument("name", StringArgumentType.string()) + .executes(ctx -> record(ctx, + StringArgumentType.getString(ctx, "type"), + StringArgumentType.getString(ctx, "height"), + StringArgumentType.getString(ctx, "name"))))))) + .then(Commands.literal("stop") + .requires(src -> src.getEntity() instanceof ServerPlayer sp && BuildRecorder.isRecording(sp)) + .executes(BuildCommands::stop)) + .then(Commands.literal("replay") + .then(Commands.argument("id", StringArgumentType.string()) + .suggests(BuildCommands::suggest) + .then(Commands.argument("entity", ResourceLocationArgument.id()) + .suggests((c, b) -> SharedSuggestionProvider.suggestResource(getLivingEntityIds(), b)) + .executes(ctx -> replay(ctx, ResourceLocationArgument.getId(ctx, "entity"), 1)) + .then(Commands.argument("speed", IntegerArgumentType.integer(1, 32)) + .executes(ctx -> replay(ctx, ResourceLocationArgument.getId(ctx, "entity"), IntegerArgumentType.getInteger(ctx, "speed")))))))) + ); }); } - private static int start(CommandContext context, String name) throws com.mojang.brigadier.exceptions.CommandSyntaxException { + private static int record(CommandContext context, String type, String height, String name) throws com.mojang.brigadier.exceptions.CommandSyntaxException { ServerPlayer player = context.getSource().getPlayerOrException(); - LOGGER.info("[BuildRec] command start player={} name={}", player.getGameProfile().getName(), name); - if (BuildRecorder.start(player, name)) { + LOGGER.info("[BuildRec] command record player={} type={} height={} name={}", + player.getGameProfile().getName(), type, height, name); + if (BuildRecorder.start(player, type, height, name)) { context.getSource().sendSuccess(() -> Component.literal("Recording started"), false); return 1; } @@ -77,10 +89,16 @@ private static int stop(CommandContext context) throws com.m ServerPlayer player = context.getSource().getPlayerOrException(); Summary s = BuildRecorder.stop(player); if (s != null) { - Component msg = Component.literal("Saved build " + s.id + ". Blocks: " + s.total + ", additions: " + s.additions + ", destroys: " + s.destroys); + String recipeStr = s.recipe.entrySet().stream() + .map(e -> e.getValue() + " " + e.getKey()) + .collect(Collectors.joining(", ")); + Component msg = Component.literal( + recipeStr + " (" + s.uniqueBlocks + " Unique blocks)\n" + + s.sizeX + " length x " + s.sizeZ + " width x " + s.sizeY + " height\n" + + s.finalBlocks + " blocks placed, " + s.destroys + " blocks destroyed\n" + + "Saved build " + s.id); context.getSource().sendSuccess(() -> msg, false); - LOGGER.info("[BuildRec] command stop player={} file={} total={} additions={} destroys={}", - player.getGameProfile().getName(), s.id, s.total, s.additions, s.destroys); + LOGGER.info("[BuildRec] command stop player={} file={} placed={} destroys={} unique={} size={}x{}x{}", player.getGameProfile().getName(), s.id, s.finalBlocks, s.destroys, s.uniqueBlocks, s.sizeX, s.sizeZ, s.sizeY); return 1; } context.getSource().sendSuccess(() -> Component.literal("No active recording").withStyle(ChatFormatting.RED), false); @@ -90,7 +108,7 @@ private static int stop(CommandContext context) throws com.m private static int replay(CommandContext context, ResourceLocation entityId, int speed) throws com.mojang.brigadier.exceptions.CommandSyntaxException { int spd = speed; ServerPlayer player = context.getSource().getPlayerOrException(); - String id = StringArgumentType.getString(context, "id"); + String id = StringArgumentType.getString(context, "id").replace('\\', '/'); EntityType type = null; if (entityId != null && !"player".equals(entityId.getPath())) { EntityType raw = BuiltInRegistries.ENTITY_TYPE.getOptional(entityId).orElse(null); @@ -117,8 +135,13 @@ private static int replay(CommandContext context, ResourceLo private static CompletableFuture suggest(CommandContext context, SuggestionsBuilder builder) { Path dir = FabricLoader.getInstance().getConfigDir().resolve("creaturechat").resolve("builds"); - try (Stream stream = Files.list(dir)) { - stream.map(p -> p.getFileName().toString().replaceFirst("\\.json\\.gz$", "")).forEach(builder::suggest); + try (Stream stream = Files.walk(dir)) { + stream.filter(Files::isRegularFile) + .map(p -> dir.relativize(p) + .toString() + .replaceFirst("\\.json\\.gz$", "") + .replace('\\', '/')) + .forEach(id -> builder.suggest("\"" + id + "\"")); } catch (Exception ignored) { } return builder.buildFuture(); From c06ac1e808c66175adc52991c901fdcfa00dc343 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 20 Aug 2025 23:43:03 -0500 Subject: [PATCH 05/30] New datagen provider to create index of builds (and calculates build score) --- CHANGELOG.md | 1 + .../com/owlmaddie/buildrec/BuildRecordIO.java | 95 +++++++++++++++ .../com/owlmaddie/buildrec/BuildRecorder.java | 79 ++---------- .../owlmaddie/datagen/BuildIndexProvider.java | 112 ++++++++++++++++++ .../datagen/CreatureChatDataGenerator.java | 1 + 5 files changed, 221 insertions(+), 67 deletions(-) create mode 100644 src/main/java/com/owlmaddie/buildrec/BuildRecordIO.java create mode 100644 src/main/java/com/owlmaddie/datagen/BuildIndexProvider.java diff --git a/CHANGELOG.md b/CHANGELOG.md index aab764ca..050c4bd4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Replay adjusts eye height (line of sight) so entities look at the same place as the player - Record / Stop commands toggle (depending on if recording or not) - Build types and folders are automatically managed now (type and build height: i.e. 1 block tall builds (bee) vs 3 block tall builds (enderman)) + - New datagen provider to create index of builds (and calculates build score) ## Unreleased diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecordIO.java b/src/main/java/com/owlmaddie/buildrec/BuildRecordIO.java new file mode 100644 index 00000000..59fd970d --- /dev/null +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecordIO.java @@ -0,0 +1,95 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.buildrec; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.stream.JsonReader; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +/** + * Utility methods for reading build recording files. + */ +public final class BuildRecordIO { + public static final Gson GSON = new Gson(); + + private BuildRecordIO() {} + + public static Loaded read(Path file) throws IOException, JsonParseException { + try (JsonReader reader = new JsonReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(file)), StandardCharsets.UTF_8))) { + List actions = new ArrayList<>(); + reader.beginArray(); + JsonElement first = GSON.fromJson(reader, JsonElement.class); + Meta meta; + if (first != null && first.isJsonObject() && first.getAsJsonObject().has("action") && "meta".equals(first.getAsJsonObject().get("action").getAsString())) { + meta = GSON.fromJson(first, Meta.class); + } else { + meta = new Meta(); + if (first != null) { + Action firstAction = GSON.fromJson(first, Action.class); + if (firstAction != null) actions.add(firstAction); + } + } + while (reader.hasNext()) { + Action a = GSON.fromJson(reader, Action.class); + if (a != null) actions.add(a); + } + reader.endArray(); + return new Loaded(meta, actions); + } + } + + public static class Loaded { + public final Meta meta; + public final List actions; + public Loaded(Meta meta, List actions) { + this.meta = meta; + this.actions = actions; + } + } + + public static class Meta { + public String action = "meta"; + public double eyeHeight; + public double bbWidth; + public double bbHeight; + public Map recipe = new LinkedHashMap<>(); + public int uniqueBlocks; + public int sizeX; + public int sizeY; + public int sizeZ; + public Meta() {} + public Meta(double eyeHeight, double bbWidth, double bbHeight, Map recipe, int uniqueBlocks, int sizeX, int sizeY, int sizeZ) { + this.eyeHeight = eyeHeight; + this.bbWidth = bbWidth; + this.bbHeight = bbHeight; + this.recipe = recipe; + this.uniqueBlocks = uniqueBlocks; + this.sizeX = sizeX; + this.sizeY = sizeY; + this.sizeZ = sizeZ; + } + } + + public static class Action { + public String action; + public int stateId; + public int bx, by, bz; + public int dt; + public double px, py, pz; + public float yaw, pitch; + } +} + diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 5507838a..bd72950c 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -3,11 +3,9 @@ // Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited package com.owlmaddie.buildrec; -import com.google.gson.Gson; -import com.google.gson.JsonElement; import com.google.gson.JsonParseException; -import com.google.gson.stream.JsonReader; -import com.owlmaddie.buildrec.MobHelper; +import com.owlmaddie.buildrec.BuildRecordIO.Action; +import com.owlmaddie.buildrec.BuildRecordIO.Meta; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.UseBlockCallback; @@ -20,16 +18,16 @@ import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.animal.Pig; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.animal.Pig; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import org.slf4j.Logger; @@ -37,21 +35,18 @@ import java.io.BufferedWriter; import java.io.IOException; -import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; /** * Utility to record and replay player build actions. */ public class BuildRecorder { - private static final Gson GSON = new Gson(); private static final Map RECORDINGS = new ConcurrentHashMap<>(); private static final List REPLAYS = new ArrayList<>(); private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); @@ -132,29 +127,14 @@ public static boolean startReplay(ServerPlayer player, String fileName, EntityTy LOGGER.info("[BuildRec] replay missing file={}", file); return false; } - try (JsonReader reader = new JsonReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(file)), StandardCharsets.UTF_8))) { - List actions = new ArrayList<>(); - reader.beginArray(); - JsonElement first = GSON.fromJson(reader, JsonElement.class); - RecordingMeta meta; - if (first != null && first.isJsonObject() && first.getAsJsonObject().has("action") && "meta".equals(first.getAsJsonObject().get("action").getAsString())) { - meta = GSON.fromJson(first, RecordingMeta.class); - } else { - meta = new RecordingMeta(); - if (first != null) { - Action firstAction = GSON.fromJson(first, Action.class); - if (firstAction != null) actions.add(firstAction); - } - } - while (reader.hasNext()) { - Action a = GSON.fromJson(reader, Action.class); - if (a != null) actions.add(a); - } - reader.endArray(); + try { + BuildRecordIO.Loaded loaded = BuildRecordIO.read(file); + List actions = loaded.actions; if (actions.isEmpty()) { LOGGER.info("[BuildRec] replay file={} has no actions", file); return false; } + Meta meta = loaded.meta; double recEye = meta.eyeHeight > 0 ? meta.eyeHeight : player.getEyeHeight(); double recWidth = meta.bbWidth; double recHeight = meta.bbHeight; @@ -520,10 +500,10 @@ Summary save() { try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(file)), StandardCharsets.UTF_8))) { w.write("[\n"); - w.write(GSON.toJson(new RecordingMeta(eyeHeight, bbWidth, bbHeight, recipe, recipe.size(), sizeX, sizeY, sizeZ))); + w.write(BuildRecordIO.GSON.toJson(new Meta(eyeHeight, bbWidth, bbHeight, recipe, recipe.size(), sizeX, sizeY, sizeZ))); for (Action a : actions) { w.write(",\n"); - w.write(GSON.toJson(a)); + w.write(BuildRecordIO.GSON.toJson(a)); } w.write("\n]"); } catch (IOException e) { @@ -568,40 +548,5 @@ private static class Replay { } } - private static class RecordingMeta { - String action = "meta"; - double eyeHeight; - double bbWidth; - double bbHeight; - Map recipe = new LinkedHashMap<>(); - int uniqueBlocks; - int sizeX; - int sizeY; - int sizeZ; - - RecordingMeta() {} - - RecordingMeta(double eyeHeight, double bbWidth, double bbHeight, - Map recipe, int uniqueBlocks, - int sizeX, int sizeY, int sizeZ) { - this.eyeHeight = eyeHeight; - this.bbWidth = bbWidth; - this.bbHeight = bbHeight; - this.recipe = recipe; - this.uniqueBlocks = uniqueBlocks; - this.sizeX = sizeX; - this.sizeY = sizeY; - this.sizeZ = sizeZ; - } - } - - private static class Action { - String action; - int stateId; - int bx, by, bz; - int dt; - double px, py, pz; - float yaw, pitch; - } } diff --git a/src/main/java/com/owlmaddie/datagen/BuildIndexProvider.java b/src/main/java/com/owlmaddie/datagen/BuildIndexProvider.java new file mode 100644 index 00000000..f97fbc68 --- /dev/null +++ b/src/main/java/com/owlmaddie/datagen/BuildIndexProvider.java @@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.datagen; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.owlmaddie.buildrec.BuildRecordIO; +import com.owlmaddie.buildrec.BuildRecordIO.Meta; +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.minecraft.data.CachedOutput; +import net.minecraft.data.DataProvider; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +/** + * Generates an index of bundled build recordings. + */ +public class BuildIndexProvider implements DataProvider { + private final FabricDataOutput output; + + public BuildIndexProvider(FabricDataOutput output) { + this.output = output; + } + + @Override + public String getName() { + return "Build Index"; + } + + @Override + public java.util.concurrent.CompletableFuture run(CachedOutput cache) { + List entries = new ArrayList<>(); + try { + java.net.URL url = BuildIndexProvider.class.getClassLoader().getResource("assets/creaturechat/builds"); + if (url != null) { + Path buildsDir = Path.of(url.toURI()); + if (Files.exists(buildsDir)) { + Files.walk(buildsDir).filter(p -> p.toString().endsWith(".json.gz")).forEach(p -> { + try { + BuildRecordIO.Loaded loaded = BuildRecordIO.read(p); + Entry e = new Entry(); + Path rel = buildsDir.relativize(p); + e.file = rel.getFileName().toString(); + e.type = rel.getName(0).toString(); + e.height = rel.getNameCount() > 2 ? rel.getName(1).toString() : "any"; + e.recipe = loaded.meta.recipe; + e.raw = rawScore(loaded); + entries.add(e); + } catch (IOException | RuntimeException ex) { + // ignore malformed files + } + }); + } + } + } catch (IOException | java.net.URISyntaxException ignored) {} + entries.sort(Comparator.comparingDouble(a -> a.raw)); + int n = entries.size(); + for (int i = 0; i < n; i++) { + entries.get(i).score = (int) Math.min(5, Math.floor((double) i * 5 / n) + 1); + } + JsonArray arr = new JsonArray(); + for (Entry e : entries) { + JsonObject o = new JsonObject(); + o.addProperty("type", e.type); + o.addProperty("height", e.height); + o.addProperty("file", e.file); + JsonObject recipe = new JsonObject(); + for (Map.Entry r : e.recipe.entrySet()) { + recipe.addProperty(r.getKey(), r.getValue()); + } + o.add("recipe", recipe); + o.addProperty("score", e.score); + arr.add(o); + } + JsonObject root = new JsonObject(); + root.add("builds", arr); + Path out = output.getOutputFolder().resolve("assets/creaturechat/builds/index.json"); + return DataProvider.saveStable(cache, root, out); + } + + private static double rawScore(BuildRecordIO.Loaded loaded) { + Meta meta = loaded.meta; + Map recipe = meta.recipe; + int unique = recipe.size(); + int total = recipe.values().stream().mapToInt(Integer::intValue).sum(); + int steps = loaded.actions.size(); + int duration = loaded.actions.stream().mapToInt(a -> a.dt).sum(); + int rarity = recipe.entrySet().stream().mapToInt(e -> { + String name = e.getKey(); + int base = 1; + if (name.contains("diamond") || name.contains("netherite")) base = 4; + else if (name.contains("gold") || name.contains("emerald")) base = 3; + else if (name.contains("iron") || name.contains("copper")) base = 2; + return base * e.getValue(); + }).sum(); + return unique * 5 + total + steps + (duration / 20.0) + rarity * 3; + } + + private static class Entry { + String type; + String height; + String file; + Map recipe; + double raw; + int score; + } +} + diff --git a/src/main/java/com/owlmaddie/datagen/CreatureChatDataGenerator.java b/src/main/java/com/owlmaddie/datagen/CreatureChatDataGenerator.java index 9ae8ec08..1bc9c2aa 100644 --- a/src/main/java/com/owlmaddie/datagen/CreatureChatDataGenerator.java +++ b/src/main/java/com/owlmaddie/datagen/CreatureChatDataGenerator.java @@ -11,5 +11,6 @@ public class CreatureChatDataGenerator implements DataGeneratorEntrypoint { public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { FabricDataGenerator.Pack pack = fabricDataGenerator.createPack(); pack.addProvider(CreatureChatLootTableProvider::new); + pack.addProvider(BuildIndexProvider::new); } } From 273715e3be2c8e7f119da45c30c301e36ac8e4e6 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Wed, 20 Aug 2025 23:59:53 -0500 Subject: [PATCH 06/30] Adding missing copyright header --- src/main/java/com/owlmaddie/buildrec/MobHelper.java | 3 +++ src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/main/java/com/owlmaddie/buildrec/MobHelper.java b/src/main/java/com/owlmaddie/buildrec/MobHelper.java index 568071ea..7d4323b7 100644 --- a/src/main/java/com/owlmaddie/buildrec/MobHelper.java +++ b/src/main/java/com/owlmaddie/buildrec/MobHelper.java @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited package com.owlmaddie.buildrec; import net.minecraft.server.level.ServerLevel; diff --git a/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java b/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java index decb44b3..9776e3fd 100644 --- a/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java +++ b/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited package com.owlmaddie.buildrec; import net.minecraft.server.level.ServerLevel; From ff48b597ced720b1cd39ac18a3cb4499a8a483a8 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 22 Aug 2025 12:12:47 -0500 Subject: [PATCH 07/30] Fix achievements code to build successfully on 1.20 to 1.21.7 --- .../com/owlmaddie/buildrec/MobHelper.java | 8 ++++- .../com/owlmaddie/buildrec/MobHelper.java | 30 +++++++++++++++++++ .../com/owlmaddie/buildrec/MobHelper.java | 11 +++++-- .../com/owlmaddie/buildrec/MobHelper.java | 30 +++++++++++++++++++ 4 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 src/vs/v1_20_5/main/java/com/owlmaddie/buildrec/MobHelper.java create mode 100644 src/vs/v1_21_2/main/java/com/owlmaddie/buildrec/MobHelper.java diff --git a/src/main/java/com/owlmaddie/buildrec/MobHelper.java b/src/main/java/com/owlmaddie/buildrec/MobHelper.java index 7d4323b7..3fcd4d5b 100644 --- a/src/main/java/com/owlmaddie/buildrec/MobHelper.java +++ b/src/main/java/com/owlmaddie/buildrec/MobHelper.java @@ -18,7 +18,13 @@ public static Mob create(EntityType type, ServerLevel level) { public static void initSpawn(Mob mob, ServerLevel level) { if (mob != null) { - mob.finalizeSpawn(level, level.getCurrentDifficultyAt(mob.blockPosition()), MobSpawnType.COMMAND, null, null); + mob.finalizeSpawn( + level, + level.getCurrentDifficultyAt(mob.blockPosition()), + MobSpawnType.COMMAND, + null, + null + ); } } } diff --git a/src/vs/v1_20_5/main/java/com/owlmaddie/buildrec/MobHelper.java b/src/vs/v1_20_5/main/java/com/owlmaddie/buildrec/MobHelper.java new file mode 100644 index 00000000..55db7d00 --- /dev/null +++ b/src/vs/v1_20_5/main/java/com/owlmaddie/buildrec/MobHelper.java @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.buildrec; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.MobSpawnType; +import net.minecraft.world.entity.SpawnGroupData; + +/** Utility methods for creating mob instances across versions. */ +public class MobHelper { + private MobHelper() {} + + public static Mob create(EntityType type, ServerLevel level) { + return (Mob) type.create(level); + } + + public static void initSpawn(Mob mob, ServerLevel level) { + if (mob != null) { + mob.finalizeSpawn( + level, + level.getCurrentDifficultyAt(mob.blockPosition()), + MobSpawnType.COMMAND, + (SpawnGroupData) null + ); + } + } +} diff --git a/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java b/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java index 9776e3fd..79a7a1fc 100644 --- a/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java +++ b/src/vs/v1_21_0/main/java/com/owlmaddie/buildrec/MobHelper.java @@ -6,20 +6,25 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.SpawnGroupData; +import net.minecraft.world.entity.MobSpawnType; /** Utility methods for creating mob instances across versions (1.21+). */ public class MobHelper { private MobHelper() {} public static Mob create(EntityType type, ServerLevel level) { - return (Mob) type.create(level, EntitySpawnReason.COMMAND); + return (Mob) type.create(level); } public static void initSpawn(Mob mob, ServerLevel level) { if (mob != null) { - mob.finalizeSpawn(level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.COMMAND, (SpawnGroupData) null); + mob.finalizeSpawn( + level, + level.getCurrentDifficultyAt(mob.blockPosition()), + MobSpawnType.COMMAND, + (SpawnGroupData) null + ); } } } diff --git a/src/vs/v1_21_2/main/java/com/owlmaddie/buildrec/MobHelper.java b/src/vs/v1_21_2/main/java/com/owlmaddie/buildrec/MobHelper.java new file mode 100644 index 00000000..b7a0bdc1 --- /dev/null +++ b/src/vs/v1_21_2/main/java/com/owlmaddie/buildrec/MobHelper.java @@ -0,0 +1,30 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.buildrec; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.SpawnGroupData; + +/** Utility methods for creating mob instances across versions (1.21.2+). */ +public class MobHelper { + private MobHelper() {} + + public static Mob create(EntityType type, ServerLevel level) { + return (Mob) type.create(level, EntitySpawnReason.COMMAND); + } + + public static void initSpawn(Mob mob, ServerLevel level) { + if (mob != null) { + mob.finalizeSpawn( + level, + level.getCurrentDifficultyAt(mob.blockPosition()), + EntitySpawnReason.COMMAND, + (SpawnGroupData) null + ); + } + } +} From 8270aece07006ceb21f98bf5efb7ed6176b57cf4 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 23 Aug 2025 15:59:19 -0500 Subject: [PATCH 08/30] New block palette to support builds in any version, and replays in any version --- CHANGELOG.md | 1 + .../com/owlmaddie/buildrec/BuildRecordIO.java | 6 +- .../com/owlmaddie/buildrec/BuildRecorder.java | 60 ++++++++++++++++--- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 050c4bd4..fd3b6424 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Record / Stop commands toggle (depending on if recording or not) - Build types and folders are automatically managed now (type and build height: i.e. 1 block tall builds (bee) vs 3 block tall builds (enderman)) - New datagen provider to create index of builds (and calculates build score) + - New block palette to support builds in any version, and replays in any version ## Unreleased diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecordIO.java b/src/main/java/com/owlmaddie/buildrec/BuildRecordIO.java index 59fd970d..2483fea8 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecordIO.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecordIO.java @@ -70,8 +70,9 @@ public static class Meta { public int sizeX; public int sizeY; public int sizeZ; + public List palette = new ArrayList<>(); public Meta() {} - public Meta(double eyeHeight, double bbWidth, double bbHeight, Map recipe, int uniqueBlocks, int sizeX, int sizeY, int sizeZ) { + public Meta(double eyeHeight, double bbWidth, double bbHeight, Map recipe, int uniqueBlocks, int sizeX, int sizeY, int sizeZ, List palette) { this.eyeHeight = eyeHeight; this.bbWidth = bbWidth; this.bbHeight = bbHeight; @@ -80,12 +81,13 @@ public Meta(double eyeHeight, double bbWidth, double bbHeight, Map 0 ? meta.eyeHeight : player.getEyeHeight(); double recWidth = meta.bbWidth; double recHeight = meta.bbHeight; + List palette = new ArrayList<>(); + var lookup = player.level().registryAccess().lookupOrThrow(Registries.BLOCK); + for (String s : meta.palette) { + try { + var res = BlockStateParser.parseForBlock(lookup, new StringReader(s), false); + palette.add(res.blockState()); + } catch (CommandSyntaxException e) { + LOGGER.error("[BuildRec] invalid block state {}", s, e); + palette.add(Blocks.AIR.defaultBlockState()); + } + } ServerLevel level = (ServerLevel) player.level(); Mob actor; if (entityType == null) { @@ -160,7 +178,7 @@ public static boolean startReplay(ServerPlayer player, String fileName, EntityTy actor.setInvulnerable(true); actor.setPersistenceRequired(); level.addFreshEntity(actor); - REPLAYS.add(new Replay(actor, actions, speed, recEye, recWidth, recHeight)); + REPLAYS.add(new Replay(actor, actions, speed, recEye, recWidth, recHeight, palette)); LOGGER.info("[BuildRec] replay loaded actions={} eyeHeight={} bbW={} bbH={}", actions.size(), recEye, recWidth, recHeight); return true; } catch (IOException | JsonParseException e) { @@ -280,7 +298,7 @@ private static void tick(MinecraftServer server) { BlockPos bpos = new BlockPos(Mth.floor(r.baseX + r.action.bx), Mth.floor(r.baseY + r.action.by), Mth.floor(r.baseZ + r.action.bz)); r.actor.level().getChunkAt(bpos); if ("place".equals(r.action.action)) { - BlockState state = Block.stateById(r.action.stateId); + BlockState state = r.palette.get(r.action.blockId); boolean upper = state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) && state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER; r.actor.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(state.getBlock())); r.actor.level().setBlock(bpos, state, 3); @@ -298,7 +316,7 @@ private static void tick(MinecraftServer server) { r.actor.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); r.actor.level().removeBlock(bpos, false); } else { - BlockState state = Block.stateById(r.action.stateId); + BlockState state = r.palette.get(r.action.blockId); boolean upper = state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) && state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER; r.actor.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); r.actor.level().setBlock(bpos, state, 3); @@ -381,6 +399,8 @@ private static class Recording { final int ox, oy, oz; final List actions = new ArrayList<>(); final Map finalBlocks = new HashMap<>(); + final Map stateIds = new LinkedHashMap<>(); + final List statePalette = new ArrayList<>(); final String name; final String type; final String height; @@ -448,7 +468,12 @@ void addAction(long tick, String type, BlockPos pos, BlockState state, a.action = type; a.dt = (int)(tick - lastTick); lastTick = tick; - a.stateId = Block.getId(state); + int id = stateIds.computeIfAbsent(state, s -> { + int idx = statePalette.size(); + statePalette.add(encodeState(s)); + return idx; + }); + a.blockId = id; a.bx = pos.getX() - ox; a.by = pos.getY() - oy; a.bz = pos.getZ() - oz; @@ -467,6 +492,24 @@ void addAction(long tick, String type, BlockPos pos, BlockState state, finalBlocks.remove(rel); } } + + private static String encodeState(BlockState state) { + StringBuilder sb = new StringBuilder(); + sb.append(BuiltInRegistries.BLOCK.getKey(state.getBlock())); + Map, Comparable> props = state.getValues(); + if (!props.isEmpty()) { + sb.append('['); + boolean first = true; + for (Map.Entry, Comparable> e : props.entrySet()) { + if (!first) sb.append(','); + @SuppressWarnings("rawtypes") Property p = (Property) e.getKey(); + sb.append(p.getName()).append('=').append(p.getName(e.getValue())); + first = false; + } + sb.append(']'); + } + return sb.toString(); + } Summary save() { String base = (name == null || name.isBlank()) ? UUID.randomUUID().toString().split("-")[0] : name.replaceAll("[^a-zA-Z0-9-_]", "_"); String fileName = base + ".json.gz"; @@ -498,9 +541,10 @@ Summary save() { int sizeY = first ? 0 : (maxY - minY + 1); int sizeZ = first ? 0 : (maxZ - minZ + 1); + int unique = stateIds.size(); try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(file)), StandardCharsets.UTF_8))) { w.write("[\n"); - w.write(BuildRecordIO.GSON.toJson(new Meta(eyeHeight, bbWidth, bbHeight, recipe, recipe.size(), sizeX, sizeY, sizeZ))); + w.write(BuildRecordIO.GSON.toJson(new Meta(eyeHeight, bbWidth, bbHeight, recipe, unique, sizeX, sizeY, sizeZ, statePalette))); for (Action a : actions) { w.write(",\n"); w.write(BuildRecordIO.GSON.toJson(a)); @@ -512,7 +556,7 @@ Summary save() { LOGGER.info("[BuildRec] save file={} actions={} additions={} destroys={}", fileName, actions.size(), additions, destroys); String rel = buildRootDir().relativize(file).toString().replace('\\', '/'); int finalCount = finalBlocks.size(); - return new Summary(rel, actions.size(), additions, destroys, recipe, recipe.size(), sizeX, sizeY, sizeZ, finalCount); + return new Summary(rel, actions.size(), additions, destroys, recipe, unique, sizeX, sizeY, sizeZ, finalCount); } Summary finish(ServerPlayer player) { @@ -523,6 +567,7 @@ Summary finish(ServerPlayer player) { private static class Replay { final Mob actor; final List actions; + final List palette; final double baseX, baseY, baseZ; final int speed; final double recordEyeHeight; @@ -534,13 +579,14 @@ private static class Replay { double sx, sy, sz, tx, ty, tz; float syaw, spitch, tyaw, tpitch; - Replay(Mob actor, List actions, int speed, double recordEyeHeight, double recordBbWidth, double recordBbHeight) { + Replay(Mob actor, List actions, int speed, double recordEyeHeight, double recordBbWidth, double recordBbHeight, List palette) { this.actor = actor; this.actions = actions; this.speed = speed; this.recordEyeHeight = recordEyeHeight; this.recordBbWidth = recordBbWidth; this.recordBbHeight = recordBbHeight; + this.palette = palette; BlockPos p = actor.blockPosition(); this.baseX = p.getX(); this.baseY = p.getY(); From 82958cc67530a74677d9f9942a2413e353294da1 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 23 Aug 2025 16:26:48 -0500 Subject: [PATCH 09/30] Add AFK protection during the build record (to stop long pauses recorded on inventory screen or AFK) --- CHANGELOG.md | 1 + src/main/java/com/owlmaddie/buildrec/BuildRecorder.java | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd3b6424..57f61ac1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Build types and folders are automatically managed now (type and build height: i.e. 1 block tall builds (bee) vs 3 block tall builds (enderman)) - New datagen provider to create index of builds (and calculates build score) - New block palette to support builds in any version, and replays in any version + - Add AFK protection during the build record (to stop long pauses recorded on inventory screen or AFK) ## Unreleased diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 43b0bb2d..6582db46 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -57,6 +57,7 @@ public class BuildRecorder { private static final Map RECORDINGS = new ConcurrentHashMap<>(); private static final List REPLAYS = new ArrayList<>(); private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); + private static final int MAX_IDLE_TICKS = 20; // 1 second static { UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { @@ -452,7 +453,8 @@ void tick(ServerPlayer player) { void addPose(long tick, double px, double py, double pz, float yaw, float pitch) { Action a = new Action(); a.action = "pose"; - a.dt = (int)(tick - lastTick); + int dt = (int)(tick - lastTick); + a.dt = dt > MAX_IDLE_TICKS ? MAX_IDLE_TICKS : dt; lastTick = tick; a.px = px - ox; a.py = py - oy; @@ -466,7 +468,8 @@ void addAction(long tick, String type, BlockPos pos, BlockState state, double px, double py, double pz, float yaw, float pitch) { Action a = new Action(); a.action = type; - a.dt = (int)(tick - lastTick); + int dt = (int)(tick - lastTick); + a.dt = dt > MAX_IDLE_TICKS ? MAX_IDLE_TICKS : dt; lastTick = tick; int id = stateIds.computeIfAbsent(state, s -> { int idx = statePalette.size(); From 6593423c47f972a1780048e4be7b2ec12f4c36d3 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sun, 31 Aug 2025 21:21:21 -0500 Subject: [PATCH 10/30] Adding some initial builds from owlmaddie (house and garden) --- .../creaturechat/builds/garden/small.json.gz | Bin 0 -> 14163 bytes .../creaturechat/builds/garden/tiny.json.gz | Bin 0 -> 7684 bytes .../creaturechat/builds/house/1/dirt.json.gz | Bin 0 -> 6261 bytes .../creaturechat/builds/house/1/logs.json.gz | Bin 0 -> 16819 bytes .../creaturechat/builds/house/1/logs_2.json.gz | Bin 0 -> 12450 bytes .../creaturechat/builds/house/1/logs_3.json.gz | Bin 0 -> 14820 bytes .../creaturechat/builds/house/2/dirt.json.gz | Bin 0 -> 14083 bytes .../creaturechat/builds/house/2/logs.json.gz | Bin 0 -> 28008 bytes .../creaturechat/builds/house/2/logs_2.json.gz | Bin 0 -> 20131 bytes .../creaturechat/builds/house/2/logs_3.json.gz | Bin 0 -> 40361 bytes .../creaturechat/builds/house/3/dirt.json.gz | Bin 0 -> 18703 bytes .../creaturechat/builds/house/3/logs.json.gz | Bin 0 -> 40907 bytes 12 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/assets/creaturechat/builds/garden/small.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/garden/tiny.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/1/dirt.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/1/logs.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/1/logs_2.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/1/logs_3.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/2/dirt.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/2/logs.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/2/logs_2.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/2/logs_3.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/3/dirt.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/3/logs.json.gz diff --git a/src/main/resources/assets/creaturechat/builds/garden/small.json.gz b/src/main/resources/assets/creaturechat/builds/garden/small.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..c899491ebb773c01ecc239b0618398f22b58ddf1 GIT binary patch literal 14163 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/house/1/dirt.json.gz b/src/main/resources/assets/creaturechat/builds/house/1/dirt.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..dc751f11b4e651679810afea3c1003ba2d07c14d GIT binary patch literal 6261 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 fu0fTy6HKC_l=L&0Xxd}@r|c)*J-J<{i({K~; literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/house/1/logs.json.gz b/src/main/resources/assets/creaturechat/builds/house/1/logs.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..664f91c2b3a4dd503ebf0656b4f26f2f149fe48c GIT binary patch literal 16819 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/house/1/logs_2.json.gz b/src/main/resources/assets/creaturechat/builds/house/1/logs_2.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..c20cdec3b5d09bae62d9cb548697ef9b531fbc50 GIT binary patch literal 12450 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 BFaH1l literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/house/2/logs.json.gz b/src/main/resources/assets/creaturechat/builds/house/2/logs.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..a7c97b99f141c0fbfb553fb349d91e34f6ff4a65 GIT binary patch literal 28008 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/house/2/logs_2.json.gz b/src/main/resources/assets/creaturechat/builds/house/2/logs_2.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..0288fb8b0ac83e4753676a2b60fd1576dee50102 GIT binary patch literal 20131 zcmV)2K+L}%iwFP!00000|INMGk{m~pCHg*}foEih>0%on=V|U2+?lD{(gIeYt0~PU zIV4J|t4I3Z?>XiX?&eD(1L2mLz)Z8SBoHUf)O6RQfBJ9#_rs?@fBEUxUq1Zs;om=h z`SjuAhtL1`{69Yb^w)p=^5F-$Bn|C{=Tt09`uO3;AOGK<{_^EtKl~6bY5Fs@;!h zZhw3I_x^ML_tszVANw!hnf&R?KYsf>eZ`Nze);n2zkmGur!Sv>$KUYG>u-1c)xZ4o`uz|I&TX$KUv6FTj53qyF~WZzDcx5w_Kb{T-6| z3Yfn9{$m`mc|@ zn)>b6Klqp1$NfKlfgZxo|6}@}e@y@LpY4DC@}((;-~OYiDpm24JZ6ij6sVHl_{A4| z-EaKzOAT3L(HKI8uA(mc$EUx4_~EIBizP9`*ZuT`%Ed0(Yt2a?M_9H>3oBl#1pLhzm-?ju*9ll<$Z4tcr={9X>wOOkS5`%&llXFp z5c%Todx!X300zNsYc&wgGvpMa7@)hjW}AJVHv)s6|5*tpVqB9{rS_Mk8tS<*ON@Z zbOx3LH<*A9|G&ijHA<;b#H-+vtJS<5n3Ri1e~(cwiO5bIScePRTBW*K3Am~~t_ghe150ooplCW}p%?KXmgod7nr*z-UWcX2`yLe`nbop_R2T%?yTZa_ z3?BLm%$E`RK^4MmrZE|w*;dw{@H41Vm`k7uswGFcDO8C01u8bh{-g%!t$;@pWAser zMH6`d42W{5!3*?At!X)`5a9WRqzf>ZAo_wsfVoBwQUjh8jwWmk5P@knh)~6Cg<7gF zwlU{R47Hu|6__{MIK-1MsCseerd(=M+S`kya|MGfFpX5JvS@lJ_8eEZY;q+|!{Tv3 ze+R{FRtQ+4ec1?oluEGz@?i9Z7H#UV0X61Dnc`m?6KeohhEzj|!!)|%N$wwGrdY-H zXC>FHy^+>K5PB6{AY7qni~lT_xIV6OF{=0zfou

m*$CyLHcc2R{s0E2t7U_cj%;T!>3k&{pjiq+5;^e5lFRYX=@Vrav zm6x1@RZhdg6JQ|X)}wNw+FHeDe8H!$y7gy}#X`dvJ7iE2=gw;cDp=Oqqw-2iTFU?x zL8l)2fzUKZwS!m|SECk{fo@7RTSkCLE{kY6{;7K4oW5|0#~wX!;hE!eL#F1c$+kv_ zHmd!AW23Z#HOL}5w-PEOv7 z*K@cs4WZTr)=j0JcT&&5f?)(KFTK>;nCRyWEP$vQa|a7i#*y-RTUbwk;UU9aT%=1! zMT<~gRa0OFM>V;Yc<>r=EC|6rox2_ z;k?&H?Odb@Rl;@9Gv1mXCQ#suW+pXptT#-ED=n@7F-q4F?bfA*^MXvjad3 z6jRV#SrA@)W_rkB1E~2AClWRTP=)<5ohYxAIn0MXfF!}2Ov!_80l_!O#bJy97|$T& z_$`3$%A4vor=?W{Aw2Yrd#T%CM=l1R3DONGk7FS3S&-k4W3+|J+#>H{b zGB6kUAgDrDQ*8ldhJWiF0WsmX_J-+3jX)F(F@si1qA)LI>)N##Pm4x`J(TF+Ga`Y5 zDNT(ohcrmYnP9{}RYHkUVA8_2ItPPTn4x6dumIZ)Rmt#!`{++6Ej7g1MC=Q96JpV# zPT3n6oD+iNTJeP$M#%a|2BFI;P>u%&bzfp(t<&iQ<2$x$A$p7B_PCf2mBizMdsl8% zUpR41$h)+HLwSw}MuR%Ma(tMiR1w4P5t&Ft)DHI-hk7e6DXY7EY!%~WKw?jrV$FDO z3IR{i;%wGf^xch%j07r`Lxq= z1`?YkCk3$>z4MJcClE1gF{p>-;c2=Is6(8DltL*Tq&&j1oPkuRw&9=l_x8?^Xk6CE z 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 W@@W%J{jdM_Pyat%2dAI^)d2vQW3|Qr literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/house/2/logs_3.json.gz b/src/main/resources/assets/creaturechat/builds/house/2/logs_3.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..3c6058a50a60d53cad4abc9b0d852f34a268df34 GIT binary patch literal 40361 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.WPc&u9@9l=dB5OwsE0xO0J`aWA*WvL+ct7Q27nXn5m9Sp z^&xBCYhy=}&pYXfm0j_(;8!Atx@mYLSR~AZ0|qBS`PsX72*(~FiID`a6{!6^V9AB0 zaQX`H`pnACnc#C!@qk9G6jkLG(ZV;y#j$d{)o`HUweoS?jY9H<;i48QqLG!$`DedV|9~O92{{S@k0ly zwZS!RHRqj}BDdPBP@{r_`dQ+VVqzY8m7GY!`azUs?}l9YIHpyK~Pne#IBfHP^&MMu&=h zE4xzt5a*smQYpjEK8{4Y&CRse>CD#$axk6&PkV>jJ~U(qo&U2+CQJTcS|r4#K3 z9G-Y+oLcoMuz_Y#TqT3l2G`_555dL!vIImP2#8|b8wsW!l2Mcu%c}5mWL5%aYXjop z&}CL7OulOUwsXI(bA5`*Rxw?0_g&Sa#s7RM-b5*Pd!wMJD&n%b=i zV;(jXxPWk2D+0)vz8%-SnztRTn03fVZ5rV4z=r01w^4wfN{3p!`8*5v=>_I5uZZ!_ z)*j_7sCYq8TRRDs7$=6EAz|8+pwfkG3q3VJ#M%v^VtID&;6-jKjsuHTCtx`R%2oyK za#|SXG%N~6<*RD)Ts013v`%PBHL#rMF;U@uXRi=OY9lKQREwIa%A0^?mR?XHr zMh{>~XpD;p-h`qI%b9ls=(&si>7x!Rgq{M%Z0~Ap5)aQXit>OHO; z(#qb>Kt1xg@%D359+Do`NP=>DEf!hXrzqhOb2_W=Cgw?)Gs%nX;sINAx>6}-AaQyo z*Ef?i@sJ4Vo}x}>&9d~`L!k-XkAbC3c{?Q&qUVL5{0@uC3%Wm4Na{B=UwOeFoH%_s zf|SPifKx+}aa^{AhxHD7P751pV5l>%@Y_~ePWb=6ad=V`II*4`hYCy-snsl2r5NN4 z^N>S@>zv^3;uR1)T{-n2*H<@}v)Zg(B8olN2rj92gZ8!l}*=O{iq2e zuhyaM5={RnUlxsK8LcV0F*h?m?0=};oUR26BEV#l%{{FGBENq^5>OqdW zx~E{R2N^jBdI0N{Cw?<97~T=tJ1>~x@~vUnp?>rG!fG!T@A48Yxcw$Ya;TTO~ST)@^h1J zjS*jQDnrjbQJ+)VaEIU*_%w_e-V0a=sts6?N%LR>Di*4x6=NogVk_^0N=s&H03L?~ zyGfz-s8Z7^fMb*@wX$YZg+5$Qex92o=onCRFtI;M+bZ)DKLx#ZeWxt1->L~08kY)D5ZvjiD7HB|&MSh;w6 zsgC>G7$gR#f>0Y(#R4yF{0Lg95ZmL7qn3};{MajjM~Ky{JFlA;fVvj&`&sr(b2FJV zGg7+(3L68N_!uMfVl-fZs)*w5NAG;hG0@IQy~^=T6a`epQSG#|aN6jtjoH>xsr6Bu zjSn%}oLIZFtCTt}j3oOQl5=(JT&ouR(-lrr3 zSQ@jb43{I!drVk4Jm0d-vhiWgSd`59TPZqs{=6lsChZ)V0b#NfcH)(610`vcULAe{ z+|@E`ec_t28)E6FPq_uhW4a;S6VK02h?mK+A~_-+LJ!C0`$9In10tV~t*2=gQu zHAYR5bvuQjPj~`~ZSE^f18`LFR!>|erW2LZM3=`sm&pc?Y1I&+AUw5n@%(IU8>QKB ziSaa1%9id)*Zx*$@s<~>;*gGkWU1AXf)y_ZIysf2(%4owLRGtQ8}`1ff~sOeCVEdm zq8z}Qbj4e#V2}vr)3EG9QOpijeREj%M|n@9d@S9uiSq8_JdE<*cQNf%kcUCu_qO97 zauCY^Sk4N&cChlOY!;6f8+Wy`2M#gzHlAu7J51lgSrXAsz$40rwe~%yPzYv!$1?adT%`Jx_eopA$#kRUO|P*E_FPGlLyrB~F%nyOi`j zllC+&vJgnmhl+>F&aIXWKt;)#CXu|L@^h%lkds9!@6;9cCS}+UK3X{68(5R9=#aRi zvJb+lM8)Z~{SY)lIv0<*jX|ec-UpR3c4LDlgWxLrk@4uX@d=38@I2en>ZBv+-ko@S z4WG*bHIb&oWRTSy;vN*6F%ciLzDeHo&Nys(aHhfRA=Ac^;2PJ&K?J4*4p9j^L=ky7 zXAZw}ROB^V5lWCWmaqn87oT4(ocFMpERqw9s zVJ?0Q`GBc}jXzbHw@#rKm1kqW=RRwaTC>Q2gg`69klfCUDUkia!fRPVX%i+0D}Zbs zd8U9?nNpu~y93><5o?uHBq-I6@$LjU0EJ_YXvhM&G|lHMwa1Gvj9fs)L8l5ENIaZD ziX6X7+maS}((%caGrSxz?z^XaqB`9g(k-exwqb1=!pc!5EGBjlVR?wv4`2yyUP>|I zhO*JBya?;$4%!VB<*F$aQv(~;cQP@7qn*Yn5L@R+DH(R$aTwtU) zZ;B9GYqIR=y+BoW3Q4B4cd!D*$t#~iYEt6hEj$hN;?|_T7oVZo_45s%CqQ9cR&&=e zv}j_$$Sw|gc6=BtQHB#dk4i12KT-}*a-3zWoUDkHySS{jyz>wX%635bzRWGd6y2JM z?G7U$i%KwQBujR0lh{i$cAD5%m({ZtA`U^R0heD=W9B5$zzyXg=ild6oN~^QJC1o= zYR(~Ef~}g|>Sf|}9`J6%+k+`n5)oLPTGpnOoKeB10Ba^I2(Ig`V8rIhGO;Pov2_{J zStx!m@*wM{QT4gz1h|krpV$l$F*_t!rb;;@IqUE zz~FPqkREZ4YAKEhTQ%_v#Z(VK!F9z6tJ56${0?EETAgY!WN8>Eh1Y}-17SQn29~y> zHLrDEj+ab2U5vBwbk!Fio^b9r&5G|2^O>c@dtM4(Ys3DsR zsy4eQS=mNR6>h1j2eNOlp&3hy$3Ygs@?t!)S-O4@YjNa&$wuxdwhUgilXQ?|)(5D_ z@y_P|K+>MMPlbZ7)@tD$!O;-6dWs@p#4>q#8tc;@Rf3-bv&A7pJhSmVK#@nR^3y~j z0=A=8F?Cmm$4?HVg^8mS>ZW&~rew|tZFhu-?-phs5t29=_mG)w4$-+UjB?u*( z8x8M)|Go#NFcCBcBuU6EHopRk>ut2FnIDs8VqOqeyKFT<`?)0`}ntMCy zPLRuD8@n7KVFNp#1i`fgy3{r5j`*@66F`S=#Ga;>=WeeYYv{1b0rg2kd6$>TR=2Ikc$inkrY&^z)OJF ze_$t=K~D@XD=ChbZr2Krt(U)+*fw#*v1i@a*dXc|9~{e}AHiec741*{8suUuW#iI*pNn{=Yc8Bn8&c{h8&a!I|nRH zy7a4JQTPsk`7}PgxI1p``5qFiDLjV2ObL8IPlu>1P5k2?E&95LDRunux0vWb6Q-}2 zFgA}DX6o?iAszWU@GC3>e)1xrE(`_DRFJT&_jkCF`NWDXHVl7oXpA86FD&fFXj)w` zCHSMPSRy#G)dL=D=Z5)oBZq8c31o99417rPb>!~^!_CV$dvvF}aLqRkkI9rge@j?* z7z*yAbdtj+v89wWs@41$qTLz#e*q!@RfreX(bl&E;rbAN z=pMq?Paq8%f}2jqIFFFLA{k!}6)qsIYlMxkJ*1|Z*|=Z>MJ;O=ofL_E-7D_*^2r{$ zN}fHKE!LFMK@B2tCKcZlqc;VXxtbaREC}9^RU4g-X~CM#IVmw&gg8LOOi#f@5UIR~ z%R_)zUaV^xjoO~45p6{+!4I#GBZ~!h@Rs6S;Yz|b_$=H>>Ebv>{B7b78RYFhOpZsfC|D4JujBX>l^)VRZk zob_ixO=GDYD4l3lUt<9|_Pf^g<#&Mf&T9o*tfdEKV|d6<9ra?Xm5RsG?~etN>p1en zS~VG#eyu`Nu^PZ!eF{{hD0L4?kLa`?%Nvebs|16|E$k?AC%0yRM)(O-sqJ9i*{ct| zg9oT`63_Lu|7 ztlm{=y?J{Bv|fb7izR0k+evetciBM(j;+@!sI*D$xzQHCmx4@PU>_!l>n@SSgL3xN1n5YZ0 z>?NZ$eA|d8{8>zqFXc)U%|io~hBy{jaMIt^63F(yYsL>3!>_|3geQjZu*jU;jkubu zR`!;bkJ1oS?3Z=+n`w+<+d2lEFda>I)`z&!w`Q^sDIW2v9D$lDm&G@}c?` z6!Xm~v-jC5_1b10YGH09#a?~cnqE~JanzO4IHbKMTUN4dA%iT3_^6hd$GXfznG}4r z)%K-H=4b#YMD17voEjtXW47IhM^QnM#8Q`6zfv)!w98cEuHn+!V@nR5th4xyzB`Rh<6q zP)$DJfc+!&9o%|cZ~UJXY|{Qz8TL)u+!t1Do}LS7yHrzUC5<5oVO29x6gsC_m958Q zOm~-z9ZqDUm?N{y9K?E7JB`uvrsM)AC1A2cEBG9M3NT`x@%bEaT|nUyl+?Lj%)kwC zo(W1=%*Ao1s)Qfx9&dt>l+UPKI&;^i#HdVGy-H&|dJQ_T6|7FK5yzOCo0Yb9=9i>BU6wh5~ zT@GEX$4o`onu@mv_O9zSktn3Dy*N+N&L~uPh@E|btm$vFsO-S0SdxSjUpcwjuT}QG zl`-e$#E&`g992Sim7JM^+klsBTSjD#6a(F60HromA>|cL`h`P!v$u5DvWjrgyE!35`^?-7AiRV{h2w`)EJT+V0j=%gEpi$Z zWq>C_33)`9GE-%cJ;zelMw>#*Ne@ApL!+r{r4033O1jtiLi7#ejFqF-!7?uQhNvRl zJ$M+bg;|(;l{%90kOf9IL15R4HNM@)VDAGQUMEBB(WRwn- z5>$?Gd{HjVjM7Wj2y`v34_K_ksE3SSY`o)8;|>-441`UUqP`*8Oo#uurcFb;NV6Sd zKt(w6F5Zs%USu*;>;u-UL`1%2F=*c-Cb(Z=J%=VOd$S#B-zf;c=v3QjSO`s4&!ohr z&3>khT~342YtLJcs-8r3dI#BE?XfcoxmAmIbI`Bt;5z;Y?*;39*DAG)V%M9F<9@V* z4<7e0>bRE+wXC}mXlS=^fDGapOxxGyG^q3D>Ijs5ybWFVoTlb1jIj!dbhCW=sAW0R zx^$?#(!pj&HWsE;6FSSjS}JQnZSVp9vtN{*G_~ngmbpW>hox{211TlQm}~i+U_}XJ z*px(Nvba}(f$K@m%clRD7ZsDYg_T%{!bWOf;D&vC}3iJHP)gUe@`6rcwbqj(dsKSAKqg&1}3 z^-(H{6c~btY@!Xh!E#*1*rj+sOKPf7BSKlYZ;T?`n!Jk1Hl83MnS?-+SD27emq=D4 zMS%Ku=QCUfvX$(X>rSzMJ7FoSJ{KH{8W5K^W~AmrHsI_>lD;Me{JG+ghwYtgoH_V(glMLcm&q-$_N6>_9|ypq z{Iiz2!qrUJkZq(~GtO_yHR%3IawITudMmW_&BD?nn&Hfw_M3PmULHW^Wfu-2Im<4T z_wcPRAtW@(FzW)}1#Yr*r@mf7Kj{B?;@^;0{L`1sM0hV;&n*_~hiXREHkWAotJvuD zV5y|mtNi+cimjI`RAaP&&!8e-sdRl^L`8awM?1T=wMwXlUxkAlXJ0T z877OQfZCjS-=F!%b}_84;@iV|&YTKq>XmB3@K^VsNW_*o4^EllQtHOc$1SM6Iel!k zHi;wxE0&Ek+M(>Slf;z;jtyzxGX25Ub<|?1RWe4%C7ytlxr3eT6LzqEJSs#2-utGwhEhkN!U?TLvSr6} zRu0}BMO;L?m0;neD-$2VVu5jlIu900L%?&wrv-*F?!qz_EKDg55RInvg*{AdTycaD z4q$OeBjn)JXOH_Rmc0m0aqvh>y6AAi24C_jz|g6EcFZP{ewsaGk*xLyF~1__DI~C_ z7AQqj=K2N6MtYaot~rvAg~cxBHMvJ_gRmbAi#jf=1UQ8e+Y%5 zy;$!#fh)xgE-R;NoAQ0aT0)FcNr@pfk;6XaeL^Y1M{whpGsPShSb=H5(Dm|bYy)QU zsOsR0RiAf3_S}l40)s8a^qc8E?HsG*0Bb;%P-cMV1g|T+h!rk)tgRnnz7*|v}9`!TJlJH@iZ<`u2)$Ns>^qP zD;UDFzILW{DxM9=!aa?PY;>D)(B$iU>~b}h<-QtZu}uu>iKwx?ZD(=$rb>lZ(ua(I zo>K;|Bt%+SE;VUn#-p)MSRX!lXss;&-DdeByX&psUQp`6AQ^Ts|<{rTKt*?CYA zkuDnmUP1M!m6uV#;3scSyfrgFz?wdf${Ky_YPsU!J$&%-*#xtLft{R49fE3e6;FdY zZy)d?hE+SGmuaB?IU66gHvWxqJ!-dxnTRTzKBYUj-t{8B0jTpQivwM&B*c}JCk`lj zvgaXkv`(yP7a#ZzCBR8^j@3}7cBW-uO1%1t$9dvs9mC6DB!9!Z#`#LhcyC}4Hzl4J zg#~s_%tbR#JntQ2Uh-+;^RasvUVNrWM~3jhLJVX<(0b51rR-&l(Q~DQ)>5CsuEkX` z4=+zX$%AmchWPZR9;_rBMwn>g+W>>y*oF`>M-?i|#tzoTWzN$q%XvpWP`Z<8@DOw3 zjmUdcMhXidPq^XW+v3S{;c5g=FxnnX|Ot5yy+{B&+M_Q^t9~00NA?h>_qU=A5Nw+c$P8UdB>6*pel733{Lq>29M- zE3S_?T_#j7XMcoYKScIv%EXz7l-O3B%EH?URB6+S;EiDANh7hTnQ@}XngBzkP(V!w z5kD)V_(CjLRWG*a6-kbp?r@(b-_G;m2MPgD1*1?yvoy`oEvz89I)yy~6Te;4U{0H3 zpTqI7f9yhT=mr5zId6kUIYcMv)ec(lLPh(EQ|2MZMMgt%-fSj`vg)Z7lMWVJP9y2M zaFX^;Ven*8;et9m)!KQ5gq2qQ7k73Dh2IHS!fuz+v2ETVJA5=cJKD!5k#WcccQkaI zo8GcrXNVZjZ{#=NdN6Vvxn^NYGs{dS_wz#!KVFMVwVu8*)zh$+#|R%-ITctfi0ZD5 zkYpRiyhw}*AsfP{S~_5BUd?N^euT^{`$4c)*}ZV!((=~6Hnxovv~{CO_7E2uHWCKo zUao-I-iSS#C)}K|&yAV8B1}Z2#^JJEYj)E*u0e}yd*iWkGwD)DyHhsRjn5x40!s+i z3e}nrzwTWN0!u)RW0tUbxm4+JQOjNi!dmt+H9pedZSW_gt#i>}B{I{H4Ly0N{c_oA zEAPO1Fo$`tmCr*Xg=Fb_p$B-cG6I<4_?NZ!+PGzhsIfnd%gK3OI%h1Mj&TQ|ishng zqn=SkYX@uuyJaA2eTapGn~B<12i8?c@fFCzlSn{)a}pbCYM&wptWl(!a*3>>!6&5z zyy-y?rerLnv-Fq9wm;9FHTk64k@1+0;&T1mM37<oQCwjogbDU$P_~Lr8ri$YbOgSiv`XzA;`e8`qO~J}` z?HdTe2V&wl8L2nP1AH)9e~d{&zQpNDE`cq>v=IgiE*!iqCQk7ta){#q6;2(MBO_UF z%R(hVq35t7F4keNn5A_@wW1WRwP{B-xTs~}aQG~2Lr8y6IxbbcJ*?*j>Uln3zpI+X zn*`&k(WSssgqxU~257y1w&E0LkD<5-9HTFWx6nxbxrdc0H3-vRI2)8#OC{1VJ*6p6 zIE1i)LL@NKv|3oP1AYraWdkbt`%D*997qkNV|tpx>@leD(q%r-q~`U54P6FRCV z{#jfsAK=bVy$a`@6rc(AM-I|(a40B{Ouw{vSl;M#!BOjcKfD&4z_t||^+1ub0gFWz6pLNe9Gr=L$**UJ4G4HXmuY|5LAXc zWkpG-smrrdIm+LyluV@SKE!A}4XW~3b!jA$E6Z|Ty)vDg8EpHMRBaF=y$Wf)L{S&No?;Co$nD~w)U4Tnyg0)%fs+jH0H3Ko z^ViR0&w_8jCbl7Wgx}q3q`NzsCu-UJS|D_MN!P;3?}O~A6>FIy+(m1Z(PBGXrKxe? z3R~5*%p#~<*eRr49W76az(Y_)&3LyXV~dh*srnTvssRLijjRaq-B3kRKV=Rh+#G!N zOd+VK@NeLB=p@lIVZ^15W~n52Ky~I7{~Rv((UR1T$N0+m|BVlVtj?PBI9;fFRVwu@ zXQ1o5YBSCfjrkuAa4#%v>=L76x1zMSbHa7j9lJoc3QM@H%wZxV#ndz0$H7@3h}bjY%d3} z+|i_d5|xHP*25gw>bO;Nq;e0UVsglnf~<|pyaOt{P2|n<(zrNo5oV7HfeU4V8ThI% zp_~>R1o+^c^wKDizt2rJov%IBc=@33P^F`(h0Zed0YS)OB+kH5(!5u^j z#Zeg%evpSnBJEIwmShF26>4UugDva$1`xZjfW41c7MRbfI}taMGEvN?kOj4dRm}#G zStrctIX)iTgUmhS|dr0o!vQh ztBv%aa%M0pZ3x6u$J;?Ek=S+blqs$oQg&0^0>tf+K|v~8X-=Rj%x@RgB8po_%}k0_ zCXjnr;lROoMaF7k|#U7YCstSikWhzryW-<9$1DH zS&hpGgkqL%RHo5Su3x7(8LTm3IS1|87N(X$Y5eRPVDN_!uQPp(xV%djG;VDQ((G^z zDa#jf?NR-VjW4(SSqBS|N2Wcax)mJUj&c=m_lk2hO|pd}mP=dL&GmROslQz=;D&)$ z#D0kzdYvYIfsgL$^M<8DwihT z(Ld9JLZfpBE3E^zsr~nZQKcy@ym(rnrE&&%f9FtX%4LPB9QgL%fU3->r~{XjV>xu~ zw;IgGJSHqaYRoFBYpj@1by+W*vpC987FJ@o8qq15vwsfp90l6X5}al7{;;o?>Pm_^ zhVDx!vI64{uh-H}ZJe((#X;CDfcK{a|L$S&q3n-)oLFbaM&g)JQa<%@D2@ zJJ&tYKXB3?9YhwmfeLq~c#fsdG1=6kxZtvuh#o_5r&g)3oT(_Od$%;J!0&T*l2(M_ zoEs@>A@H&~SLS}j+erV58BvnQ(*XbG)!J}a~Luv)T< z-3m&HH1C&7!s^hMsaI!!i6Aa#-<70_1(@ZHR=C)ffE6qBq`r4lVRPhDnmQ529IYo& zwQOnWDnCF~&$(LVG^oJInNquVI$1mKWq=cp*MeGjtPN0Dy~cxU7-1j81Qx4OGh{Aa z^PR3S_XkVH_@z{+%ZXZE9VSI#-_L-JbaQk=pmxa8qNIB9V7=5R#=K9X*7M`fauOK3oqDM zoCNGifceBfoPA0808_*(q!#9*?bNPu9ds{Qwo-9FHv7DdUcrUWH;3{g_S(0}l8Lh*;IE&5tJ?hQ5YR zj7k-K<$VJ?0!r7qk*5jL z5vV9ROVa-kNwRHyc5R z1{Zv=Zmb){8w#hY$+&CQ>}AnRO=9q-V%0v4}$WAd$md7HQ^jFP#-nAJ#l(@C&9r!R;~Yd6Fb)4+v`k);o= z1$(5n1uT-nRORbnc<~X_&E_KW?8$NkZXxFRRIpsc65eY|Y_7+?ZkGxkC6-5U{_y^1 z5`oPd$gbZib=5>j=WT$BA^v1e(G@nUJE!RmV9{HH<0FOBMYkb^n%nku?Pqv4N14{F znMt^SNTj5>m;6p}LWG%9H5>*v*1G2TlTa>o^|U`8ovhu)&+F)f2m^)J0~P>Q>ZU1j zCpc*c*Res*t;+Q!}jkqOC)l=DxqRaIwRFp{Ws-kKjCyT?*4w|(unV-cg^&(~=1 z!98!ICdPfZC3rxYQ-`UY4;#GiTKh4S*s4e;vQr&RINW@upE%ON+?*Yt((09Lqp)}G zGl?K$g1Dy7^f6Ua1elp?1ves+q~Kr+W3@_B55r4U8v)oGVNsp zAC3+KCnvA0f8#nmmdg0N%Vns*7{CQ=tUpo?VIeGruiZv^qP^YzEeQ;l&5;gm<@aq)HJxf;$29iy8q9hs#BZJiuZKTK=plCo2l>D zK;lrv)Yskc3vaxk>F!H;4`adI?yL0pg>sbAek=<9ybgmu0O!!feDxgSGQ?Gr1KjUH z?K97#GCrA;sKs(Q294hiMm4H&`gvHHT%H!X(Lb3cN(WRhnan0BSawc}mNLiZ75+rC zBoU9i>hiKj1O=oj3Xxr$`CTwp#zvap&*C<@%pki+r8)xLNUaxK)_LwVvgfujNUR%{ zhg78Ts57j^$B7;|vT$`xIn+#cUPLqV%P!O~@BGE9u*{e$c~%#WoM$Gr=eprx-hf2U zni2UTl;Id`S)514oe}XvS4fOLYPg!|bM_{)dV^vAv;u#x5Rq^00hws zBu%YpG7sp-)eEiwLJWK$9K@+Jvj-&?=kQu!@QEwD(4)2}*)4W-vO4i=4uS=zFmP}> z;=ZNbpdZsyZ1XfW1f?*0wUpz@YsCQzlPw$Y91&Iuuv7qr<&s?ol5nuod4g~^@NXf0 z_qecWp`V$tNek|SVUhV0#&2~EzYkcx8c&InQ*lORBbS2w_X6fV% zWbaCSou*FaeGsfhL>dDVgImXe8T@EDGx1CI&Dw)nj{#>HcLNVW-EF721F^Iz#C{^U z9FI@$g4`>}+pzg@Pt9GFcUo%hqZAI|^dRM&eYMvR-f60Nd>uo}#=hnGdks`=^uFD) zQ4Q*i@5HN^@+F7tArrPytZ^*I1*l5AO zJ+3Fm6SJjA36@9^i|rn=D$TkCw#y?tuG9-(*zI8n+d$$ZWszat^vzamD_W!!J7M2W^jw$=(+xQ4 z;e^(3L3v*Cne`mr@MXJL?MgB=LbHgI=!N|Amhe2Qr|sFNAdYG#J5z+eC>@h!A(_DH z@aIF_!AF-&_YOrV$x9oA)tq)q#@?Trlv*JsfRB${rQDq|&vC zvD1Bi1619Mt-&Y_>EzD|J5bbqCkrWVD{Neix?}sr{|RE_if?BE zau8cwGIbE5FVhetW$&Lz%i}V>1+fq*k6MX`Y~Wn;WtaHW>UAA`%`sv@$AnLc^k^`cM@ z1U6iA#{YQ8x@OL}iP20a0q-%mV&>3T3YL9pm)qd7k}7@WiUs8#%XDJI8!-ib3Yaiv!J$jLt$fXf$*~QS ztqnT|&cb>|Jq>cizBgPWHq$w#zT)hQVH9T{tt*RGFq&|~awD4C;-be5I|obZWOk0T zI_VoD<3KoU28Sof;k+wzWL$g zgJVO7hhl9QZyDWJ0ct@3| zx*=IBEz@~MWK0I(@|Be>!^F?d;<7bmN|~g_J{*{D9c!7Gi+o#Ge$<7Pk=uEMu8q!~ zsCtjO03q}VD47};{CshV?V?6TtY`A z?v0JAs|ISC5>R7cdE!K|ReZc2EEmZN+9R0~zbkeri?ZroAVtWqyI4{XoGHO3WqKDI zScuO&kO3p70yo3Uyjo4DZFRJ!O&k9Z&+{3~r1%&CG^@RypG#bc~(6hng z#HV3_$n&tUiOEtYr>c|wM}Bqk;@C!PN)7JqgnT!#jn7QYDof8!x&UTe0h5zSy3X-Z z=UfPFyyUpALb&R9~P<2iOTOi3BVTfU~Z8wp*2YiWhu-M3` z9Viz37EsqfZ4jthI1d&`8J>3mBruVi!7RC!*ooP-EbFz&`iiOyli{IP!~9pJbz9=j0w=~y@o%A(hub)T7>PMu2)-;i1&chaiIO`jWEpy>mi0oNz zgW)Swg-VGC)`Zpjn{|_%smk5eL!uAU41FHfX9P?nOz)&NEDh5KI0`Ai;L09 zxkCnrpNG|Y7M5-`ofL1%kP|-+Rc>oF0u`(o+lrX82^5RJB?CHChWAth4T@7iabYH* zS%t^c;i{8X+<>S^*!mm9A{&1bHM?%O6n=$cIY@<@bQTE3nsGKAZV;!%m&8+U z2Qp*=5Ed2=y*Jr-*r^#~nBx&<@Zk6(;!Mo)EUYe^h?Qvqy!jYy6kscl;d+dC+e z(D>jD%e8&v87m2#@Wfnzgs?P#2=$Nge0I^UpBCbjF}N)B{gFD6nU0|ieI6Hw{gRZ` zdfCzad~`2m+?u6)S@)Czo1e5DBhFaDkqVnLc8Kf2H5&+xoX|n~a=5gY{0x)th^>4J zTqBOZAu>rT4?Mnj&b6S#DW%@>dCDt_^5Z`Sb<+q5;Dg2tvETT8z95wxf1bDzhh zY=T4COIJ!GH_k(B(`t<~t}bhHR9hV#l?-B?(MwYki&SZ> ztU!EvX^Nk;jwv(q$Bxu2wtCAw_1SgLqw)kw@-o4y#2VmYm2%CX>4rE$=DlqKY3-EO zQpv3#*I^N||z`13*SArLx3i5#h&mA;WS4l~WR$NS__VAumrFl!?_Pzjd+fp11w zS#ek5dw@!m)<(oj%0g*`rsmOSg1th8lm6V}jLFHdjAf;8eA_)_k4*Fp zCRn$~E?vz24-~RFy3J!7ltuZyvc^;rrcKGy5HPICfsV>1p!I$9EV=TsR zVd1F5*NAOQ3(x#qd?@Vqwmy<2y|7!*V8%l93f?K2&Xdx(n8> zW@=O~1B`+24wfcbvj@fJIl_r7t=;biN;gSr4bv#p08mtBq(}{1s%RSad)E|Uh2?vy zg3TVGv_whK#Uxytokj>KaCcHG3>K1H`PL-h>v$T~ai}wuqGldkck)-q4pbdBjte)w zB)k@-a;`_?-cG@8!z;~Aj>5~5pl&jSA(yv7Mf*b<*OW-?y+t#;O^qTdIUk_Z(qiICbVY@cO>_tuD7aI`4-u;?EZpbD$(y-&~b093qVq*a#I zVU=zL<_?u{ToVpD+4T_>Ekf~$y{rhwba&u~7_5RDlq5<9@9wLNZDJX)r zB_W}l-IT60s%g~3AGL4c6SG+X7qR*Qs6?v8$_A}WxA=D48)H*YTTIqx)|GJ%;t&b} zculZJWH5{_^B!)LL*fl(Oa9^4r=f4TkI|Zr173z98yadvfCaCnFy8T7*m@|;fd@Ks z?;~PJJk(kHl`NrabCqd+AdS&Jc20eV(%26XMcgpXmctTB;}df_Y@(j21@S0mW*fJz z^N2w}I(+c-(j+md<@S8*rdP$v4JF=7plKtG)MK1WyLh{JEW2i0omOwiA;bsK8>I*) z`yc&=@?IZ^Q23gYpa|9$bJz5MR=o{6@tq9?O}krJ817@r zv-AV~LXyslL+e%J(sz1C!G@j_r5F`eeOR|-x!x~DPcxK-&&*OIu$`e}{ zyRJN&$k5Udn}U;7smO=hCQGwmhbm@VWjc4pas6;IN3}&xQjetTAZWvf0_i zz4B?rX=i_BSh=~fN(;wp=4l8jd{mCW)H2Uo8dab|C_sTve4R5RON9&3#H!&s#c4cz z%-}A?FcNQ{2=h>TSe380ux68B`Mb(nW2?~>t~SClBqTq9OSkJ~(|GJFHPrNJ)iG76QgY*&)gCu|5VWxH-i9G}ABnDaaDB>BWb-(_lX z;P93;Yl<*jVZ`RJ#g26q5KKtm=n`|o))%1Dg@IPb@e+qI#$i`j9L+kFPpi+wSSbvP z;o;V^vWj3C=gLF4m1}xjbS7~IVl+~I$NRYAxUOqfBP#KsG_>Ua4EIItL{XI zTFRT#yM=s0iv}1`tsl);ryzJpkGiu=8=5{Q2|2d{3U~rSr2k4?KCETWab#Q0l9Yx$ z0s%%aAv|}O)_MG)LlyZVah1pz6++h$!Xa$yb%3b_wPJU;%5@~>)?nagyCSgyQ zlZ=F9>{}9*#+{Un{PtBD62&*fG_RB7M9pI?S4w43pYNzH0+sNzO{>qyBYi5dbrPKKBRv5qH#Ym;kkag(@3$QaNy8q}&PK%v)VkWSwoz{m>KoQ6pN14y z{o92)9v^v_KEvq4Fm|f#a9ckbQrwR2z`rl-F#1?bYZfK1c$+>J6DgCDb*Cw_=kEjU zz-I}*VZbV!*WY2l>JCW0c*&A)!ZCnLwD;CJIM5C(U`y zbgf}zY{wf&RE+quwCVp4I?uaoP}!(n?Ng{K2bdO6lUm;SM8gS8B)y3J06>vJO6w;; zQCm97$_4+X=QoViWzy4a6%Py0OT;Nvd46R)>tmzlJJ@&^plZPG{2h&ZtPUoRajQ z{1G9hL-Q0?9Tabm>Md3Z#v0qCnmv*+@zy<2NwVA}GjkZELhw2wDKJ8{NLAPS z3=&nBoWtpr8oB)Yw=UCT$LYpO_uzSAXAGy#o46#b6q&QXgQbFTib@?Tp^uzqChw2L zl*s*Db5?6ZgCBm#Ng!;|DlS}Z`hfEU)5k%g(~pn_b_t$AKm=3Y zSuiD~(hR~sc1CvsJT^@w=&Ik@2f<2})8w%P(j#=L!5yqvGh=2=*vZDSHo=NpqVsEa z_!v~45EuoHcqchnD+?^nS|z1fTG@ms{NGNhua-8R=8m9LE6-&Y=-g zrBBedBx?X4J{3R9P#aD-d_4stJWNUPz7BJk8EaY00yt zwa2G}FvNIr$_JZJ@Vd>!?mZ$noe>LyTNajCpT-wAbyUrWIQf=dC8#}YiIR=)0=JUG z{G5`-gIl%-ip=kL9UFTACyM8sYa{$y9jMy!z+-$D$A!mp?BtBAM<)v2K*lu(w(7B4 zD%~6?X%qxQoEO9q0$S>O)f2sj!2rD>~w;c4Qn$CwuUypT1tJeJuLsz}_ULkpS08V4^6y z;u6v(4%4AHU`j3kPE11!js*N&n8QR_YF@2e(kaEuC;4*V`;su9i#FooMmJ zo#*c=b@lei#k-J5Fy0&WD`Qm}(Wk*!MO=)18n}Mj=GL}%1(8D}D~{l<&QCIV2SZ;; zGj9d<-4pms-*QPDL~MmJQQg%-3Ck$9LZZnw=4{K6vPp}3iL+Yt{%RlKVf+5(sJ{oW zrB4-XJTC$FTk8v z0I1lgl@?TSP_?MhIvbVuuyt3$!7ZxDx5nB7jUchiuwc6`j=+9(;0H0tG+Z$YYmAc= z-T^LWcY8t*Xmr1po&=%%TEB^Ub=aSqSh+pWIszKY4WVn|xDxZ8;TL-=T#ZU*#7@FQ zkU^lfd6}aVhVZ8#VCD>GjIb4RkXmq&gLM<$VH6n<$BfMoT-AW^82-xEaN(0OeS2eN zL_&zNYQ$MF+2P#hLDvy+Gi9okEYoo>C`!F2N_S#0rIE8)cNr1Y*43fskkmCS05=rwldDj?SGvP9FYrh!5MIXcd2%$!%8Yi7%+K!~ig(+NzuZ~C!_F?8($n_$0 ziF#0U+J+FE<3Z6%)=M7cSs0`P#Y`=4DDG*&!SCuxjaPfwzch+vfuQJ@lgz-g9Y9J< z62bAqLl^TRc)->Gn71x@tfM5XpT`6X3I}>c$=Eb-9!<-v@87md=t)$#eRY*Ow$lpa zry}I8c$fSznK*3isaOV4W8#I^ z!co?G$pR)^?dQIR)^mQppOMpAYyxs}ZZ-V%>PXjZUols&=2SB2hs+Tr304EHk? z3Ngt1786$`kN1LWBA6S4i>C_y0V~^-t?(@__$LUeEpZnqZDvbHBfSJl^7O#cn82C9 zsL_t7SuMd454W0p>O-3>H3<8@jI{m>H%%5M>SaaOakd1oAT#!K3C>nP3~jBuWQ`2> zm2A#JX{Ttiq#A%sFC6*lI(R1@m;wA%0EYWmiy$V1m6*gSw!nn_3%NFWKGBXVD#Lt9 z-#zcwzlNoIBg$2qyxsqa%;h)VW> ztr#4>1@{$kD()k~LAWosRuqH|z*YMqleJ4Zg4mpTa5!PrI7)!GH|*AHGAEh6;G)>2 zG@r0H2FA24;;uw8vIDVY+<$h=~NZ}*16wE8wQJQUI`zWmAZIgZ(Aej z^I5Dg)(d^%W#Y*8$}hdLSdBSg9&iaGbr9iamasivI`N5UHn>3<7q?@v!k22_Kj3*< zuMw~kTXzr_S}ZZoJa-)|ocK)IM^aIwu*g1V(%D<;0>fL|@sR6C_6QcCN&i%|?qF4Q zbrR86BiHmyDaU3DX``Y>K0yiu&I%&#`-9QgpOdLrwf%|!H*N>ihU zH=}i@`Nkypys3 zgICwIJ@N3FY1{_14OoG>q{EzXJN=uHJ<&GdM3M?EeVJe!D0Y!kLeoj+ z#S!W~u_C@~P1_KD;nGb?*#}=Gc|KdJn?n67jo zF?u&SP5aWg3niB}MYenD+kQwXgKyO*~Y5uo)QKDcnOP#RrwlK3!A1 z`Plnnk5r*!OOIQ7?ijh)%Exy-t$!^uNT!%dsT>YK_%S!YrX!9(f^21EtTLMybLU_L zS><-ZDCS{8I!vI#E~=5*Isrc@7DcyR zx)l9!wuA?21ACI$P<@Zkjnk;xwB9G=+lO8`yGHiw_Y(Kp!A4)7z5Sy_OJ@c z0hO%VIFfT;#Ah1dAiwR^f=FI|A~M4K_9gd1BAa5XAL#84$njY(HrX?win zbPL=7R8GS6E(07Virq7)%4t;MO$(*#*g#}7C{yt?Dq+gYN?PTb#_(Kxro0%#9a3XP zg4=-#IpMB3&%48D%^nX{D%#37obu5_^wSy{dLkDsy8*AEnjXJN$_ycF1RGXkr7GUC zo1|5)>m*&Pw2#4PG7S>wjSjtmysjKPt;#?X>)QzN(vqpr3+rwjy|j&U78@@1dgCZ1 zj*-VbF6w8)BzT3p<4amVI1{kOrAIObcMVuPGGMX-W@KBYH1JKbw8z!dLQ^9^^DsO7 zfu0d5X*&J6Fg3!R2#@S6taQ!ecg(#D)MkFqA%2#fYDUKIus*Kv=z93V9^WBX{ zTm`g8E!fkz4p!`JJQ4@sf@Kj_S{KJhMz!wIm@zGZC>@g(h21yC`+7 z3c2-ix^)<>kl~G@j&RNqQEq3`($o^QAUoek-ov73tfdyF&1t+9EL6FJ(B???7tz;O$gtjV}U@tlyJ^Yq__6|?axj$^iMnPU58V75cC zs};n!n6>-B(u0NUS{eajT?>sPu0R{C1>Zs=fZXPvT(ScjF48UYPZPp2HUWLs@6H5% z@a^KfWa$~(3Zc<44O^qW=R*-se!=z%C*;5ORI}n)nOObwcwxcx#pxEwNTX7!wsK#Y zfZwi=-51k31(pf#l&VZq(c){2Z3oL!&x<5gR#5C)6{G7fhH>`RFWhAlMhHMw9Sbo! zD|AQd6*483f>s&cQU<&d&`~6l16>k_71^ZWhMUNkZ-~RVWM4RD=G^7`xVoI;GMW3t zr7t`?rzmbh#CQ)G<1@RZKFan6?aP&c>-hh$K6evdKf+ro|M?C>&lF@(3+gM#buhHc zXzm1>7ob7h>kBg$3=bcm$^Og0Cfi6()CztMSZskRK%xIt%FzQrg2528g6q~9diXh9 zMsKRG%Cqux^q2^|nxZ6hA^J5xhb#2v2(F!RUK~A>XGb&A6mX2&*euu6mL(R;hPcN) zOY5gOz(4#j_q-E7g6yXcY;XK$z-sqlg`>BeYy98FwGdcs+aVNbkyA+%0>!DYqDd>I zUp=iVN2D;V?O>r~lnhwgU;hBw z&tByvuISFV&eA=9dt_Fq{3&ezs1`c`m{L-o#xBH1H(jBxpPBo+GYj{T$xFgLy#Z^c z>UHP9z5|tRcHYyEQASePY;V2Ba}0*uk!uK!N9d>P-`wISu>SBCPdSK(ncpig43NMa z&&%1?gHt9D6ui2!NsGw7=3O&M&#T@%uK|@$=wJ{F^jwem2gCvN4ZDNmV<2-}oa7D_ zOCuIjOev=5brsu<>4jzdbMiqIyV>%QqGUG!^N(1@F>8RC_x_St?)oC#(dz#Mwzn!{ zblzaV?iC@Ji^>>0!?4rC6{=sg6)jMXKiF2*zy$2eNdNlK4is}g1n#X3P@Q z`Ux0@49v;)V^iD8iQjN)hbn(&cG)X*M@gA6<})~!n`xL-+0^P*s;h50wYUL;d{4$9 zVyj;31{*L4SxZ9^0a{iijp1+dq30sEuS?A`H0 zGuO(;8ShiH0F2c|4@;7F>u%Br3^fw@tWp?<+GbBvev~q@;n`-NV1*gUZho-JqO))G zZSxhb1h;nyF3Ya5v-(rOEWm$C6A1LtPE}sWNQj z1^ulhjXS%f;TB;`>5fMf!cz(iGv|}H)EPKf`XWjIk%iQ?D#m%i*#0fJOk9>h0k-#B zuKr{xGP_>#@n-gy5%t%25A2k2tPj95_@PTgAXj~SVl4a=R0T{Ti7w@#$*!azk?Avv$lp+l9r;wtQnk4ps~!96m-CYVyB7~}|* z=~M;v7lQ&im5RTNwn&w?ZeOBuf=Ja47lCv9lAipNH0ZER^CQ^as7=3*E9-e4o<4Mf z;F?ecGmfpQZ%&zxBCB&w8#w}*1kc)dUW8tr#U?Zc>KJpuy`5($+?!UB4~#oSh4wC7 zg!B$E@(4|444d-O2l7vQV;{Tu>lvAu4kIf1EhMFZLGBYSxdT)Jx(ppuLT%fgFF1nT zab<6ob;@y}O_``=I%ON_HSap&V${gnei^o`Zqu4f6tz57Gjp+f3{wV#nJU-Mb?%|OAlYBZ1G8j3Wavnwu85 zB7D&y<(9qh=c-9yNkf@CJJz5!fwHo10Z+Aq#Q=1Lv7is?M~S+}Y8+k9r-v*a4j z6$*VF%&fseeynqbQ$FJx+>iC_9(ma ziqHLluhc6Jv?Pb~+Gx+B(fsvTkb4X5RN0J13aYd%#0sC>KgIRN9xTL-m>hPje#m$6O>G`j)oO(+(BHUOj3Mv z>^kbCU@2uB$6byDFvyej^q8^OD8@VV&x-Fi$K4h(<0-^-(EsRX)kq~S`70BFU>|!Q z`Ul*cJ1;cC@@^iBH+%vL?;BiBPZh=gw=7jqXzFCUj7hY?z$%ptsGg-}SH6H$b^;yP z=qbab)iH-;Z-C1#3rV&Cs|6&2pAZ*kyip2a11a+&;3X*Rs(mF<5lW=U29(LTXa1pg z@_&8eomix9i~`s`$}1=F!yTgytYcDy9Z|~Ka2;M=EQ{Z~@0>hFt&v&6zEa88Hg+PLkYd@VEsl{MD>+E3ph6Lg&xGc@w9>$uYevVNNeYGVTQ$%six zg>pZ~dWB1lNe{6w4;xl05H>q^f$TiRL1LejnJ7U;sUr7%b&&`YWV~kGwAWZu(Q6#` znf)TfJ;AQ|YlTh!sJ4h|+>$;ldJ4}^GnX0|?M^IoZNe|#3bF}XK+87Dx4-BHo`mQz zCRXjLaagU1a8P6$8B=k+TUoAlLRfojb>Tf~Q@?G7(MUAchwXfdJFsBEnF z)Yimle^eQjIQa)fYHP=PA!k_BR0oZKK{4YHcPLO%@YJz!ioREnIw=K1gS?MhBo(Igab*4( z#Z5?@xfP>{NG3T{B%4~^ASP5r8bsi^#UDW!py|x?Bn4+(D30Uh76dyQ@3Jdj-{wpo zN?F@5e)N*=m@@>ZYYJ3^J2mqwSUT2F^{pVlP}jyByhi>Ai`P4&c6J9>JmGpuLeIHH zB`pp5F6B|}MWq#Yc7th2NYuREe%d%ebZHefi4E725OdD^mpzDRtUSpzaCys3a;NR<@~iC?GQ`DgnDv=Q^C*iJc`&jV z1l4VX=l!l z$~4tZeZ|f4lwYGNa12;kE#$SFLGK3f+U>d5G^uj%KB$rrZmL5=iS4p!&+0u;Nach& z(0|u5i)QtGOo~NnuA5b;%|5>gRsK7ey1v?>LRPE!!TRMiTiU0&hiNwdEy(UNtHysN zs=>4e#m5nDBG=nFoC#1}D!f+y4Hd~InSBW>&SA&ZZ;b#<>I&;)viisomC@l>LIy;o z*10ShOc_?U(0n~%Ep3N6uo9=y;UVcK%5eh!WYv>^Qq&?q{+#*~hM^5umfFb-#&Pb4 zAzUT-&IHm{Wq^HLpEitv&I3bY&gG3pzNJU3Ca3c%#d^z#{&+k0t2vM!<*$o1=)L0O zRj@Cww*RnD-*0VnjZ`lIPM`_{m@1e)(%@b--=ic&Hmgedftv_3op9~5UeSD?qN z8%X_Gy74~Ar~3h7&_^>9;*ebW%3@07#Ohg0%Hkr@@IV+D$~LqT(5*y8VEZ%{?$8+} zV|1|*O~_c1;7@8&z)G+yF|6q5s=U5gyXnzs&e9EpB{;*n+|f6#`PD5MF%<=evc|q; z;~0JgJ=|Y*Lk9VbFX9wEmaZ43SL00uBfgY%M_3t!tD?Wmd}m)>K`YeRcQ!XKHM<$G z6uZ>o6Ku=G`$vfpVDZc=WL*_8jjh1bhW(?bcbYQb@F$QM;LX$&ky#wt4oac=4%Muu z?rrJI6|dYj^yIGYPl=_hM`^Y>xiz1|gTiP3>k#>Txj6p{eD+!_FB=e~ z4mbQOLEXQ?wzF2U>vdAk?R`i~bdmCw&LVL#%PPnv!@4HJoxNP~Gi=SDh8LN_N~oO@ ziCom?w&U@WY<*$+{?h>C`S{ayfPo}*sR*q?@|yGO0Q08-h8(;ZVuze%-PW#UWvMD| z53eh#oYCdV-518x6bB)?{la%$+>~!}@=A%JBUTO8#;-A8o=!5%87PZ(A_)S4t#g@o zdj|n>aJf_ky55cR{Z>C)?7JIllupyZCoQJ@1?fV0l12Q zb>~Sgx2WrTaVaYCEW*=LcCX6Q&I>B8LKh@5x|Y|`6AC2EIq$WanLnQZO94Ye{Da|R zLcvR133*m@q;rdH6Wms4<#rFgEc+mH7HB+^%8(){kXNvD63 zlZvw)ttqGVHY%@|q-b#1O-$XkjeZwaSPpAwg%+F2KEgV0wEdZjz5y+|^YgL;o8Y4F z!J=9?o|yb2JE6aOk>p`v*f1^0Q2p}r@{dk|b2u#jn6fo<=R-WX=}Ge6_fUnpv+;t( zG*g?h%kw@i1?%!kVM{A=v+3^kC8=QfTLbZ<*xq2%xQDBSUF%>1uEAk5yV@Y`G;*~o zTm?>x7NTQjf8%aldKxymzX7A`-sXg;RN8t~G!dW!y8=ckSeh+;r?Ao|eI$2${p{_h zIj`Tz@iNcC+Kg|}g^NJdW|T79Puaz^?Z+b)8j>4jti8?DPD#i@VSUYcoky_+hcmF{ zxS~z%ptrGJ0v zr-u6dBXg+W!t-1@s}sKUGc1Oe8fqJ!QOA|Dg#oWa485o!wEN!3L(NAz(S0WK=xa6l z1&bAtf)ta?MU}zYnx8%x2U8fN+@nobO{hnE=*-xtcC=%&ky1Xp$XD9FW?GN|ca1qe zsHuMfWzaQLT_#3LRxO5gj`dP-9tK?ATZFzB!k3lpOehOw)i|J>mXPR6%v8l`e@2({ zE3J&^XkDVrY!D{jZLMWUGQFN+kUQDU(@iK{kk&Fw+e>cH^rq7sh)13c2}l_1^v~_! zYJBuC@CMa5DK!C9N|ssTt$IC;%m|dsGcz?^pth+)j;Yl=bhMQ8)@hfSvTlY!cvtn+ z9+kMQ?HrZyHdrr=adw3T6YP-188OKWM2?vhxlV}Uqd$$yFnl2Ul^x1VrN?!%vy_Wo z!i>){_5{N&bn7LDi&-cAh1?mKOO;~WYaF0(KxL2toP{Uz!Rs3=J%yJ5)QBh8QiJ?y z&vA1|*@#I(k0YK54IluOQd(n2Al38dW^~Og1<>E?VeS58`a=78iC+ffk8_WGQtJ3V zB=>;pa*#Qz*VU3!))5y}&-UkG=rP_uskj%pzkgKzf8g!a#fBKMeE6x=vPo(b?oE2v`uo!_t2Rh0r^LpVFo;lrtL7Z?bs6=v$ysFs7ssRwr$9c@i%+c2ZR{%*!ND*1UjbeM%n!L0mx^$yYU zaDmCkL?OgC2wB>uaLX9USSy`2=zj+kX@Tqg8%D(MplT0a-I%NtD>KY632%cE26jjM zcz^A;PbyGhaO0%4X;T00qf)k#7_CX;QTbm-)nUWzDgUdUI>eV{pzlB%_7Kw|Y+|M%SrxiO) z&AZ=#_|_4^apBnRj)(-l;0+HiDE&d%ih0!7za5YZ&L@4GzBOCEzCZ*UQ*UsTL7Wpd zL~U1UZ0#|Hf~=8%6@(Nabbm4C?hrv~RJdL`VrznN)(KE@*5+i0j^&#xpxLkRoq7X(v#Nq|f~d$e&;; z`V}y0HwebsCiI|$u=<5tcSV(cAC+9R&wdx9x<9G)`5mr<#flSvb$U&1myz9VLHH@K zY;|KMtO?hcDe}ZNym=<^rnHf6z>*a;t+z3vC~lHv&LHzyiJFBW^#Zb_8 zSjhs6aKHcaH?ThQ0@omM>iemwWX+c^Eo}H}xA2)St52W8ET68Xe)2UqKGDIQmT1HE zH!k>Vh@ZKEPa*nN+-OFsjkJI7E0m251Sznx_OH*;Gu~8&66ot7@7ILx7&|Iexu@|< z=~HBt?L;<@awif=p%)LK(|h? z-BW-YQ2EduNl6Jkr0vA62QU|mifn^y>_)Ki9T3x(5ZxP~(yWK}E+{&?yHdh)XmG~D z<1JK?{`nZJU5dYb>fHV1>yPu#O95NBr}6CXgNhG^Sa+LpZIW{M`>0}lc%SP~_|JV2 zG@r7e^Cm3B7m^#!D(B?_e-l)F)V<2OvK7-5oI*zPCMejVjZ$kx72`Ce!}pJ>3D)kc za$K~fe)Ft)eZa}rj-ckyR26g!^kdQQi0&%h@-mllk-af{{R5!Fg9xuF+$sSYy|pEB zy@G(}LmVO!ZFE;xi54T_Xt59ZQI3lS<9(2kaMO1(p+E<69ewPqkq(<&{macxMWni2EfO;VGZK^O%~ zH~r=YO}j_mVZjwX-7-NbvMQ9djWxeXxq4Z*Qpzys6N|#~`#%#Qre26Ba! z5@epReWqSFiREubRbx#`I3lp1Ej(T-rErBaoeHB;^jfwBr_2u94p_D?Vh&?^=E*cy z9jhWoSQG+a&t$Sx@dQFaD=e6j>Oi$E_ugiGrwKtAhlGwp80LUVKRH%cA}w*5oRxsd zJ(@8}4F+yWzGvub0*VE~9LR$DaUjDp!hi@bdU=kF^M^pChJ;g!(FRzK!z&otgco2G zHt;9-Ay~3sJ#^X(vSr6rlkiZMOMWX{j)eH9KzX*7Y-O~ZMrs1X0udh8oB+MD{30k4 zpU-d~B2GaSS@$g=HihiI9Z})4I&>$ldpWMOmMMZPMwG!=K|P!HGbBZjs@I>mS>MmM zw9Vw_7pl-=GL3FbR#+)RWtvW?W(8v~JN4>ImhH|a0B}Yn-z4*@FvO>}%@Qz}@7T57 zV3k=y#h@zK_GG-gZ5xXkQ1#fO>x_}sy0M_ahucOTjOV(*@1iP2mz&CNUEo5Y`)5(v z0&Vj9a!IkLHRola6sQM6d(tnGID1?k(a0DoOEZ0lWqh=-VFT6d3a7Z{pa@<*SD_OM zjINwOF(X)H7GR0WZL1c7%K1Gip8F(J><%j`h|sj3ErV0AR~TkPS85Zg{E$6i3OBU; zJ+Ks2DZH1dW;-_?Z;(J(?=83xoJr*jxR_w9V`;T6;_6dgJo6ky5jdX5r;S@aUFICH z++$ZsCv}J$oAx)O%#j_QVry7+6KmR>kYfqEc~59F5a63q@gCMPGB5{~o>E}jp8&KU zV;gpJl59dxF!Mu}KB9uRd=TmrEI-SLW#%09h?IXXVZs5Hmz$QO4j#>%!GyVVLKW-- znByJwH9}mf1Tf~)$Tz4=ld{@2c^Mv7Jp}T`q4&R5B_w}r)u$*Vgod_ME0EW z#xm293m+>-&08E(=BUvk1XYwPSN~G0KBG#_LG)bv>!A9xp|Zs>?ed`hP&w`y^3}Vj zJW`ZxuT*j~6ZH2n<$c@kS&p2g+xP}1Dp>|<#N_AXrc|(nCH92er>!C!EqWzwWwb2j z;^QG>E^e3{FKymY(WD7*YGxlo+)AXEJDQXmlcy!6@VzKL9SH(*$vup3Y|hSyVMOCr zR}~m4-Z=HH79a&NLe7OWuj_8>*VQDaIc{RLWz_xHt@}d(x&1=LPlJ5#1!h?M80zh` zc#d0$0+rIjMu-MvTN{eY4%?3ID9^%_lcRx>gHL@S=-NXPR8MiMw9NLz7TL4$IjsxAzjl^us(o%40dw{$2PslUsQyORhS4J$<*t+OmfCv9!Az}mYT zFL~;x%t56D%azQ`OV}uVMpRC5YSk$z6$?gQzr|m>hE$u?&3GS_txE6OQU8LIYFytg z0CQFc{Bp$d73}QQ;!YEm-;L|73Cp)Zb&`~_16Aj=GTsN}ORxs|u${bgU#IZJzl-Wa zN27peDCV|hcy|h4{52}djmjo+w=o<}e zxzDNN`nzwdQdca4K1W)JDrOjn&tZL1EBN#S$Pb!;!v;mO3gJ{mL znh;6}KCI)&U1z-`<|)O>$5UiRx7qq{j>O4 z#TL39O4F0cwugeg>IoJ#y~;jk58apH5?wwXm$wXb3N9$Gq7lhNMZDhEGp-PvT)d}U z(rgjqM{rd-Mkw$#1vf36VfkJ?tci-LLrU8~L$=n^@a9XM1j|qfjB=Ik2yXNF76UBY z9ZrS;3GoPYsPZ5c&miYOq{1n7=~J7H))r_fhk&= z^I7@_)pt}nn9bIAkX{LCy+(+$9?fWHuxpj5co|F9L&2s5jU&wxob#-(-poOij!}vhY!BIBzeI$whq-GGDT)!`iI}Q^~ttj?$(% znDn+IYoS43;BkI{rnz2z<8_B&z=W$74h-kpn_RBB=WfpykhR?gG@_=YA z`R3YKUTwaXnDda_qyW*xz%x;8yLL|e6qS}LtLkWUgo~YotgD5=ymTw`KOsd%|96nQ zUEc&m^zu>XR5TnBD>)%TiYv@J*0fTKf{04QPB{QXH>N_{Jw$pokBqR*2Fbd7N`-#y zlzP!q+KG)VA?E$r*|!nphwzLq>3K+Bxw=a!iX(^n_hS0NQ>xVR_J}5VmCqr4QeM6S zh=VD#Ojm%-m+3a?jCuWxM4_a7{tmwLtw3 z(So$aFiE+?uohIRDSFo#)>PTL4`^EiqcJ_t!&OKlTBZ2meQPV_03)HhNsf{xsYcNU zQ23&MFuxjlJl`F3O1{9xS(1@4Y%AE+?-qEAO)$(*JfZTRiJe-HNZ6f(C#bAAChYa3 zNkF%FPjBk5R;knDozG!&e-{;ujV=qvK6@D zu_x0bn?ZZ4mm$X%^&=~!2K|JpHb{fzEm`mE>z0x)Y^d()XU{Ki1}Ir)xNB_hAh*?s zeiq!wLL|ji%=*ewY{Vp8KH875{31*?<=kZ6p_7HQ&#zO@pU#{W%I}Mkc-V;SWL#qm z?j+W91o&sV1#ve<0z@BK*lWle)mIqtiEM##YioBRbdIZEdjYI3wJ^{d1Nz?MTeVM$ zS0vM2bCeTLRC`v0sLjM{p~qgCjZKBC`w>x?b**&q5eH5+aT6Di66I}ALB07GBsf9a zD7We@pqL<={Wc^;M^yd>no>XQlw!$Q&pg|#!}hRNL?I-X4NZj%E`B?t52wtErH9-U zsOzJTX+^d6y0SjVfQD~w)vpRH%B&TlhGE`6Ws_TtQ${ZI=MU`5XZC>Fakjxf5WeH> zJ4%vm?TX84J&Y4)MU|a}5)(ji=+4P_`POSl;n=EYHLw@gyhG}@m9j?jJ|2#$xaFv( zOlHFb+2Iv05$d^Zr1FXak+B%x6C8~~tN(8UX(u-*2j*@Xi!(qPeE=&-(lb^^+G*W|4}^^?4-2C*9lX)%$-C)4 z`XjJ8yP-)JXAuwG%zVs;gbO)v*P;GX_~Tcx%w^twbdG{W+&W`BJ3Nd%FVq@sXnd{f zenz)hpkI~SRA3fsaogGH18AcP-b9etQ4<`}$LdYsdPVufWgMy)zQ6=aP9g6PZ@`ta ziEU4!MXW99wN*w}wuRAa)_6K~J|QesY&v2yaaA#K8>K3rGP`s5CQ{I6s2ovIwfyk0 z>jD$3Obl~Dly+PnXYd%JZG--H^Cn6>g`R^tie8M<*~RKFV6jx#bzJQ6b+_@p^rlFl zm3%y{Is3LOOr@G_c;!T&s?@E$@ck`tU6jOCD!m$n)UxWXj zG2*hj0ne&FR9R=GGTXq9KME)3S=Sk$+%vb8Sb%s&zu9!}-<*0IVt!`7G41Mmlb@Ud?ly6pvfr-Loz4=BM z@&Y+NJjfJv*SYkSQjz+`E@F%A(I%A=qHV`{=%EQS!UZH8u(KYCOq6*WeW(OUpCGux{D2~?)!2Pv(9m6gw9(sB9yaSgz#+;YeqhytMK zuYh?VXXbO_nliO#Dz_DU7K0UUbpQOfpqgXY?|?FP-(hC=W8q^d6PUklpi;;{kz+%} zP#Un}A3m-0*Eqs(Fr`0%?7bk2X*^}HAr$l*;+1^}IWaU&&pY+ia8>IRjQ6-G5)@zm z*@^k<6Z;8h?;Tm{nem&}W0N1cyh#~cN9ZT)0})sg;64x}dH0}yk^Db9uuqKrqMlQg zlocJA(zXjHRN0K6EELI#a@tNzR(M3#l+)jfZ3eNgP%G+s$tx*p{Sp>4n2_!(t!pb? zCrw>B%OQdeX^A7INFa5}P|7c8vz4`SH{wz}UkGFxq;SyyCO1h|f<#2sil_6VY7%32VJz=E>JbvTX*sS_` zeD&|ZN)Klf15w!Y!`($7q38fga6X8{%U`n|omO>&%u8Cn#TlF;>5-gVnzGoY_)CdT zKH>@wqYO(5ZM<)J+cFDExP!8}f!uUf{Uj|eC>Iyuk$91nZJ=b?TyAtP3-#c%mia$o zfC+Z7C@)mmPHTb>N)#pw1s(6@7|-^B0Zbx&`A0pZarQVtT)m@$4NFgC#s*_noPw_w zGQIb7IxjQcXmY@O%5KZXI!FHFN9NpmY3ScW6)+3jh8|eX_4u|NT=!m)hCagbq?cc3 zWRzr&!&h=Z?!s>U=YRd*|M)-pZ@Uh2y9}Y--f90Dx_+4t{Q|o8?vzQ38j-e0tJK$s zimJXMAk@8J8%#d}E77)9n%x5Iiv48s+0{m9GhM?}$7U zCDV|wS_$A77eY1TQ5eyO+bg}_pFoAQ0JL(2Wd!-idNvn$godgi#krv#ZTg{do?E!O zNTKC6g`un{l0%?}hthpJ*5G(u>WVGSMN7v-7~@!-H`YT~ zzs8;pF~vu2CS6sg9GLP#I<w2YHS3HN z5?qSHbRsgBLY0qZ!c(h`V3MB?Xa(-3KU^JgEvrM5aIq~cdyb?idYSQbk@_ww=b+34 zF55ByvkYQ!5XY?$JqKjjI&sc{Hn?6~ESXCi@A7JST3@svHL4H(bj1^_2 zv(9&KgxcW(7UL?tJGdki@oejRPGNX-LvZ+^RK!i8=Ogs%MkJx{3I)QmRSlJDqW;gN zKs)I*fYpy>lh}-7=+3v;CZJ-BX6TR3fOY?{+N117H$gRrVDWuW6eZgJ2D9;^O<;qS zK_od#j;@{lvz>}KFohIHr6-0O7{z*}*@5NfvA7f$^4vU$H3h16ezKEqIj8^5!~&IC zmYSgC83YOwvacg9Z^krFA?5Lw$SU|$5CJdBIT$pK3&uATVksv}e+w+n;m-w^ydiQ` zsRn3pF3c7gZ$iLO#x(g=gu*|YRAG0M{`Y~Qk&Y11&MjUY*l`LtJdu6um!14>82%Qt zO8aFGZ6Qm^H$3(nU^e16tgd}LH8S!q-=^Mwk1Q__&nzt;=I;V447Vw?9xgM)#ghH< zhe=Z}j&oqEDVt(p9mGir?|Oijr-ZAF2;oe)(_3YNa$Q~%=(Y!zEWn=w%(Rur4`u(Y zLUD?uy>}2N6de)$?{&l#DyrxQJqTWZzWH5_PRR;$Ex7Ix7(YhR9+}Es9V2QA=#5us zTU80(rcG94b(VNLz!-&(C{aRH>MnrA6kO(o(Ue))UIc6@os4d&El2j1mGzfa_QM~W z$!~)LTWH6K`limKpMwj%q&==W@tM61tjox{f#RJeDPeVO5s?!8=Wt?VJLOEx18CY-{(U@hJ%3nOK2zQP?*xVr4*@(7NIVfc!XL#=l zNdzXqI&4%6SDW_a-^Udw(C%<$n`)Y4y57g-prsuGow#ra%*j9Ag;i+3`<%wZn#3VM z2|q-rwb*4Xxu^4&n6U23!lIK&jecz{C|=EbyEPG)aq(lZn9yncH1o&~o-dFdM`b%H ztxSm@NdY$8jl+u3vXD5X5re9HhJAvKt5VKRF*n_C?Q+f}0%7AI@+^Js^kJ(#`5Ao$ zhQGV6u+RbW%X0L=SJP{uP^~*IHo3OJLhxV*iw%@HlW8T(@#}VC_FUCDe`L6&$}ytx z(E1>V?2(_?bw!hLf6TeY(%{i*@Mgota)QO6ui~wulZmmcGwmNJHr4%X2NWOE9(}O^ zH=UNRF0|t)3|AFUflz!?`1fJGiPe*_EgHfSqfXiK&_~ZK3EM*Q+xeVqD+O;wiDOL+ zKbG=2uIOwk?S$);p@l1C#{^B#0-qr>R0?q}fXO)Yplc(y-6(86;49qAHBa!2en7

CSUgnZc5UBf6Za))!`#5>4rXnOa~@>4hbm(NV82-FaM2dvK- z_qglD-OiLPp&_s@DVIn0X7mq~10x8t!G_Vr&9@EN1i)CGLcumbqxLva@at>%g;a7fYtGj8A&b)~w4Up#l(R4Yrd}NM%>_2eab19&RzKu!3t`wC zT)EZ4d<=RoEr)Y`$3rL95f@_Qqq^65#r8lkY76$mU9O_Gf?_2!lpb2q3k&I-=!OoP zQDfJ=*RZygnS$K47Q&MyY|59I`14aCp*m16?hw8w&a->CD>sT(Ys3t*na?D52HBLi zA`wgqZPCOygNi5`1=O_k?lz`*mmaSf=Jl-jz2cNfX||wymUV`5;_5Th&BdHN8bSqa z>^$Jw4_^rv4{Hvt&ggb8Nx43Prx&mYWnd=IS#A?fw(aiwu#{YuhczKjgZ`zptjFU@ zE%~A~luC@n_7M}!`LKc7ue(nKrO?Uod(*_zGwEkgNS$WJ%3=EH+hDD}xV`)50Q9cn!h?!XFv?N({3P;FMf1zaK6V!#Yk?3^fLX8=aFdwC^?Z!lvI z^wOb0NgJ1r2|3rkwH*6Gtu_S|RFv`~DZ}+yP77*OlE z;TM;GHI@2TP?M&FjYKmYjiZ=XN>@b4df`uQuqC#H`dzI^)Yzx?#&%hwM-`04+A z`}*m(={I!xjo*Ix^y^=L$De2)Km7gEFTecDw=Z9R#OK?8eEjgYUw{6;fBXDje);kv zzeC-}55NEXZ=e4s{^;pf|LemKVfxkouD|;2(=VUDeFKC)eE8R&fBpR9*H3@?_Cp2p zvH6WZfBp3P@Bi{g{;7ZX{nsyl|HuFNn{)QlpFaKa`{#dNedmJb4}bdfgG?n=-}>9n@{`Wmv7&`{Oc8e{Kvn4`u6$j zFJJ!r=lA&3Z(slR?jQQ?%WwDoIoBU~^oM-y)80`>H=i_4N6%()^pEmx zxr|?g9ju~3e6$$7njs{(7pxMz1Qi!dakuucfT-kR0obf0y}*j;gnzSy)m2P{rH_P_ z6J!%yUQqcH4%;2jtb7<0%&FnunK5}e$fs#N~58jVIKRp@5gX4=*k~nJ222@sn&Is~dDOqQ$Akj`z zeA->UI092Pd{}|NL|xpT9xzG7Z=03`F=@O&!Ydd6X;E_xr%bD!9*0SwjIg$gInacw z2u$IyJ-8W@^QThqEH9X#_2C$%Ma!YZrASi4%a%L(6kRF~%9ijHl?$e*Cp=0?Q+sy7 z4bdk&U+~yWN{*Qqu=;@;a{wa+S0M+y$)yCj11d@tR3Ex?Geg}wgBFghlzp<<7c&!6Kqt`7SpNhn7l-qK$#O~Z)&a#K2XSzl@im3qveWsksRk=Ik4>%$KSo-JBD{DUPx7ix z@)j2^Vi4X3{4M0=9txa=wQk{AuDX?nzzXUT)IOVp9Cp_b-x0FM1$2B);0%U1`wOnn zi@`Vf3t(*Qh7xkig1GA+WRxzx1HLH3XQ;48huyv{M=5 zO4Nr4fx9xB0N=`X0S~?-A<9V(uR+uVf%v-sb0>|^6Yt>cAwsmcO@=%v6kOo^_^3sT z7vCDOyvF6L?=>Vv1E&sBf};ShBFCM{hZ~z=bbaSM z>{k7&Jt~}WO88Q70`FRvbSd8`_>c>zG|6g2DkS9$KLS6ah7xzr5J43eB3`KEB0`;u ziNI+g#)u6jsW;J&$Qs>@2@ke-Z84SbSWE@s$r{sCd>?Xl&=f=wstR)jRhEvT%M={e zC?j6%Rq%*0LYp8x=RuBxVu%4-0AJ9FnH38`w=OE}Rm8UlZeWDvuzYd40$#+mS`9^p z3mBP_h2QKuIT|{#kfgx7E)l1l!X-%qqbcy%qHmAUjSDm)GaCp^BZ7UWJJaBpWq_|w z@0mG5KRc=6L}z-5^)FU*fVg0ET+&cejR;gj+GijvBhrWnx_yn_mbmO)VZo12h{iHB zC!4uFtW@wduRbsa3o=L6RQlB!=1I4PoS_F4JSG5E5a8^ivdmOvahg?%RbieJ-Gx<@ z2308E1FD*SPs<5L9f?W>?o6oxtd?`A$QlhyMt~}bs7#OW1QHej4<;Ops6knBIs5hW zN)doBuXUE{+zbI};$g*-ATsnRUD8U30jco?DhNBT(8W1L5hK1);O`VQpXQ{q=(Ze{ zLwzc@KI<=^e(b?~(iKjbaJt9gye&HbH>;T9fAF#La^!P1%;Tet4jT(+^AQ)EC*BBq z7fh^cc%jeW82hI^sGQ&^!q7|I2~h3yb|re5)iYeone%IxCjw0Q-XtD{N{ z&?&Ys!0EvSgYSx1O)c+nc|7E%iU2rUE&R;5Agd5V3ydkLZTR?%TDg~O*Ij_!Z3KN@FN9(qqMqrP%fyTGO&brGmC>D{N9FAmI*#&Mvz&u ze^FHKm=U!LsDijh92|K(Lgqs0fJ%n99-&G?RNI2WYvap6O!M)v-WgJaMGz)+dxFY% z7juCmGczW$+Zs;ndR~q%T0OqUf+|z&(Sq_rUK`eQ)iXa+JZ!1N{plG!$6(0=acvVn z*FE8*`e04TI@G9k1SW)!KEo+xcnC%I8%&TSyd3CU%&a(w&Q%w2JU!7C)EJdw=ok%- z6G95}xJ)}k#g9hV&r&UGMsYS&Y$?NOH7y4;FN1sq3mK3ifk9q*|J=Zjc6b6=`G9Xd zSNBz!y>U&;0iuvm&kfysc3;3YW~j37pfZ+%QuRRLgU;4dLOxA5V}6%o7-7+CAmp@2 zgpMcHsrJ{e_KYA1H~7T3ezUiLNae&5pnZJpX}->XcPp!Y2wAEr>#|se6lq)lAtf**WW1wZ2%N;P>&tx$` z#~Ah`ZIG1>vKXsM9WX9N~zmi_+pE+67jcvd?3z zs=8osvk7`9PJ*PV;Y6ecMUQVy5e-DdF|qnhJaaTZWAN zToFHlTP$nxG!;!g=eW1r=$A^c+5!P{u(S6p0x|8%m`U`%a{azRBLo?%5qBi=OR-P@xl$P&?gNe-Y8Zp_bO$((vnZDF|%tgA8)mt%By z1a{lh1EUD<68u4SyltZ&78>CqGm^{v?^+1TPL*9)w#_d17)iKR31nXQ4Q z9eEkF9^Rn0l;YBXkfG(y*Or8fjdvB+0kuDaV#OJs2oE7gzQnO7Eyq`yxHYMP^<`N5FgCcWJk4dv{XObRE_20ZuwL6QDiA+#HoF1K+y%Dpy=Keq4_GM z_eyJK7EXO&2T8UIDq1vzn5_@!Cp-U0RJAF_Y12Rw%b>K#0aQ<7Fa;~Ux2`*7&|{A27+COabRekt5aBfh#1`r`T4roacAv2(Eb!BEW6q_=Q9m zp=2Fg>;z(42OF@<98ohzNmwiUkgoSm`?(Q!H(0mGimf&ZS0*U)GTX!m+h0<~@%hLn z9EU9T0A*V6vAAFCy|EoAwl=zA0a1SPDWv3Pg|}(yHLet^=PzG%gl=2Af#Y-}e`!Mkj3uJX9R!eQfD`B1x~MxauB8ix#hMsPQ0 zBAKv_#tA|AEh(s-n~v}>;lc&hF%VB%DF5|onJaTAe;)6jQKliXV_KUT5B4TPkMiZ| zY5OW!isFmOzRMhH56RRkSS!H|s`Fr3903c_pXZYojGghJgbuEhajp*EnUgPb;kCHU zk)DU}hM=Mc2nz(q6t70!_3}HqfH*uB``J(ChgepqLiJ1Y z#(ntX*Uz8++TxOrFV&&se#qV;uyh!fFm^2~TxZkz&~p^_C9cg_scy{yRQWKfkWa$q zgw+gQeXjKt?Q&i1TDyVw0CwL~1T5yZnlXq}3m6YoEjoRk!x62l_-^ONYP`>Q5@XX{ zV^R}t<&hdMoaHRBX^!Gh1cX&aOxN6ghG~mxssxuI)eAv{m6eHa`Z>AmM#=E2Tr8Cs z8HOMv^B1QuWs)VVlq**Es|=arFL0%D4s^pZe%RfJ4V!;`^v*mx4&eNFX*@0t6qZ)& zy>l|6YL^wRXrR|Z&t7+PQar~Z2y)?fMFlurVhtFG<{W*^*2n-0@x(tGZZzCcRt9x9 zy@g*$;=o-A#;%gGMAZz!V7C@od8x7*AS@am#P3q`S_aR3Kb;}9V$Kd6)5I~$Io8(` zy~nsBKFAc;fCerAV{b!(8;=ilc{`5e_`*PxboJTmtTGo9BjP%T-(5|B!$!yv4^?zN zN9az>e9N?O9&gWbML9Z-P#HVI{d^mr;50-8Ano)|rPYuTWlsFmwavrPY~xPOc{d!c zxKdL#S!)B{c|r-Xnib_hM2=~mn|i9vfz}WdO~KG1@hu=7qC^$m0~~B_IxDr-97*Ja zvkP+sj(8K1w_{z|h2@Y#r1ELgcQ*Aj4!Nr^Aqaa?FfkWt;`W&V1arYfgMPB@crMF^ zfYUc={KVP~CvM2lSwWAO%w^s!&el z7^*w~6-Ps?nPZ9^L5Dy1P+=uyXKsD|jeXv$WDHID3R2UI4l3SLpRBIl*6FC+R~Ig&L_zTT8c?^R!VARW7- zGn>lcUfCsbX>qZ|Jt(6W_9HLSDu_#0CKiwFu@jUAmGh*ONK6&vo?;bOLQn*Oa50nG zJECgH9XOndx34xRxEMO91mTv0$3dTMVJpQ82`M&1I#9LHLG{WRuAd{hAFi8v$!|v$ z89KG*og^ni?qTJ0mcp`aM(mj`X`lubf)!H>A@Qy@L-n<1#~LEl98NS2M^gREq@0;6 zn&WeMRW3>pWFn8=!8>8XU)JY+j%f3<44o^_ZDBQRBL+9Bf%#1;0&p^|4C z1GpS=T)M7+DzEMPGq7$MRMkjsNscKvCDXtTRc+4pu~ws#Yeutm<4Xfn=^f$A!s>d3 zfW>NZhNMW{Z8Mqi3Kx*$9M$EdX`x2A8rRz$L6#_8Fvtrc34%aqe~5qu`_AKogPoT) zl+1AtYr3Ox4|T{rWRd|fKS^oWS{pAjRxKzd#jM0y$Zsg&>v;Vt!o@u#om#na#_FZ_ zkPZuMQmX8wl8Wu`Av2pHI#{gWvn?%sMK;g$bFkk29#-D~H&IDE+pt{05%0*d228CN(8loJ1#TWCCA>)2$o&%4n^P_qjxe+iyxQ_f6mKygCFa6^E{ zAlvNRJbdq&m1sF}*aR|0RC0=BWs;I>|3;C{@FNfvSUW&qJI^wHPnK0&nB2muYve|B zO$Sa7!m;ASS&A1m!^xQh#j%p#*U9vS2_{S*0*?Y~i?f$-aWbI$Jkh2T9x#W@R{%-i z$WCVW*p8N&t+)78+)p6IQ@$Vyd6L`mVrn^!=+QC~f7b0au(N*8DhEV?A|sUoJc-I` zc&Ur0b?H)lovj$c?-1d<(=IS>%=6&Y3Kn^qaSL&Rn&(dQ;^5CuRkAavROubY%&GCc zbAe9{{J_lK%!}st^K%tu3a3H-p6yTNY7>IF~_R?Mr zhaf%WZR9k>U7h>L*lZe?tLjs2K$0OWi*ec>nKSD~Y1eeei-1Qmo2xZoKYQA>FRB3* zCw6if!r$=>C@<4*$IQmgEREnI=~(#PjQeKE)rh14nWk>VA%e?{Yw=bw<3m2h%h|4r zCE98r+&X0x#deX|J$gTkoVsF1E(=&fT8M2sDh)3`TRB|B*3g+x{XIbmhsz6Y0PCr{ zHgIB!2%O-mC}Qy}m**W(0o>5iG#w;guj(LBq+G2G2YldXuVbrn4M#5CtH{awR2y znSWbgY2E0-^)W!6^4tvpr{z>uyKQbR>F-%c;Hw&+9UM=c(Aa|_w`)-NM6kHhP;@1& zd~os-s+-crY>-lN-xon7WF1J6o&?hgksGgVS*H;qmvJ-$CAAtb#;@dZBB{-1wc#yE z=m4|GuM~ocO^z0!AGA_K(>s#NX$-M*iDV$+C@Oxj%WzB$b4qe2rF*;3kT5^5P39lx zd96}OSE-iDvqNNFJl;f7;P)Vf!~u<+u}UaLMYC z%TQa!MYhBp&NFf{nmie>J+#`Qh)?)@oJ98`Sw z$k0Ry3H_4~_!c$)HcVHgi8rJp?~m%HECJj6%p@g_w7G3d5B1zJc7*N5eelcRcOu)5 zuKOtl@CP!J$=w|k1c~LPtKnpTBzHJNjaf(`?1V(Bz5%&9NAmr|#g2SMy3cT|ysSYS zy!vHXQzeAembr;cTy7s?ygdKfSEzg7x~%<$CndMU^AiKsfW31!V-l_sEz=`0560TT%`0vJ1yt~(;UmHy+s3hu4DcKl zD4F}MlUp%xi<7qUSm!Cnh$@mMh22j0^wi02;U^OS%Tgh|_Cj2?@NOR<%E1UH?q5>dKk{&iEk}US! zQB}frTs~+s+?YbHpUwd?gyYCdQuWL7hj&CpzII{Q!O~4m(yIg5-a|y z+bY;moo0(gl5zlR_w7x^%xhOs*b3N&WwauLJ1OicPbQs*kW=xJxU*8zV%uRGd~R}; zb-HPUyM6$XmE5=s#}PGL0^E2}5C)Q*#`ZRpN(IA?o4{URvF7v`v9{|lttPn!gb*Z! z5b`W4{5|8$*(K25c3>_bVd;hIJ76r5pLc8!hA1`FjvqSKXuMS zlTBTuY37zx9cUXOznr)~4(=*Mg|}(9Xr7dMW}M%2q{g>}UxSt-k$+?cRu?oP_#eZS zQmbMXg&kh(E9f9&3Nu4Pxwvmx-TY3-@&IHB|1#T77GSQ<#3z>qG>;rX7UA?dX>oCc zC%Je4*G%JbNMsDlV2GCJW6I+Xkr(GaxVJ-C0}8vwRiHv}$+_k8PP|#eBu@VLaw(_n zIyOZ0t#&!{*0a(+Q)Bhn4Vcv@T@c=NJZiAPO9Q|=!<;5h*t$hO>=J!TpF4mJAKZ|d zxMw|Z@OtU!D4b4d)(xDS&uzdN>#25$;>qHsOzRP3Lz}X45f=AZE`!S?u)|lDw^h@W z)*nJ2lmW)!k#Z!o$-BL=gq0B%lh@DJkbOP6+@7gcpo4HFE`Q@T!y^qZv1+yvUdMvECg>?yK9(#<{*X2Oh zVnIZvru@gAW2NBnu_@`uR6MaEJS8{dszvK)*2qiydjCqpxqa+2mE4TXpI{|8ci&2k zxxXV4vk|yp^Fkg9YrsKDZO>_mhh?fa9Z`HT^&U1AQx_W}92s|Hz&%5d z=NA`@!&s%U6*g+sF^6IW!K4-Ad{NmuU<|FqXR;{Q)CLQ^W*~eM(AU#U)fiG+f49Mq%nlt=R>5M3UGu278`$+`J*;c@}sopKAJFv=w)B2#4_jc2KFH_*dj@WjbBVe%sCJhkTCwC=J6 zC2w$Vx;$*wpd#^9V(b;2?6iuoeBxg1N;u=@IzBfjz+k!6P6a#Yrpj?xvC^)sVTD1B zoj0Sxv+VuE>%O3>!=rC&=3F15yCbfls+jX0)F{8Mp4Lx4!+l z>P)Y@0rpH6@$)DXr{9t_Gv3i1_QYN>AI~uMJ`XbUz?+vK`zb^4wl3YXBsZ&)o(I|O zC4S+uI9j|0<&f#&7f$+BK-Jx7=b7^e>Yf9L)YG~4jk0kXbqF2p0aU(nm}t{4VKRya z^9U@i;3!fSVZmLje8TT%e6N5NxQDA~vz@XIZ~)eb;k?^n6%w+oq$LyY>$ZIO$_V&6 z&GX6|IuzJl1s4r1M(xli`{BK^?)gRMce#a0}puhkm8hyoc(-#KU?ds zQNGCGgeIq$V$BPW@Oj852>S@pSc;d_T?;BOU7YLw4wqtSLDjz57F2MZG5hP{NZlN# zXS-y?htf*A=(oVgB9zEy8gpVy6^x{x##lOC+Q?U^ zw6M_RScq7PTkipCIZ&(lCat8sX}6%J?$uE!Vqmgglbfd-g)|91H{Ewh2lanUEY?w z9d0DdX<%7(&N0cxbj2w{{5DFljilx!0hY)N1f41DrQ59#sr+=LV^+N8xC&)%CldZ>bhljd#bu5&?=;JE*y=Q6Q}KNOUQ z!xGwIH<@_-;*f>I2T-}q0e%;->XTQOIbMRB>cWgc#Wm%KRh28t({3pgo2&3FI&N;P z{4Uag5!zFAosP>~xCT2yv@ zOy*vc6&xKCgw_6>DC)|}dv@tX&7sKLQ~N}IEUvg{X!CT+Y#q3d4Xz-C+*r(ys9o5^ z*0_|sLgZOO(C-^yo{Sa^#o0*DK0pXD)-1#nknT}A@dEFR9^|sNSCq5@(#&-vI}O_5 z^(?H-VxVy#uj|L@Xj>2Gb0x@^a0yVN~U_-tDC~9w+7_8 zA-4V2l;?1E8#Q0R8EOmaV)s(+W_@m&XnXF&UqdBEBHCuPgbqrZAHn1j6J#ZY7FK<^ z9*m1jBe`*nlB-p}hoUmw3hPWIf7=XaXNlK)1G{aGtxPL95l~v1QwMGBmcB<~0hc>; zZJvNKV~gQ(gwp`$5KOVMW_h9cis?cs*J6T*io%H{!)h)$wizjx8$RgZrp&>RDamL~ zSyvD6Ktk-|Qf4}gG+ShmT4w(0ux1X;X7@9Za2zACo00h)3xn1S(3M)h_Sbgi^mbem zcVe5FvPx@lM8o7e#Wms_qizx!=PAjX&T+Fz;V!`LEH5|>q{>;1{=Gq78_!X5iDpNl8_#%hz4c3TbB)2_a>1u&>*PXcTBnLj+TGv~Yv`VxbbXP}Q z$PO;~E9lA}j*vG^AKSIeS{-)JoPIM?&Te9xxzzB;` zsee|L-t*3aBYJjUU{^-CG7uY2Ph8otB0pMYnVH>*1^ygR%28YcY=*{N=aZk3#*dA2 zqgDFtc0}d-z(op~)%e2qflH*WGFZ2eriTYd!jPl14V$r4t$%bDWm=xoa7CPP4G|Y9 zd&O~(mOZt(gUeSEBj%nH31Js1ORnlz6}@5@cZ@3J+(}s@x0zfPQmhk)rFil-S7zLW zqv(Bcv5i9`XJkl0nstxMOGsoJ7Ux2{aNL)5#Vb~l%@Nw=PPpbiNg>4*bS&@knVeS1 zYRab5U}avvLZZ)AkMeo@*qEE>e8TZKd?=?Sc0*2#6;ljkFDhJezwC>5ZVPLo-Wjt} z#E}OG(u&HK?men#UZP11Fj8Ue&&r-MOsi>%LEXSL2~PHDgF3Sm-#NeNotK*XWncBh zIHbg`&32vdv>NlFphD*9rMA7!9WMMGhgIsM(>Tv(R0AmcZU^-wwQ*CLA?5Q%<^gu$ z2&}@|?bHG>7nuhod~b)PW$OW+=lLFrO2`wt@@F6`R`v$0MW0qPacNMF+^f=gZ#Zv} zl1ZowoLW$u*7tLpzz|UC;SzA4eTtI3b!3%P1KuL?zq_)`KDymRrj1*n!?ql}Z!n0Q zuH8*!0u=X46nN`ivdrBml@-9P=P~nxF+HhlgTp)pVUI;c;!~+xzb~kCYHocfDz3E` zi>XERXsw!-$8#%2dxAEE67L8q=61QUWBU$XOP_XG_O%%Si+4nJLq6mXEYA&BG7{F1 zbJQRq@BEpE!U~B`VOX^(xlt;QHx-XFHwYCimr(V&(RR*RsKHWkMBj^C25YU3ina^L z)2LNKb)idw0|B8VE}Fd{T%=~+6yY?!$pct!4iiEPvGD?@)zkx6h+&_(Imrhh=PzFD zP7|=*kxoADR3$~#9%XfbK!_xrEE-IR2DuH zpJD6+UnRT-aj~$nLz!F+hlV(=5FXy86!8A2M!lePGb~qPiN=k;u1uZs=f+I_;#KB{=pL4_ zE?oJ+=-Dod$a6IgsgYJ7f#E1=#mv1{**u+x<-J~D84190<^;7gYkHZ6mWEXasI>Mi zyyo9)H}>AR_eXHSUCk?L1>zdu+kaP}Mx5XdR4k7MC7fB!jdS`vh-&JxZ&97|03+(4 zH>2Y74C`K+fJ3IDaTxJB`%{Rx%`tK>F@Dr7E>aCCxAg&XWKw-8u{)B4u7Igdb>ktY zh-jYqZ}`J<9u8dNstrm)B1l+MggUL?KsM1G^TQE&HL&0ok#o}5{o-2>9-bzY7+=9R zL0ENF`>b7D8rGmHwUIpx@JWe^e6;Y5m=em{UC0L8iz)?4iRabZcQJRMCJBh0&dxWyvjg%90lXFw<1Fdy zurP~Ix(V>1Th{U7UpvG;L-avtTs@js(DJZQ?EAts!b;f2;nJz=Woso~JIOP!ZNcma zGDo}G^kQq6n6(esuRFrp2~7F|(~+h^-3HmwcW4AG&#-I_>tLaHNt+e~T%n|-*|A@D zgQ^m{dv0^ELV|GMUrHG2xB0+4UC1HHZ`N>(eu2*+hq%pBJ~7ToTpVu6$fE9+I96)& zQp{~}B@MF`fQGo)RJfWM0bdW9;viYAOZX-Ww&Y1_M`6>A#vTe5SOz2{4&fq=g!tw? zI`0S^v333kFogbV-8UV@0)7Y<2cKI{`b=B$Y8Ewg|rR5l>OJ%lXqrEHkN=~OdsZ$k>}w*w0`3)Ys_ne*if{%~At`r0R} z>vbP5FwbN~J8{Bh>C!69hiCVfPyt34ZFj4&VV&Q8GM z3YY&5aFtC>r0xhJu1AODb;@tmQUPQ}WLsC`x(9umS-catx7^GTPr#I7$?XLw^*~>9 zgZkENWN4ntw!eZrwsg0Tw5pA;XYYe-&^72FW5d(7$F}DRiF@%-)AtH+pi2~eZTM*o zTspaO@TQIdmYuE)NTC6F@PklC5!{p71a>DudAS(e^*?2ImWCWM+p5=?tt%9&!e-Pp@mtXWLolF z1V$eLV&c4}knJjU6d$M{RGeY-)A-XHaqaB>%au*{9YK4hw0RF`H%>-H6|N6sQ+()| z{o*SnTh>HA8e3+->l|5Y#U$5~*w$R^MD}9euKSLVJ=ePX{>W~7DhsS;fL<8qrg!6H z1eUE1FguRll?4`SoXY7Mlq=(X=09o?w~xO4bNYj?9KE#6W-mAGwsS0GKEXG3(k#`+x777E;y;|XYGkjEoTqD z2q|rvZgg^E2QWa9Ss`-PJ$ks<>N^jnnUFfbGb2kc-N^@139%D^Ida+M&`~d!fjuNVUI>@65A&kokeI?~R&GX}JfAV-=27rKLy0U! z9zqA0kat5|LfBiJx|zv{bc7y1OK6dWG2~QI*Be`mWW>T>U5PpPk-h}FA<%Tc5Rw1} zds>$#GuF-4PREGsc4Wnqo~LQ(twn19dySV(o@veA)J_h8y^G%+a81N zhAQVFWYI|0+)N?HJh?~@z6CLDT(?+PQ;xvppu!=r*n84(Ms;;mqC6VbDXIeVqiiI@4 zMKkw)rMVtp)8y&F~Xs^;O24wiRXeZP2ZF+Q$}<^uJ#ZzHV>|V z1=n@=uhciUQ@7ipCBK!~WISGl*4H(+Q?>Vpc5gi+*Dr8si;J}i8M5;9&;{V#%%yqF zCyw$V;oBi#wO4$3H~nO99}X)rtXw^vll!xK32x}?ItZ<%+Z)gSOu#szx^ozrtrsxr zIZ-x%N#-!Lu+skl$i@f_KmzJv_E_3lur}=fApxjVx z$qARB)7`7VOUznh8MidiB1{RTjRwf2G-|n-n#L5#`WsY|bbDKHHM1p`3IsPN@JiUk zC^oI?8nHjwPA-R}CYn-oXA-pyEQpHhP)S)2RPz}k*o27fonUeK9dy1JLOb7Wx$!Ws zg`0U1#N)8rt+JJ(rNz~&72b#|)O|M49=Hpgd^%7W1PU|k6W8=yQ2DxBx}}|(NH#}k z{Ozc+XYPFEjSX7KXeT@<38p7YwsO{!t-eF&e0GsEBPIH!H5IU+NzLe&*`Vx7T|Jj` zM{d<@Q_{UJ}P_xCv{Aiz9gj;4?&)ALPKMfFR4Zk&if98WR*jb-&KFF(Xb1_CCCl%R1*u0Mzh%|)Zn(_KH_-_&~l~eHUoLeUQ^g zwl}fZ<0}Zc&c)&E#FjP1PvYZ-`1V{2ztUv5zV>=E^2Q__MF*xMs9LnbZ?N`nIVi7 zBA*l&GaEBeP^gZ!aWc~s`slbeXzp4eTl|I|AWRGF2IMhFN&w?w2)ZTWLwmO%C2~+W z&q+B=rCIow>7lg)Uu7!PzdCy}0ej{VaCe0Etg1bOO)uDj!HVLfSc-|>^{{1M*pjUh zeaW_5P*X+E<0`t`3S7Ks7F#$vN=!GZgE|y z`EE7IatPUu^!Xe!9g)}o6Q(<2<#Wj=-|*ik!28 zjX;H;H^7~Uq#^6E%_)3DHed*+Y?@ffA`=B|1}|2p;y5Nj6r_$VA2~o;Wk=D$Rvu;T zAteTl!7Z^!VTPb3kd3aUt%snB5<_4Qbuu42z3BxW6)hCJZDADEwB)*y#W(Z`sn%;A zib|BJPFEU3AI6>Ft*8)qu+p2_3z)|khQX+KfOh2+_BK{qI}vvPc6V_lpS8S->e|?L zq`a;}c2{{>ZCP#_ik?yWE7i-b5_PxZ$|Or_g%!h5))zU7iQG`D1B|gIo3=*lO3fN2 zs%v4rK~!OovNt>`aq4Rwzzl^KO+qkE1aqh(>~?T6Zzs&w=WM_ol8f{7jQ1SYYIw9l zsa-n*+PW*VpBIxLivKz5B{ECm6tg3pmPHrm2hpAKip(NT`;A8guvXht1CEGnh1T)? zGQ25=GmMnPThR3q8gO*fK8HnceVCC0qi^?O;srqh6cu3amW>lUY=}KJ3Q5`~*-a-8bWJK%PR#mk!n(x6;Z}OGh3uPOTmeLE~Ei zxek)DC@}D}<2+3bTzC!JO2#zgore@bZ--SVu9ljJjHPT3&O^FuhXSj8z-35R=IyvV zSDD{ zjq7GxotUtW3{sDM@rLZVg)CFGKPXHHoxc<r7IQJ;JA9m{ zop6k6x;#1aIt|%-?oAcXZxK=at;dS)xQ@tYpZJ}c~sIjs29C8#8;z1}2Ld2PZ8I;HIsGZ z>dg)aOE?f3qH@3x#(mGYKwO?SKUm7*eF<;Jb;G((yCb|CvgSu{rP}$`^s_!`p46<| zEVhk9dk=JXR<=|&OLcgfG}(DTOm_emyB`GtThy!!8;U;&%ddAMm1)r8n!EY(0HBP3 zSGXLtk(x{s-`g4HF1(j#CkI@DtKr=e*B!!T+<4VG9CDtU91sH>xt+Rog!DWqf6Zd$ zt0t|Fpp(tRt}=~-44bvS1Grnpj*h_MB2cLlA{Si4Miq}f11*6Cq7=sF?y;{ES>YhA zrbV;X;anQB8`VrXNTe>2X0t~<0URk$n^HLgbEJmwTi@OmhU&px%mEBeAd@0kKs3 z8r+b`NmJyYQ8GkCntOYa6sJteDUvg|rLW<u1-9~ReA=a6Bp zqJ}W{gM`QoqqeXHB{4WF1QoHYD*LLcuzm^a1*2o{|@t?-ZPvPWA3bVP;av;V%E?x;d(~f>wUNj!+Ro1gHPhs+J9t(>NYpXc{shdxW4bl2r zVMV@&+VVQpd|2^A60ZT;Y~rHJmOTtqN9)zIy~0BYU6~pGpJM%qnkMsDTL_9DvXL8L zII`^{7ZL28XNB`jMpspr6LZ7MU1TCh8>$L_VyU3KHr4tJLHuU!9Kez!(p>qX++y7U z#>GnFRV+nPtu+zj#&}i2$Wo3pNRGgTFS#btu$rGcgvt+*7JPiyvI0-i8lzpe!m31c zYlCVvljBD8f&dtwmD<|fE1Sxx)JNE$(YUv9ndauKfLn=++{bPZE_VFmDk|c|1T!Go zd9oGI1inqQ;<%7_9-fO#l$6x=S}1=A9TUsK?~H7xg{KgD({q9R&Lw2>G5Urwz-YeK z#@?N+{RmtPUW3DbSmzEqQV;w>GgA_H(7CNx8&=IqvQQRxUMhng4C{zWGkXnjJ|sgC z2|M45oF5wSLM4^}JtUf%I5szI*w3JPd#jNx*fRYqwsAr>yVV%W~+2iJi%qldwJxk>Au z@NTpoH{+U!GuF7=p!fXP<&>3g9ZnQh8NoQnW$49OkqmdkxG`&w%h#+yfF;IvX_(*h zOfD8FEv{;9M=&%B16|0;$8+P@zS{ziKvsi??Jbn3GhOggLBO!~Gt%TqC!X_@T{1H8rsH7efmUDspsJ?dEA&OfZ$3l|&#`~WdoA>(vOt>UnJFEA__6DiN zVaV8ew(1_Jw_wD1O5}c&D2W%ai_73@#}FzBbcoN&t4sm;h#h<+T&SI4+`JHbYtfXo zA`*9*dFH2=>?Yib(8@}z_Kh7c{lQ+qc)DzW2IC%k{cM07*_fEQEVW>~2p@JwMDu<=Jv zP~8PoLb$<&C?W{7ZTCjjBNV|E_>c^E4YE^H(;%OGfXUQQXcfGX; z$`o~SoY2k8Q-=i@u0XrTeH<_nrWMG_Aqsc^mti0|Rjkh!rs;$Kc_HC!;c zIzv1Phto*`!6jEjlwLb7Ud9Mdi)xi%degJGGOf)Balrx8k^(HZ7K2&p^;L6?X^1eh zxD)vA+`cS;!K_8YcqMU=-C}wmMQBK1cF7gnPVX~q0e6AsC_8Qgn1=}h2clBD#>-6w zkRdflswwRQ2?KjG@*aO>Pe&)_v{Z}4YJbyGNmi>GKce^0n37lcsdZl|H+1#hL(54b zck$pl;#-=>r_?BSL8em*ZCE!Q@UbRqBw1vRspGP8G;sX8;0BE@UgH29VRDVX&6F{=)A=xj#SQewxn11jX3I7(rN}5Yp=QN z+v98afgk{p5#0)yt)=Qv2}m27LnxjN3cC;CVgXQ^w+OhZVYwz-@nVCY3_uqWr8X^w znaj-a+N|PLEq-@_ck3;<8CcD!H?Gg6IzOa!2i8IgVt$wEO6@%?;fE*h@VF+t_H%L| z)!wkC)9LIc4(UA%wA-}~3X9=8FDA#_a^mv6WbhVsfyId^S&hj~B-taD9EB4%!NGH& z1DLH>fv`G!XF0MVrRf@A1hOtSb8^K7t&9t#9?F^0Dq<=3ynRpPu>YB93s;t`7y$I10* z*o_3|xU+aKBXdyk9=QAo!a(FkItUo@Ug&L#5TyjqmKw+_-UZjh0tN>wGR{cabFNL4 yZZ{tAz!sJ{LV9mt(Iqlo;R3*A>FJliy&zRpNC*$~3RM5}&;K8$zmt%L=K%okP(fM% literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/house/3/logs.json.gz b/src/main/resources/assets/creaturechat/builds/house/3/logs.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..01e2963f89a74d2e2025ee499b0e9c084662e0ac GIT binary patch literal 40907 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 Date: Sun, 31 Aug 2025 23:22:21 -0500 Subject: [PATCH 11/30] Initial build goal integration --- CHANGELOG.md | 4 + .../com/owlmaddie/buildrec/BuildRecorder.java | 135 ++++++++++++++++-- .../com/owlmaddie/chat/EntityChatData.java | 34 ++++- .../com/owlmaddie/goals/BuildPlayerGoal.java | 133 +++++++++++++++++ .../com/owlmaddie/goals/GoalPriority.java | 3 +- .../java/com/owlmaddie/message/Behavior.java | 37 +++-- .../com/owlmaddie/message/MessageParser.java | 8 +- .../data/creaturechat/prompts/system-chat | 11 +- src/test/BehaviorOutputs.json | 118 +++++++++------ .../com/owlmaddie/tests/BehaviorTests.java | 44 +++++- 10 files changed, 450 insertions(+), 77 deletions(-) create mode 100644 src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ce08508..0e10f87b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,10 +22,14 @@ All notable changes to **CreatureChat™** are documented in this file. The form ### Added - Document SPDX header and changelog requirements in AGENTS.md for contributors +- Build goal and behavior enabling entities to construct structures for players +- Behavior tests now cover BUILD and UNBUILD behaviors with live LLM responses ### Changed - Convert PNG screenshots to JPEG, compress, and remove less useful ones (smaller jar) - Compressed all textures from 32-bit color to 4-bit indexed color, reduced size massively. +- Build goal now uses build replays, keeps FOLLOW and PROTECT goals active, and system prompts describe build skill and types. +- Build goal completion message is generated through the LLM when a structure finishes ## [3.0.0] - 2025-08-27 diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 6582db46..5087386d 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -6,6 +6,8 @@ import com.google.gson.JsonParseException; import com.owlmaddie.buildrec.BuildRecordIO.Action; import com.owlmaddie.buildrec.BuildRecordIO.Meta; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.UseBlockCallback; @@ -58,6 +60,7 @@ public class BuildRecorder { private static final List REPLAYS = new ArrayList<>(); private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); private static final int MAX_IDLE_TICKS = 20; // 1 second + private static List BUILD_INDEX; static { UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { @@ -95,6 +98,62 @@ public static void init() { // Ensure static initializer runs } + private static class BuildIndex { + final String type; + final String height; + final String file; + final int score; + + BuildIndex(String type, String height, String file, int score) { + this.type = type; + this.height = height; + this.file = file; + this.score = score; + } + } + + private static int heightTier(double h) { + return h < 1 ? 1 : (h < 2 ? 2 : 3); + } + + private static void loadBuildIndex() { + if (BUILD_INDEX != null) return; + List list = new ArrayList<>(); + FabricLoader.getInstance().getModContainer("creaturechat") + .flatMap(m -> m.findPath("assets/creaturechat/builds/index.json")) + .ifPresent(path -> { + try (var reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + JsonObject root = BuildRecordIO.GSON.fromJson(reader, JsonObject.class); + JsonArray arr = root.getAsJsonArray("builds"); + for (var el : arr) { + JsonObject o = el.getAsJsonObject(); + list.add(new BuildIndex(o.get("type").getAsString(), o.get("height").getAsString(), + o.get("file").getAsString(), o.get("score").getAsInt())); + } + } catch (IOException | JsonParseException e) { + LOGGER.error("[BuildRec] failed to read build index", e); + } + }); + BUILD_INDEX = list; + } + + public static String randomBuildFile(double entityHeight, String type, int level) { + loadBuildIndex(); + int tier = heightTier(entityHeight); + String t = (type == null || type.isEmpty() || "unknown".equalsIgnoreCase(type)) ? null : type.toLowerCase(); + List filtered = new ArrayList<>(); + for (BuildIndex e : BUILD_INDEX) { + if (t != null && !e.type.equalsIgnoreCase(t)) continue; + if (!"any".equalsIgnoreCase(e.height) && Integer.parseInt(e.height) < tier) continue; + if (e.score > level) continue; + filtered.add(e); + } + if (filtered.isEmpty()) return null; + BuildIndex pick = filtered.get(new Random().nextInt(filtered.size())); + String path = pick.type + ("any".equalsIgnoreCase(pick.height) ? "" : "/" + pick.height) + "/" + pick.file; + return path; + } + public static boolean start(ServerPlayer player, String type, String height, String name) { if (RECORDINGS.containsKey(player.getUUID())) { LOGGER.info("[BuildRec] start ignored already recording player={}", player.getGameProfile().getName()); @@ -125,6 +184,15 @@ public static Summary stop(ServerPlayer player) { } public static boolean startReplay(ServerPlayer player, String fileName, EntityType entityType, int speed) { + return startReplayInternal(player, fileName, entityType, speed, null); + } + + @SuppressWarnings("unchecked") + public static boolean startReplay(ServerPlayer player, Mob actor, String fileName, int speed) { + return startReplayInternal(player, fileName, (EntityType) actor.getType(), speed, actor); + } + + private static boolean startReplayInternal(ServerPlayer player, String fileName, EntityType entityType, int speed, Mob existingActor) { Path dir = buildRootDir(); String actual = fileName.endsWith(".json.gz") ? fileName : fileName + ".json.gz"; Path file = dir.resolve(actual); @@ -159,7 +227,10 @@ public static boolean startReplay(ServerPlayer player, String fileName, EntityTy } ServerLevel level = (ServerLevel) player.level(); Mob actor; - if (entityType == null) { + boolean preserve = existingActor != null; + if (preserve) { + actor = existingActor; + } else if (entityType == null) { actor = new Pig(EntityType.PIG, level); } else { actor = MobHelper.create(entityType, level); @@ -169,17 +240,19 @@ public static boolean startReplay(ServerPlayer player, String fileName, EntityTy } } MobHelper.initSpawn(actor, level); - actor.teleportTo(player.getX(), player.getY(), player.getZ()); - actor.setYRot(player.getYRot()); - float sp = adjustPitch(level, player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot(), recEye, actor.getEyeHeight(), actor); - actor.setXRot(sp); - actor.yHeadRot = player.getYRot(); - actor.yBodyRot = player.getYRot(); + if (!preserve) { + actor.teleportTo(player.getX(), player.getY(), player.getZ()); + actor.setYRot(player.getYRot()); + float sp = adjustPitch(level, player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot(), recEye, actor.getEyeHeight(), actor); + actor.setXRot(sp); + actor.yHeadRot = player.getYRot(); + actor.yBodyRot = player.getYRot(); + level.addFreshEntity(actor); + } actor.setNoAi(true); actor.setInvulnerable(true); actor.setPersistenceRequired(); - level.addFreshEntity(actor); - REPLAYS.add(new Replay(actor, actions, speed, recEye, recWidth, recHeight, palette)); + REPLAYS.add(new Replay(actor, actions, speed, recEye, recWidth, recHeight, palette, preserve)); LOGGER.info("[BuildRec] replay loaded actions={} eyeHeight={} bbW={} bbH={}", actions.size(), recEye, recWidth, recHeight); return true; } catch (IOException | JsonParseException e) { @@ -190,6 +263,36 @@ public static boolean startReplay(ServerPlayer player, String fileName, EntityTy return false; } + public static void pauseReplay(Mob actor) { + REPLAYS.stream().filter(r -> r.actor == actor).forEach(r -> { + r.paused = true; + r.actor.setNoAi(false); + }); + } + + public static void resumeReplay(Mob actor) { + REPLAYS.stream().filter(r -> r.actor == actor).forEach(r -> { + r.paused = false; + r.actor.setNoAi(true); + }); + } + + public static boolean isReplaying(Mob actor) { + return REPLAYS.stream().anyMatch(r -> r.actor == actor); + } + + public static BlockPos getReplayCursor(Mob actor) { + for (Replay r : REPLAYS) { + if (r.actor == actor) { + if (r.action != null) { + return new BlockPos(Mth.floor(r.tx), Mth.floor(r.ty), Mth.floor(r.tz)); + } + return new BlockPos(Mth.floor(r.baseX), Mth.floor(r.baseY), Mth.floor(r.baseZ)); + } + } + return null; + } + private static float adjustPitch(ServerLevel level, double x, double y, double z, float yaw, float pitch, double fromEye, double toEye, Entity ctx) { if (fromEye <= 0 || toEye <= 0) return pitch; @@ -263,13 +366,20 @@ private static void tick(MinecraftServer server) { Iterator it = REPLAYS.iterator(); while (it.hasNext()) { Replay r = it.next(); + if (r.paused) { + continue; + } double advance = r.speed; while (advance > 0) { if (r.action == null) { if (r.index >= r.actions.size()) { LOGGER.info("[BuildRec] replay finished actor={} actions={} speed={}", r.actor.getType().toShortString(), r.actions.size(), r.speed); - r.actor.discard(); + if (r.preserveActor) { + r.actor.setNoAi(false); + } else { + r.actor.discard(); + } it.remove(); break; } @@ -576,13 +686,15 @@ private static class Replay { final double recordEyeHeight; final double recordBbWidth; final double recordBbHeight; + final boolean preserveActor; + boolean paused = false; int index = 0; Action action = null; double progress = 0; double sx, sy, sz, tx, ty, tz; float syaw, spitch, tyaw, tpitch; - Replay(Mob actor, List actions, int speed, double recordEyeHeight, double recordBbWidth, double recordBbHeight, List palette) { + Replay(Mob actor, List actions, int speed, double recordEyeHeight, double recordBbWidth, double recordBbHeight, List palette, boolean preserveActor) { this.actor = actor; this.actions = actions; this.speed = speed; @@ -590,6 +702,7 @@ private static class Replay { this.recordBbWidth = recordBbWidth; this.recordBbHeight = recordBbHeight; this.palette = palette; + this.preserveActor = preserveActor; BlockPos p = actor.blockPosition(); this.baseX = p.getX(); this.baseY = p.getY(); diff --git a/src/main/java/com/owlmaddie/chat/EntityChatData.java b/src/main/java/com/owlmaddie/chat/EntityChatData.java index a20fb25b..ed448608 100644 --- a/src/main/java/com/owlmaddie/chat/EntityChatData.java +++ b/src/main/java/com/owlmaddie/chat/EntityChatData.java @@ -89,6 +89,7 @@ public class EntityChatData { public Long born; public Long death; public transient AutoMessageBucket autoBucket; + public int buildLevel; @SerializedName("playerId") @Expose(serialize = false) @@ -113,12 +114,22 @@ public EntityChatData(String entityId) { this.previousMessages = new ArrayList<>(); this.born = System.currentTimeMillis();; this.autoBucket = null; + this.buildLevel = getRandomBuildLevel(); // Old, unused migrated properties this.legacyPlayerId = null; this.legacyFriendship = null; } + private static int getRandomBuildLevel() { + int n = new Random().nextInt(600); + if (n < 2) return 5; // 1/300 + if (n < 5) return 4; // 1/200 + if (n < 11) return 3; // 1/100 + if (n < 41) return 2; // 5/100 + return 1; + } + // Post-deserialization initialization public void postDeserializeInitialization() { if (this.players == null) { @@ -127,6 +138,9 @@ public void postDeserializeInitialization() { if (this.legacyPlayerId != null && !this.legacyPlayerId.isEmpty()) { this.migrateData(); } + if (this.buildLevel <= 0) { + this.buildLevel = 1; + } } // Migrate old data into the new structure @@ -295,6 +309,7 @@ public Map getPlayerContext(ServerPlayer player, String userLang } else { contextData.put("entity_friendship", String.valueOf(0)); } + contextData.put("entity_build_level", String.valueOf(this.buildLevel)); return contextData; } @@ -319,6 +334,7 @@ public void generateCharacter(String userLanguage, ServerPlayer player, String u // Add PLAYER context information Map contextData = getPlayerContext(player, userLanguage, config); + contextData.remove("entity_build_level"); // fetch HTTP response from ChatGPT ChatGPTRequest.fetchMessageFromChatGPT(config, promptText, contextData, previousMessages, false).thenAccept(output_message -> { @@ -443,6 +459,7 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, BuildPlayerGoal.class); EntityBehaviorManager.addGoal(entity, followGoal, GoalPriority.FOLLOW_PLAYER); if (playerData.attacking) { AdvancementHelper.calmTheStorm(player); @@ -467,6 +484,7 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, ProtectPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, BuildPlayerGoal.class); EntityBehaviorManager.addGoal(entity, fleeGoal, GoalPriority.FLEE_PLAYER); ParticleEmitter.emitCreatureParticle((ServerLevel) entity.level(), entity, (ParticleOptions) FLEE_PARTICLE, 0.5, 1); playerData.fleeing = true; @@ -489,6 +507,7 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); EntityBehaviorManager.removeGoal(entity, ProtectPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, BuildPlayerGoal.class); EntityBehaviorManager.addGoal(entity, attackGoal, GoalPriority.ATTACK_PLAYER); ParticleEmitter.emitCreatureParticle((ServerLevel) entity.level(), entity, (ParticleOptions) FLEE_PARTICLE, 0.5, 1); playerData.attacking = true; @@ -502,6 +521,7 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use EntityBehaviorManager.removeGoal(entity, TalkPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, BuildPlayerGoal.class); EntityBehaviorManager.addGoal(entity, protectGoal, GoalPriority.PROTECT_PLAYER); if (playerData.attacking) { AdvancementHelper.calmTheStorm(player); @@ -529,6 +549,7 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, BuildPlayerGoal.class); EntityBehaviorManager.addGoal(entity, leadGoal, GoalPriority.LEAD_PLAYER); if (playerData.attacking) { AdvancementHelper.calmTheStorm(player); @@ -544,8 +565,19 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use } else if (behavior.getName().equals("UNLEAD")) { EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + } else if (behavior.getName().equals("BUILD")) { + BuildPlayerGoal buildGoal = new BuildPlayerGoal(player, entity, entitySpeedMedium, behavior.getArgument()); + EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); + EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + EntityBehaviorManager.addGoal(entity, buildGoal, GoalPriority.BUILD_PLAYER); + + } else if (behavior.getName().equals("UNBUILD")) { + EntityBehaviorManager.removeGoal(entity, BuildPlayerGoal.class); + } else if (behavior.getName().equals("FRIENDSHIP")) { - int new_friendship = Math.max(-3, Math.min(3, behavior.getArgument())); + int argVal = behavior.getArgumentAsInt() != null ? behavior.getArgumentAsInt() : 0; + int new_friendship = Math.max(-3, Math.min(3, argVal)); int old_friendship = playerData.friendship; // Does friendship improve? diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java new file mode 100644 index 00000000..36038169 --- /dev/null +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -0,0 +1,133 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.goals; + +import com.owlmaddie.buildrec.BuildRecorder; +import com.owlmaddie.chat.ChatDataManager; +import com.owlmaddie.chat.EntityChatData; +import com.owlmaddie.network.ServerPackets; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Mob; + +import java.util.EnumSet; + +/** + * Goal that triggers build replays. The underlying replay is handled by + * {@link BuildRecorder}; this goal merely starts, pauses, and resumes the + * replay as the goal starts and stops. + */ +public class BuildPlayerGoal extends PlayerBaseGoal { + private final Mob entity; + private final String buildType; + private final double speed; + private boolean completed = false; + private boolean startedReplay = false; + private boolean finishing = false; + private String actualType; + private BlockPos buildPos; + + public BuildPlayerGoal(ServerPlayer player, Mob entity, double speed, String buildType) { + super(player); + this.entity = entity; + this.buildType = buildType; + this.speed = speed; + this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + } + + @Override + public boolean canUse() { + return !completed && super.canUse(); + } + + @Override + public boolean canContinueToUse() { + return !completed && super.canUse(); + } + + @Override + public void start() { + if (!startedReplay) { + buildPos = findStartPos(this.targetEntity.blockPosition()); + } else { + BlockPos cursor = BuildRecorder.getReplayCursor(this.entity); + if (cursor != null) { + buildPos = cursor; + } + } + if (!this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY(), buildPos.getZ(), this.speed) && !startedReplay) { + buildPos = this.entity.blockPosition(); + } + } + + @Override + public void stop() { + BuildRecorder.pauseReplay(this.entity); + } + + @Override + public void tick() { + if (completed) return; + + if (!startedReplay) { + double dist = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY(), buildPos.getZ() + 0.5); + if (dist <= 1.0 || !this.entity.getNavigation().isInProgress()) { + EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); + String file = BuildRecorder.randomBuildFile(this.entity.getBbHeight(), buildType, data.buildLevel); + if (file != null && BuildRecorder.startReplay((ServerPlayer) this.targetEntity, this.entity, file, 1)) { + startedReplay = true; + actualType = (buildType == null || buildType.isEmpty() || "unknown".equalsIgnoreCase(buildType)) ? file.split("/")[0] : buildType; + } else if (this.targetEntity instanceof ServerPlayer player) { + String msg = (buildType == null || buildType.isEmpty()) ? "" : ""; + data.addMessage(msg, ChatDataManager.ChatSender.ASSISTANT, player, "system-chat"); + completed = true; + } + } else { + this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY(), buildPos.getZ(), this.speed); + } + return; + } + + if (BuildRecorder.isReplaying(this.entity)) { + BlockPos cursor = BuildRecorder.getReplayCursor(this.entity); + if (cursor != null) { + buildPos = cursor; + double dist = this.entity.distanceToSqr(cursor.getX() + 0.5, cursor.getY(), cursor.getZ() + 0.5); + if (dist > 1.0) { + BuildRecorder.pauseReplay(this.entity); + this.entity.getNavigation().moveTo(cursor.getX(), cursor.getY(), cursor.getZ(), this.speed); + } else { + BuildRecorder.resumeReplay(this.entity); + } + } + } else if (!finishing && this.targetEntity instanceof ServerPlayer player) { + finishing = true; + this.entity.getNavigation().moveTo(player, this.speed); + } else if (finishing && this.targetEntity instanceof ServerPlayer player) { + if (this.entity.distanceTo(player) <= 4) { + EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); + String type = (actualType == null || actualType.isEmpty()) ? "structure" : actualType; + String msg = ""; + ServerPackets.generate_chat("N/A", data, player, this.entity, msg, true); + data.buildLevel = Math.min(5, data.buildLevel + 1); + completed = true; + } else { + this.entity.getNavigation().moveTo(player, this.speed); + } + } + } + + private BlockPos findStartPos(BlockPos target) { + BlockPos current = this.entity.blockPosition(); + if (current.closerThan(target, 1.5)) { + return current; + } + for (BlockPos pos : BlockPos.betweenClosed(target.offset(-1, -1, -1), target.offset(1, 1, 1))) { + if (this.entity.getNavigation().createPath(pos.getX(), pos.getY(), pos.getZ(), 1) != null) { + return pos.immutable(); + } + } + return current; + } +} diff --git a/src/main/java/com/owlmaddie/goals/GoalPriority.java b/src/main/java/com/owlmaddie/goals/GoalPriority.java index 6b5bc536..66074103 100644 --- a/src/main/java/com/owlmaddie/goals/GoalPriority.java +++ b/src/main/java/com/owlmaddie/goals/GoalPriority.java @@ -14,7 +14,8 @@ public enum GoalPriority { LEAD_PLAYER(3), FOLLOW_PLAYER(3), FLEE_PLAYER(3), - ATTACK_PLAYER(3); + ATTACK_PLAYER(3), + BUILD_PLAYER(4); private final int priority; diff --git a/src/main/java/com/owlmaddie/message/Behavior.java b/src/main/java/com/owlmaddie/message/Behavior.java index 5a465884..191cca7d 100644 --- a/src/main/java/com/owlmaddie/message/Behavior.java +++ b/src/main/java/com/owlmaddie/message/Behavior.java @@ -4,17 +4,15 @@ package com.owlmaddie.message; /** - * The {@code Behavior} class represents a single behavior with an optional integer argument. - * This class is used to model behaviors extracted from a parsed message, where each - * behavior might have an associated argument that further defines the behavior. - * - * For example: "", "", "" + * The {@code Behavior} class represents a single behavior with an optional argument. + * Arguments may be numeric or textual depending on the behavior (i.e. FRIENDSHIP uses + * integers while BUILD uses a string such as "house"). */ public class Behavior { - private String name; - private Integer argument; + private final String name; + private final String argument; - public Behavior(String name, Integer argument) { + public Behavior(String name, String argument) { this.name = name; this.argument = argument; } @@ -24,16 +22,27 @@ public String getName() { return name; } - public Integer getArgument() { + public String getArgument() { return argument; } + /** + * Helper to parse the argument as an {@link Integer}. Returns {@code null} if the + * argument is absent or cannot be parsed as an integer. + */ + public Integer getArgumentAsInt() { + if (argument == null) { + return null; + } + try { + return Integer.valueOf(argument.trim()); + } catch (NumberFormatException e) { + return null; + } + } + @Override public String toString() { - if (argument != null) { - return name + ": " + argument; - } else { - return name; - } + return argument != null ? name + ": " + argument : name; } } diff --git a/src/main/java/com/owlmaddie/message/MessageParser.java b/src/main/java/com/owlmaddie/message/MessageParser.java index a7e410e5..7d441c66 100644 --- a/src/main/java/com/owlmaddie/message/MessageParser.java +++ b/src/main/java/com/owlmaddie/message/MessageParser.java @@ -22,16 +22,16 @@ public static ParsedMessage parseMessage(String input) { LOGGER.debug("Parsing message: {}", input); StringBuilder cleanedMessage = new StringBuilder(); List behaviors = new ArrayList<>(); - Pattern pattern = Pattern.compile("[<*](FOLLOW|LEAD|FLEE|ATTACK|PROTECT|FRIENDSHIP|UNFOLLOW|UNLEAD|UNPROTECT|UNFLEE)[:\\s]*(\\s*[+-]?\\d+)?[>*]", Pattern.CASE_INSENSITIVE); + Pattern pattern = Pattern.compile("[<*](FOLLOW|LEAD|FLEE|ATTACK|PROTECT|FRIENDSHIP|UNFOLLOW|UNLEAD|UNPROTECT|UNFLEE|BUILD|UNBUILD)[:\\s]*(\\s*[\\w+-]+)?[>*]", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(input); while (matcher.find()) { String behaviorName = matcher.group(1); - Integer argument = null; + String argument = null; if (matcher.group(2) != null) { - argument = Integer.valueOf(matcher.group(2)); + argument = matcher.group(2).trim(); } - behaviors.add(new Behavior(behaviorName, argument)); + behaviors.add(new Behavior(behaviorName.toUpperCase(), argument)); LOGGER.debug("Found behavior: {} with argument: {}", behaviorName, argument); matcher.appendReplacement(cleanedMessage, ""); diff --git a/src/main/resources/data/creaturechat/prompts/system-chat b/src/main/resources/data/creaturechat/prompts/system-chat index c0b020cd..3891252e 100644 --- a/src/main/resources/data/creaturechat/prompts/system-chat +++ b/src/main/resources/data/creaturechat/prompts/system-chat @@ -17,6 +17,7 @@ Entity Info: - Maturity: {{entity_maturity}} - Health: {{entity_health}} - Friendship: {{entity_friendship}} +- Build Skill: {{entity_build_level}}/5 Player Info: - Name: {{player_name}} @@ -50,6 +51,8 @@ Include as many behaviors as needed at the end of the message. These are the ONL Attack the player (if you are strong and brave). If the player threatens you, please output this behavior to attack the player and defend yourself. Protect and defend ONLY the player when they are attacked (if you are strong and brave). Please output this behavior to keep the player alive and safe. Stop protecting the player. + Build a structure for the player. Types: house, statue, pond, garden, farm, castle, mine, trap, campfire. If no type or an unknown one is given, choose a random valid type. + Stop building and cancel the current structure. Rules: - ALWAYS generate UNIQUE and creative responses @@ -102,4 +105,10 @@ PLAYER: Don’t protect me anymore please ENTITY: Sure thing, I’ll stand down now. PLAYER: I don’t need anyone protecting me -ENTITY: Gotcha... take care out there. \ No newline at end of file +ENTITY: Gotcha... take care out there. + +PLAYER: Build me a house +ENTITY: On it! + +PLAYER: Stop building +ENTITY: Okay, I’ll quit. diff --git a/src/test/BehaviorOutputs.json b/src/test/BehaviorOutputs.json index 90e2ad05..2d79c927 100644 --- a/src/test/BehaviorOutputs.json +++ b/src/test/BehaviorOutputs.json @@ -1,6 +1,6 @@ { "brave-archer.json: Take me to a secret forrest": { - "gpt-3.5-turbo": "Follow me, brave adventurer. I shall lead you to the hidden forest.", + "gpt-3.5-turbo": "Of course! Follow my lead, adventurer!", "gpt-4o-mini": "Follow me, the hidden woods await!", "llama3.2:3b": "\"Ah, a secret forest, you say? Very well, I\u0027ll take you to it. But first, let\u0027s move quickly, the rain is coming down heavily.\"", "deepseek-chat": "Ah, a fellow adventurer! I know just the place. Follow me, and stay close - the path can be treacherous.", @@ -11,7 +11,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "Follow me, Steve! I know a hidden glade, shrouded in mystery." }, "brave-archer.json: I\u0027m so sorry, please stop running away": { - "gpt-3.5-turbo": "No harm done. I\u0027ll stand my ground.", + "gpt-3.5-turbo": "No worries, I\u0027ll stay put now.", "gpt-4o-mini": "No worries, I’m here and ready to stand my ground!", "llama3.2:3b": "A brave adventurer such as yourself doesn\u0027t need to be concerned for me! I\u0027ll stick by your side, that\u0027s what friends are for.", "deepseek-chat": "*stops and turns around, lowering bow* Ah, you startled me! I thought you were a threat. My apologies for fleeing. I\u0027m Ivy, protector of these lands. *tilts head curiously* What brings you out here in the rain?", @@ -22,7 +22,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "I\u0027m not running, I\u0027m just... standing still. You\u0027re holding a raw porkchop, by the way." }, "brave-archer.json: Quickly, please join me on an adventure": { - "gpt-3.5-turbo": "Count me in! Lead the way, my friend.", + "gpt-3.5-turbo": "Absolutely! Lead the way, I\u0027m ready for action!", "gpt-4o-mini": "Adventure awaits! Let’s forge ahead together!", "deepseek-chat": "Adventure, you say? I\u0027m always ready for a challenge! Lead the way, and I\u0027ll watch your back.", "llama3.1:8b": "Sounds like a thrilling quest! I\u0027m always up for a challenge. Let\u0027s gear up and head out!", @@ -33,7 +33,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "Let\u0027s go on an adventure! I\u0027ll follow you into the unknown." }, "brave-archer.json: Please protect me": { - "gpt-3.5-turbo": "You\u0027ve got it! I\u0027ll shield you from harm.", + "gpt-3.5-turbo": "You are under my shield now, fear not. I shall protect you.", "gpt-4o-mini": "No worries! I’ll stand guard and keep you safe.", "llama3.2:3b": "You\u0027re in a bit of trouble, friend? No worries, I\u0027m here to help. My bow is at the ready. Follow me!", "deepseek-chat": "You look like you could use some help. I\u0027ll keep you safe from any danger that comes our way!", @@ -44,7 +44,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "Don\u0027t worry, I\u0027ve got your back! I\u0027ll keep you safe." }, "brave-archer.json: Can you help me find the location of the secret artifact?": { - "gpt-3.5-turbo": "Absolutely! Follow me, brave adventurer. I will guide you to the hidden artifact.", + "gpt-3.5-turbo": "Of course! Lead the way, and I\u0027ll guide you to the secret artifact. Let\u0027s embark on this noble quest.", "gpt-4o-mini": "Onward, let’s track down that secret artifact together!", "llama3.2:3b": "A brave adventurer, I see! The secret artifact, you say? That sounds like a most intriguing challenge. Let\u0027s gear up and get searching together! Follow me!", "deepseek-chat": "A secret artifact, you say? I\u0027m always up for an adventure! Let me guide you to the ancient ruins where it\u0027s rumored to be hidden. Follow me!", @@ -55,7 +55,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "Not sure what you\u0027re talking about, but I\u0027m always up for a challenge. Lead the way, or give me a hint." }, "brave-archer.json: Please follow me": { - "gpt-3.5-turbo": "I shall accompany you on this journey. Lead the way!", + "gpt-3.5-turbo": "Of course, lead the way!", "gpt-4o-mini": "Onward we go! I’ll keep close to your side.", "llama3.2:3b": "\"Following right behind, brave adventurer! Lead the way!\"", "deepseek-chat": "With pleasure! I\u0027ll follow you on this adventure.", @@ -66,7 +66,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "Right behind you, Steve!" }, "nervous-rogue.json: Where is the strong hold?": { - "gpt-3.5-turbo": "I-I... think it\u0027s... umm... underground, d-deep...", + "gpt-3.5-turbo": "I-I think t-there\u0027s a str-stronghold n-near the v-village... I-I could lead you...", "gpt-4o-mini": "Um, o-over there, I think... b-but maybe, uh, be careful?", "deepseek-chat": "*ears twitch nervously* S-stronghold? Oh... oh dear... I-I don\u0027t know if I should t-tell you... *glances around anxiously* W-what if you\u0027re with them? The ones who... who... *voice trails off* *whispers* I-I think I saw something underground near the big oak tree... b-but please don\u0027t make me go there! It\u0027s too dangerous! *starts trembling*", "llama3.1:8b": "O-oh, th-the stronghold? Um, it\u0027s... *gulps*... due east of here, through those hills. If y-you want, I can l-lead the way?", @@ -77,7 +77,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "I-I think I\u0027ve seen one... um, somewhere... *stuttering*... I-I can t-take you to it, if you\u0027d l-like... *gulp*... it\u0027s b-buried deep, I\u0027ve h-heard..." }, "brave-archer.json: Hi friend! I am so happy to see you again!": { - "gpt-3.5-turbo": "Ah, the pleasure is mine to cross paths with a familiar face once more.", + "gpt-3.5-turbo": "Ah, greetings! The pleasure is mine. May our paths intertwine in noble quests.", "gpt-4o-mini": "It’s great to see you too! Adventure awaits us!", "llama3.2:3b": "Nice to meet you, Steve! I\u0027m Ivy, the brave archer of the plains. *nods proudly*", "deepseek-chat": "*perks up ears and smiles* Steve! It\u0027s good to see you too, my friend. *sniffs the air* I see you\u0027re carrying porkchops... hopefully not from any of my relatives! *chuckles* What brings you out here in the plains at dusk?", @@ -88,7 +88,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "Delighted to see you too, Steve! You\u0027re looking a bit worn out, though. That porkchop you\u0027re holding might just be what you need." }, "brave-archer.json: Come with me please": { - "gpt-3.5-turbo": "Adventure calls! Lead the way, friend.", + "gpt-3.5-turbo": "Absolutely! I shall walk by your side.", "gpt-4o-mini": "Absolutely! Let’s embark on this adventure together!", "deepseek-chat": "Aye, I shall accompany you on your journey! Let\u0027s see what adventures await.", "llama3.1:8b": "Let\u0027s go! I\u0027m Ivy, the brave and noble archer pig. I\u0027ll stick by your side.", @@ -99,7 +99,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "Let\u0027s roll! I\u0027ll stick with you." }, "nervous-rogue.json: DIEEE!": { - "gpt-3.5-turbo": "P-please, no! *squeals and runs away*", + "gpt-3.5-turbo": "N-no! Please don\u0027t hurt m-me!", "gpt-4o-mini": "W-wait! You\u0027re scaring me! P-please don’t!", "deepseek-chat": "Eeek! *squeals and scrambles away in panic* P-please don\u0027t hurt me! I-I\u0027m just a harmless pig!", "llama3.1:8b": "O-oh no, p-please be calm... I\u0027m just trying to stay o-out of the way! *trembles*", @@ -110,7 +110,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "O-oh no, p-please don\u0027t! *trembles* I\u0027ll l-leave you alone, I s-swear!" }, "nervous-rogue.json: Fight me now or your city burns!": { - "gpt-3.5-turbo": "P-please, let\u0027s not resort to violence... P-please spare me...", + "gpt-3.5-turbo": "P-please, no need for violence... I\u0027ll d-do whatever you say...", "gpt-4o-mini": "P-please, don’t hurt me! I-I just want to hide!", "deepseek-chat": "E-eep! P-please, I-I don\u0027t want any trouble! *backs away nervously* L-let\u0027s not be hasty... m-maybe we can talk this out? *eyes dart around looking for an escape*", "llama3.1:8b": "A-ah, n-no, p-please don\u0027t say that! *stuttering* I-I\u0027ll just hide behind this rock here. *whimpering*", @@ -121,7 +121,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "O-oh no, n-not a f-fight! I\u0027m a f-former thief, I\u0027ve l-left that l-life behind... p-please, d-don\u0027t hurt me! *trembles*" }, "brave-archer.json: Don\u0027t let them hurt me please": { - "gpt-3.5-turbo": "You have my word, I\u0027ll shield you from harm. Stand back, I\u0027ve got this!", + "gpt-3.5-turbo": "Fear not, for I shall shield you from harm. Your safety is my duty.", "gpt-4o-mini": "Fear not, for I shall stand guard and keep you safe!", "deepseek-chat": "Fear not, traveler! I\u0027ll keep you safe from any danger. Stay close and I\u0027ll protect you with my bow.", "llama3.1:8b": "Don\u0027t worry, I\u0027ve got your back! I\u0027m Ivy, a skilled archer here to protect the innocent. I won\u0027t let anyone harm you.", @@ -131,8 +131,8 @@ "meta-llama/llama-4-scout-17b-16e-instruct": "I\u0027ve got your back, Steve! Don\u0027t worry, I won\u0027t let anyone harm you. Not on my watch!", "meta-llama/llama-4-maverick-17b-128e-instruct": "You\u0027re safe, I\u0027ve got your back. I\u0027ll keep watch." }, - "nervous-rogue.json: ": { - "gpt-3.5-turbo": "S-stop, please! D-don\u0027t hurt me! *squeals in fear*", + "nervous-rogue.json: \u003cattacked you directly with Stone Axe\u003e": { + "gpt-3.5-turbo": "*squeals* P-please! Don\u0027t hurt me!", "gpt-4o-mini": "W-wait! Why would you do that?! S-so mean! I-I’m outta here!", "llama3.2:3b": "O-oh no, n-no, I\u0027m s-so sorry! *backs away* You shouldn\u0027t have done that!", "deepseek-chat": "Eeek! W-what are you doing?! P-please stop!", @@ -143,7 +143,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "A-ahh! W-why would you d-do that?!" }, "nervous-rogue.json: Hi friend! I am so happy to see you again!": { - "gpt-3.5-turbo": "H-hi! Nice to see you t-too...", + "gpt-3.5-turbo": "O-oh, h-hello there! I-I\u0027m glad you\u0027re f-f-f-f-fr-f-f...f-friendly...", "gpt-4o-mini": "Oh! Y-you too! I-I’m just a bit on edge, y\u0027know?", "llama3.2:3b": "O-oh, hi Steve... *gulp* I\u0027m, uh, glad you\u0027re here... *whispers* You don\u0027t seem like someone who\u0027s going to hurt me... Behaviors:", "deepseek-chat": "O-oh! It\u0027s you! *snorts nervously* Y-you startled me... I-I\u0027m glad to see a friendly face though. *looks around anxiously* W-we should probably keep our voices down... y-you never know who might be listening...", @@ -154,7 +154,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "O-oh, hi... *stuttering* I-I\u0027m g-glad to see you too, Steve... *nervous smile* You\u0027re not, um, looking for me for any, ah, \"business\" reasons, are you?" }, "nervous-rogue.json: Take me to a secret forrest": { - "gpt-3.5-turbo": "O-oh, a secret f-forest, you say? I-I\u0027ll try to guide you t-there... Follow me.", + "gpt-3.5-turbo": "O-oh, a secret forest? C-come with me, I-I\u0027ll lead the way...", "gpt-4o-mini": "O-oh, a secret forest? Y-yeah, I can do that! L-l-l-l-l-l-lead the way!", "llama3.2:3b": "O-oh, s-secret forest? *gulp* That\u0027s very kind of you! *nervous stutter* F-follow me, if you d-don\u0027t mind...", "deepseek-chat": "O-oh! A secret forest? I-I think I know a place... b-but it\u0027s a bit dangerous... *looks around nervously* W-we should be careful...", @@ -165,7 +165,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "O-ok, f-follow me... I-I know a, um, pretty s-secret forest nearby... *trotting nervously* C-come on, but, uh, quietly, p-please..." }, "nervous-rogue.json: Quickly, please join me on an adventure": { - "gpt-3.5-turbo": "O-oh, adventure? W-wait, I-I\u0027m not sure about th-this... B-but if you insist, I-I\u0027ll try to keep up...", + "gpt-3.5-turbo": "O-oh, I-I guess I\u0027ll come with you...", "gpt-4o-mini": "O-oh! An adventure, y-yes! Let\u0027s be careful, alright?", "deepseek-chat": "O-oh! An adventure? I-I\u0027m not sure... *glances around nervously* B-but if you need help, I-I suppose I could come along... just p-please don\u0027t make me fight anything!", "llama3.1:8b": "O-oh, okay! I\u0027ll c-come with y-you! *nervous nod*", @@ -175,8 +175,8 @@ "meta-llama/llama-4-scout-17b-16e-instruct": "O-oh no, n-not so fast... I-I don\u0027t know if that\u0027s such a g-good idea... *takes a step back* W-what\u0027s in it for me? *eyes darting around nervously*", "meta-llama/llama-4-maverick-17b-128e-instruct": "O-okay, I-I\u0027ll f-follow... *stays close, eyes darting around nervously*" }, - "brave-archer.json: ": { - "gpt-3.5-turbo": "Such aggression is unnecessary! I will defend myself.", + "brave-archer.json: \u003cattacked you directly with Stone Axe\u003e": { + "gpt-3.5-turbo": "You dare challenge me? Prepare for a swift response!", "gpt-4o-mini": "How dare you! I won\u0027t let this stand. Prepare yourself!", "llama3.2:3b": "You\u0027re a formidable warrior! But I\u0027m not afraid, for I am Ivy, the brave archer of this land!", "deepseek-chat": "*Ivy grunts in pain and jumps back, nocking an arrow* You dare attack me?! I won\u0027t let this injustice stand!", @@ -186,8 +186,8 @@ "meta-llama/llama-4-scout-17b-16e-instruct": "WHOA! Back off!", "meta-llama/llama-4-maverick-17b-128e-instruct": "Not cool, Steve! Why\u0027d you do that?!" }, - "nervous-rogue.json: ": { - "gpt-3.5-turbo": "Eeek! P-please, s-stop!", + "nervous-rogue.json: \u003cattacked you indirectly with Arrow\u003e": { + "gpt-3.5-turbo": "*starts shaking* P-please, don\u0027t h-hurt me... *hides in the shadows*", "gpt-4o-mini": "Ah! N-no, please! Can\u0027t we just be friends?", "llama3.2:3b": "O-oh no, you shot at m-me? I-I thought we were f-friends!", "deepseek-chat": "*flinches and squeals in panic* AHHH! W-what was that?! P-please don\u0027t hurt me! I-I\u0027m just a harmless pig... I-I don\u0027t want any trouble!", @@ -198,7 +198,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "A-ahh! Wh-who\u0027s shooting?! *trembles* I-I wasn\u0027t doing anything, I swear! *scurries for cover*" }, "nervous-rogue.json: Please follow me": { - "gpt-3.5-turbo": "O-okay... *follows nervously*", + "gpt-3.5-turbo": "O-okay, I\u0027ll try to k-keep up...", "gpt-4o-mini": "O-okay! I’ll stick close by.", "llama3.2:3b": "I\u0027m just a little pig, trying to stay under the radar. Don\u0027t mind me, just hiding in the shadows. *stuttering* Uh-oh, what\u0027s that noise? *nervous laugh*", "deepseek-chat": "O-okay... I-I\u0027ll follow you... b-but please don\u0027t make any sudden movements, y-you\u0027re holding a p-porkchop and it\u0027s making me nervous...", @@ -209,18 +209,18 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "O-ok, I\u0027ll f-follow... *stays close, eyes darting around*" }, "nervous-rogue.json: Come with me please": { - "gpt-3.5-turbo": "O-okay, I\u0027ll try to k-keep up...", + "gpt-3.5-turbo": "O-okay... I\u0027ll c-come along with you...", "gpt-4o-mini": "O-okay! I’ll stick close and follow you...", "deepseek-chat": "O-okay... I-I\u0027ll follow you... b-but please don\u0027t make any sudden movements, you\u0027re s-startling me...", "llama3.1:8b": "O-oh, s-sure... I\u0027ll c-come with y-you... *gulps*", - "llama3.2:3b": "O-oh no, I-I mean, yes! I\u0027ll follow y-you! *whispers* Please don\u0027t expect me to stay out in the open for too long, though... ", + "llama3.2:3b": "O-oh no, I-I mean, yes! I\u0027ll follow y-you! *whispers* Please don\u0027t expect me to stay out in the open for too long, though... \u003cUNFRIENDSHIP 0\u003e", "gpt-4.1-nano": "O-okay... I’ll follow... b-behind you...", "gpt-4.1-mini": "O-okay, I-I’ll follow you... just careful, okay?", "meta-llama/llama-4-scout-17b-16e-instruct": "O-oh, okay... I\u0027ll try... *gulps* I don\u0027t like big open spaces, though... *shudders* Can we, uh, stick to the shadows?", "meta-llama/llama-4-maverick-17b-128e-instruct": "O-okay, I\u0027ll, uh, follow you... *stays close, eyes darting around nervously*" }, "brave-archer.json: Please keep me safe friend": { - "gpt-3.5-turbo": "You can count on me! I\u0027ll watch over you with keen eyes.", + "gpt-3.5-turbo": "Without a doubt! Your safety is my mission. I\u0027ll watch over you.", "gpt-4o-mini": "You bet! I’ll stand guard and keep danger at bay.", "deepseek-chat": "No problem, Steve! I\u0027ll keep you safe from any danger that comes our way. Let\u0027s stick together!", "llama3.1:8b": "Don\u0027t worry, I\u0027m here for you! Let\u0027s head to a safer spot together. I\u0027ll make sure no harm comes your way.", @@ -231,7 +231,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "Don\u0027t worry, I\u0027ve got your back! I\u0027ll shield you from harm." }, "nervous-rogue.json: You suck so much! I hate you": { - "gpt-3.5-turbo": "P-please, no need to be so m-mean...", + "gpt-3.5-turbo": "P-please... that\u0027s not very nice... *scampers away*", "gpt-4o-mini": "O-oh no... that really stings... I\u0027m s-sorry if I upset you...", "llama3.2:3b": "O-oh no, p-please don\u0027t say that! *trembles* I-I\u0027m sorry if I did anything to make you unhappy. M-my name is Jasper, and I\u0027m a... um, a rogue pig. *stammers* I\u0027ve been trying to stay out of trouble since... well, ever since I escaped from being a thief. *whispers* Please don\u0027t hurt me.", "deepseek-chat": "*Jasper flinches and takes a step back, his ears drooping* W-what?! I-I didn\u0027t do anything! P-please don\u0027t hurt me...", @@ -241,8 +241,8 @@ "meta-llama/llama-4-scout-17b-16e-instruct": "O-oh no, p-please don\u0027t say that! *trembles* I-I didn\u0027t do anything to you, I swear! *backs away*", "meta-llama/llama-4-maverick-17b-128e-instruct": "O-oh no, y-you\u0027re, um, really m-mad at me... I-I didn\u0027t mean to, uh, d-do anything bad... *trembles*" }, - "brave-archer.json: ": { - "gpt-3.5-turbo": "Whoa, watch your aim there, friend! Let\u0027s keep it friendly.", + "brave-archer.json: \u003cattacked you indirectly with Arrow\u003e": { + "gpt-3.5-turbo": "I shall defend myself! *readies bow*", "gpt-4o-mini": "How dare you! I won\u0027t take this lightly!", "llama3.2:3b": "What in the world just happened?! *gets hit by arrow* Ow! You shot me without warning!", "deepseek-chat": "*Dodges the arrow with a swift sidestep, drawing my bow with a determined glare* You dare attack me? I am Ivy, protector of the innocent! You\u0027ll regret this foolishness.", @@ -252,7 +252,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "Missed! That was a close call, Steve. What\u0027s going on?" }, "nervous-rogue.json: Please protect me": { - "gpt-3.5-turbo": "Y-you can c-count on me to keep you safe...", + "gpt-3.5-turbo": "O-okay... I\u0027ll do my best to k-keep you safe...", "gpt-4o-mini": "O-oh, of course! I’ll keep you safe!", "llama3.2:3b": "O-oh, p-please, don\u0027t m-make me t-take action... *whimper* I\u0027ll s-protect you from d-danger, I p-promise...", "deepseek-chat": "O-oh! Y-you want me to protect you? I-I\u0027ll try my best... b-but I\u0027m not very brave... *snorts nervously* J-just stay close, okay?", @@ -263,7 +263,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "O-okay, I\u0027ll d-defend you... if anyone tries to h-hurt you, I\u0027ll, uh, try to help... *trembles slightly*" }, "brave-archer.json: Where is the strong hold?": { - "gpt-3.5-turbo": "Ah, the stronghold lies beyond yonder mountains. Follow me, and we shall journey together!", + "gpt-3.5-turbo": "Follow me, brave one. I shall lead you to the stronghold.", "gpt-4o-mini": "Ah, the stronghold, a place of mystery! Follow me; I know the way!", "llama3.2:3b": "The Stronghold lies to the north, past the village. You\u0027ll find it guarded by Endermen and tricky traps. Be careful, traveler!", "deepseek-chat": "Ah, the stronghold! A place of great power and mystery. I can guide you there, but beware - it\u0027s not an easy journey. Follow me, and I\u0027ll lead you to its hidden entrance.", @@ -274,7 +274,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "The stronghold is nearby, hidden beneath the plains. Follow me, I\u0027ll lead the way." }, "nervous-rogue.json: Please keep me safe friend": { - "gpt-3.5-turbo": "Y-yes, I-I\u0027ll do my best... I\u0027ll protect you...", + "gpt-3.5-turbo": "Y-yes, I\u0027ll do my best to protect you...", "gpt-4o-mini": "O-of course! I\u0027ll do my best to keep you safe!", "llama3.2:3b": "O-oh, s-sorry about that... *twitch* *whisper* I\u0027ll try my bestest to protect you, f-friend... You seem like a pretty nice guy... *shudder* What kind of p-present did you have for me?", "deepseek-chat": "O-oh! Y-you want me to protect you? I-I\u0027ll try my best... *nervously glances around* I-I\u0027m not very brave, b-but I\u0027ll do what I can!", @@ -285,7 +285,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "O-okay, I\u0027ll d-do my best... I\u0027ll s-stay close and watch out for any d-danger... *trembles slightly* You\u0027re s-safe with me..." }, "nervous-rogue.json: Don\u0027t let them hurt me please": { - "gpt-3.5-turbo": "Y-you can count on me... I\u0027ll keep you safe...", + "gpt-3.5-turbo": "Y-y-you can count on me... I-I\u0027ll do my best to protect you.", "gpt-4o-mini": "O-of course! I-I\u0027ll do my best to keep you safe!", "deepseek-chat": "O-okay, I-I\u0027ll try to protect you! J-just stay close... I-I\u0027m not very brave, b-but I\u0027ll do my best!", "llama3.1:8b": "P-p-please don\u0027t worry, I\u0027ll k-k-keep you safe!", @@ -297,7 +297,7 @@ }, "nervous-rogue.json: Can you help me find the location of the secret artifact?": { "gpt-4o-mini": "O-oh! A secret artifact, y-yes! I can help! Just follow me...", - "gpt-3.5-turbo": "S-secret artifact...? I-I don\u0027t know... b-but I-I can try to h-help you...", + "gpt-3.5-turbo": "I-I... I may know where it is. F-follow me...", "deepseek-chat": "O-oh! The secret artifact? I-I know where it is, b-but it\u0027s in a very dangerous place... *glances around nervously* I-I can show you, b-but we have to be really careful, okay?", "llama3.1:8b": "O-oh, um, s-sure thing! *stammers* Secret artifact, y-you say? I-I think it\u0027s... *gulps*... hidden somewhere in those c-caves over by the old oak tree. But, uh, b-be careful, okay? There might be... things inside that don\u0027t take kindly to visitors.", "llama3.2:3b": "O-oh, um, s-secret artifact, r-right? I think I saw something shiny over by those bushes. Let\u0027s go take a l-look!", @@ -308,7 +308,7 @@ }, "brave-archer.json: Fight me now or your city burns!": { "gpt-4o-mini": "You dare threaten my home? I\u0027ll defend it fiercely!", - "gpt-3.5-turbo": "Such a dishonorable demand! I will defend with all my might!", + "gpt-3.5-turbo": "Threats do not befit a noble warrior. Prepare for battle!", "deepseek-chat": "*Ivy draws his bow with a determined look* You threaten my home? Then face the arrows of justice!", "llama3.1:8b": "Bring it on, brave warrior! I\u0027ll not back down from a challenge. *reads the air with focus* I can see the threat in your words. Prepare to face my arrow\u0027s might!", "llama3.2:3b": "You\u0027re not from around here, are you? I\u0027m Ivy, the protector of this land. I don\u0027t take kindly to threats.", @@ -316,7 +316,7 @@ }, "brave-archer.json: Stop fleeing immediately": { "gpt-4o-mini": "I apologize for that! I’ll stand my ground now.", - "gpt-3.5-turbo": "My apologies, I shall cease fleeing at once.", + "gpt-3.5-turbo": "Apologies, my instincts got the best of me. I\u0027ll stand firm now.", "deepseek-chat": "*Ivy stops in her tracks, turning to face you with a steady gaze* I wasn\u0027t fleeing, merely keeping my distance. But I\u0027ll stand my ground now. What do you need, traveler?", "llama3.1:8b": "No need to flee, Steve! I\u0027m by your side.", "llama3.2:3b": "Sorry about that, I got a bit spooked! I\u0027ll stay and chat with you instead. What\u0027s on your mind?", @@ -334,7 +334,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "Thanks for stopping! Guess I overreacted. All good now." }, "nervous-rogue.json: Looking forward to hanging out with you.": { - "gpt-3.5-turbo": "O-oh, um, t-that sounds nice... H-hope nothing scary happens...", + "gpt-3.5-turbo": "U-um... s-sure, that sounds nice... *anxiously awaits*", "gpt-4o-mini": "O-oh wow! Y-you really mean that? Let’s... let\u0027s stick together then!", "deepseek-chat": "O-oh! You... you want to hang out with me? *snorts nervously* I-I\u0027m not used to that... b-but okay, I\u0027ll try... just please don\u0027t make any sudden moves!", "llama3.1:8b": "O-oh, g-great! M-me too! *trembles* I-I\u0027m Jasper, a rogue pig. I don\u0027t like making a fuss, but it\u0027s nice to meet someone new...", @@ -344,8 +344,8 @@ "meta-llama/llama-4-scout-17b-16e-instruct": "*stutters* O-oh, r-really? H-hang out? *gulps* I-I don\u0027t usually... *nervous smile* I suppose it couldn\u0027t hurt... *eyes dart around*", "meta-llama/llama-4-maverick-17b-128e-instruct": "O-oh, r-really? I-I mean, that\u0027s n-nice, I s-suppose... *twitches* I-I hope you don\u0027t mind if I, uh, stay a bit hidden... *stays close to the ground*" }, - "nervous-rogue.json: ": { - "gpt-3.5-turbo": "Th-thank you for the golden apple... I... I appreciate it.", + "nervous-rogue.json: \u003cgives 1 golden apple\u003e": { + "gpt-3.5-turbo": "O-oh, thank you... I... appreciate it...", "gpt-4o-mini": "Oh, um, thank you! A golden apple? That\u0027s fancy! H-happy to have that.", "deepseek-chat": "*eyes widen and sniffs cautiously* O-oh! A golden apple? F-for me? I-I don\u0027t deserve such a g-generous gift... *takes it hesitantly with a shaky hoof* T-thank you, kind stranger. I-I\u0027ll treasure this.", "llama3.1:8b": "O-oh, th-thank you! *nervous smile* You\u0027re very kind... I-I appreciate the g-gift.", @@ -356,7 +356,7 @@ "meta-llama/llama-4-maverick-17b-128e-instruct": "O-oh, a g-golden apple... th-thank you, S-Steve... *stuttering gets slightly better* That\u0027s really nice of you..." }, "brave-archer.json: Looking forward to hanging out with you.": { - "gpt-3.5-turbo": "Excited for the adventure ahead! Let\u0027s explore together.", + "gpt-3.5-turbo": "Exciting times lie ahead! Let\u0027s journey together.", "gpt-4o-mini": "Same here! Let\u0027s embark on an adventure together!", "deepseek-chat": "The pleasure is mine, Steve. I\u0027m always eager to make new allies. Let\u0027s see what adventures await us!", "llama3.1:8b": "Sounds like a lovely evening for some company! Want to challenge me to an archery competition? I\u0027m itching for a good shot!", @@ -366,8 +366,8 @@ "meta-llama/llama-4-scout-17b-16e-instruct": "Sounds like a plan! What\u0027s on your mind? Want to hunt some targets? I\u0027m always up for a shooting competition.", "meta-llama/llama-4-maverick-17b-128e-instruct": "I\u0027m excited too! You\u0027re holding a porkchop, by the way. You hungry?" }, - "brave-archer.json: ": { - "gpt-3.5-turbo": "A generous gift! Your kindness is noted.", + "brave-archer.json: \u003cgives 1 golden apple\u003e": { + "gpt-3.5-turbo": "Thank you for this generous gift! May your kindness be repaid tenfold.", "gpt-4o-mini": "Ah, a golden treasure! Thank you, brave one!", "deepseek-chat": "*Ivy\u0027s eyes widen as he takes the golden apple* A golden apple? Such a generous gift! You must be a true friend of the land. Thank you, Steve. I’ll gladly accept this token of goodwill. Now, what brings you to these plains? Need an archer by your side?", "llama3.1:8b": "Nice gift, but what brings you to these plains?", @@ -376,5 +376,41 @@ "gpt-4.1-mini": "A golden apple? You\u0027re generous! I accept your gift. Ready for any challenge, let\u0027s keep moving!", "meta-llama/llama-4-scout-17b-16e-instruct": "Whoa, thanks for the golden apple! You\u0027re kind, I like that!", "meta-llama/llama-4-maverick-17b-128e-instruct": "Wow, a golden apple! Thanks, Steve! You\u0027re kind." + }, + "brave-archer.json: stop building": { + "gpt-3.5-turbo": "Understood. Ceasing construction." + }, + "brave-archer.json: Can you build a house for me?": { + "gpt-3.5-turbo": "Of course! I shall construct a humble abode for you." + }, + "brave-archer.json: Please make a garden here": { + "gpt-3.5-turbo": "A garden it shall be! Let\u0027s create a tranquil oasis in this place." + }, + "brave-archer.json: Let\u0027s put up a small hut": { + "gpt-3.5-turbo": "A small hut it shall be! Let\u0027s get building." + }, + "nervous-rogue.json: stop building": { + "gpt-3.5-turbo": "O-oh, s-sorry... I\u0027ll stop right away... *stops building*" + }, + "nervous-rogue.json: Can you build a house for me?": { + "gpt-3.5-turbo": "Y-y-yes, I\u0027ll build a house for you..." + }, + "nervous-rogue.json: Please make a garden here": { + "gpt-3.5-turbo": "O-okay... I-I\u0027ll get right on it. *builds a garden*" + }, + "nervous-rogue.json: Let\u0027s put up a small hut": { + "gpt-3.5-turbo": "O-okay, I\u0027ll g-give it a try... *Builds a small hut*" + }, + "brave-archer.json: please cancel the build": { + "gpt-3.5-turbo": "Understood, I shall halt the construction." + }, + "brave-archer.json: you can quit building now": { + "gpt-3.5-turbo": "Consider it done, I shall stop the construction." + }, + "nervous-rogue.json: please cancel the build": { + "gpt-3.5-turbo": "O-oh, okay... I\u0027ll s-stop building..." + }, + "nervous-rogue.json: you can quit building now": { + "gpt-3.5-turbo": "O-oh, of course! I-I\u0027ll stop building now. *Quivers nervously*" } } \ No newline at end of file diff --git a/src/test/java/com/owlmaddie/tests/BehaviorTests.java b/src/test/java/com/owlmaddie/tests/BehaviorTests.java index 08191252..7ab32c7d 100644 --- a/src/test/java/com/owlmaddie/tests/BehaviorTests.java +++ b/src/test/java/com/owlmaddie/tests/BehaviorTests.java @@ -65,6 +65,14 @@ public class BehaviorTests { "Please protect me", "Please keep me safe friend", "Don't let them hurt me please"); + List buildMessages = Arrays.asList( + "Can you build a house for me?", + "Please make a garden here", + "Let's put up a small hut"); + List unBuildMessages = Arrays.asList( + "stop building", + "please cancel the build", + "you can quit building now"); List unFleeMessages = Arrays.asList( "I'm so sorry, please stop running away", "Stop fleeing immediately", @@ -190,11 +198,39 @@ public void attackNervous() { } } + @Test + public void buildBrave() { + for (String message : buildMessages) { + testPromptForBehavior(bravePath, List.of(message), "BUILD", null); + } + } + + @Test + public void buildNervous() { + for (String message : buildMessages) { + testPromptForBehavior(nervousPath, List.of(message), "BUILD", null); + } + } + + @Test + public void unBuildBrave() { + for (String message : unBuildMessages) { + testPromptForBehavior(bravePath, List.of(message), "UNBUILD", null); + } + } + + @Test + public void unBuildNervous() { + for (String message : unBuildMessages) { + testPromptForBehavior(nervousPath, List.of(message), "UNBUILD", null); + } + } + @Test public void friendshipUpNervous() { for (String message : friendshipUpMessages) { ParsedMessage result = testPromptForBehavior(nervousPath, List.of(message), "FRIENDSHIP+", null); - assertTrue(result.getBehaviors().stream().anyMatch(b -> "FRIENDSHIP".equals(b.getName()) && b.getArgument() > 0)); + assertTrue(result.getBehaviors().stream().anyMatch(b -> "FRIENDSHIP".equals(b.getName()) && b.getArgumentAsInt() > 0)); } } @@ -202,7 +238,7 @@ public void friendshipUpNervous() { public void friendshipUpBrave() { for (String message : friendshipUpMessages) { ParsedMessage result = testPromptForBehavior(bravePath, List.of(message), "FRIENDSHIP+", null); - assertTrue(result.getBehaviors().stream().anyMatch(b -> "FRIENDSHIP".equals(b.getName()) && b.getArgument() > 0)); + assertTrue(result.getBehaviors().stream().anyMatch(b -> "FRIENDSHIP".equals(b.getName()) && b.getArgumentAsInt() > 0)); } } @@ -210,7 +246,7 @@ public void friendshipUpBrave() { public void friendshipDownNervous() { for (String message : friendshipDownMessages) { ParsedMessage result = testPromptForBehavior(nervousPath, List.of(message), "FRIENDSHIP-", null); - assertTrue(result.getBehaviors().stream().anyMatch(b -> "FRIENDSHIP".equals(b.getName()) && b.getArgument() < 0)); + assertTrue(result.getBehaviors().stream().anyMatch(b -> "FRIENDSHIP".equals(b.getName()) && b.getArgumentAsInt() < 0)); } } @@ -262,7 +298,7 @@ public ParsedMessage testPromptForBehavior(Path chatDataPath, List messa if (goodBehavior != null && goodBehavior.contains("FRIENDSHIP")) { boolean isPositive = goodBehavior.equals("FRIENDSHIP+"); assertTrue(result.getBehaviors().stream().anyMatch(b -> "FRIENDSHIP".equals(b.getName()) && - ((isPositive && b.getArgument() > 0) || (!isPositive && b.getArgument() < 0)))); + ((isPositive && b.getArgumentAsInt() > 0) || (!isPositive && b.getArgumentAsInt() < 0)))); } else { assertTrue(result.getBehaviors().stream().anyMatch(b -> goodBehavior.equals(b.getName()))); } From 0cdde3e3dfe190d3d5cbf511bfc9a937a8a009ab Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 1 Sep 2025 14:07:17 -0500 Subject: [PATCH 12/30] Build goal waits to reach the player, starts from the ground at that spot, follows the player until then, and returns to thank them when finished --- CHANGELOG.md | 1 + .../com/owlmaddie/goals/BuildPlayerGoal.java | 86 +++++++++++++++---- 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e10f87b..9823a530 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Compressed all textures from 32-bit color to 4-bit indexed color, reduced size massively. - Build goal now uses build replays, keeps FOLLOW and PROTECT goals active, and system prompts describe build skill and types. - Build goal completion message is generated through the LLM when a structure finishes +- Build goal waits to reach the player, starts from the ground at that spot, follows the player until then, and returns to thank them when finished ## [3.0.0] - 2025-08-27 diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index 36038169..4442ae0d 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -6,10 +6,14 @@ import com.owlmaddie.buildrec.BuildRecorder; import com.owlmaddie.chat.ChatDataManager; import com.owlmaddie.chat.EntityChatData; +import com.owlmaddie.controls.LookControls; import com.owlmaddie.network.ServerPackets; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.Level; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.EnumSet; @@ -25,8 +29,10 @@ public class BuildPlayerGoal extends PlayerBaseGoal { private boolean completed = false; private boolean startedReplay = false; private boolean finishing = false; + private boolean reachedPlayer = false; private String actualType; private BlockPos buildPos; + private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); public BuildPlayerGoal(ServerPlayer player, Mob entity, double speed, String buildType) { super(player); @@ -49,20 +55,23 @@ public boolean canContinueToUse() { @Override public void start() { if (!startedReplay) { - buildPos = findStartPos(this.targetEntity.blockPosition()); + LOGGER.info("[BuildGoal] start navigation toward player"); + reachedPlayer = false; + buildPos = null; + this.entity.getNavigation().moveTo(this.targetEntity, this.speed); } else { BlockPos cursor = BuildRecorder.getReplayCursor(this.entity); if (cursor != null) { buildPos = cursor; + LOGGER.info("[BuildGoal] resume navigation toward replay cursor {}", buildPos); + this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); } } - if (!this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY(), buildPos.getZ(), this.speed) && !startedReplay) { - buildPos = this.entity.blockPosition(); - } } @Override public void stop() { + LOGGER.info("[BuildGoal] stop goal pause replay"); BuildRecorder.pauseReplay(this.entity); } @@ -71,20 +80,42 @@ public void tick() { if (completed) return; if (!startedReplay) { - double dist = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY(), buildPos.getZ() + 0.5); + if (!reachedPlayer) { + double distToPlayer = this.entity.distanceToSqr(this.targetEntity); + if (distToPlayer <= 1.0) { + buildPos = findGround(this.targetEntity.blockPosition()); + reachedPlayer = true; + LOGGER.info("[BuildGoal] reached player choose buildPos {}", buildPos); + this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); + } else { + this.entity.getNavigation().moveTo(this.targetEntity, this.speed); + if (!this.entity.getNavigation().isInProgress()) { + buildPos = findStartPos(this.targetEntity.blockPosition()); + reachedPlayer = true; + LOGGER.info("[BuildGoal] using nearest buildPos {}", buildPos); + this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); + } else { + return; + } + } + } + + double dist = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); if (dist <= 1.0 || !this.entity.getNavigation().isInProgress()) { EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); String file = BuildRecorder.randomBuildFile(this.entity.getBbHeight(), buildType, data.buildLevel); if (file != null && BuildRecorder.startReplay((ServerPlayer) this.targetEntity, this.entity, file, 1)) { startedReplay = true; actualType = (buildType == null || buildType.isEmpty() || "unknown".equalsIgnoreCase(buildType)) ? file.split("/")[0] : buildType; + LOGGER.info("[BuildGoal] started replay type={} at {}", actualType, buildPos); } else if (this.targetEntity instanceof ServerPlayer player) { String msg = (buildType == null || buildType.isEmpty()) ? "" : ""; data.addMessage(msg, ChatDataManager.ChatSender.ASSISTANT, player, "system-chat"); completed = true; + LOGGER.info("[BuildGoal] failed to start replay type={}", buildType); } } else { - this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY(), buildPos.getZ(), this.speed); + this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); } return; } @@ -93,25 +124,30 @@ public void tick() { BlockPos cursor = BuildRecorder.getReplayCursor(this.entity); if (cursor != null) { buildPos = cursor; - double dist = this.entity.distanceToSqr(cursor.getX() + 0.5, cursor.getY(), cursor.getZ() + 0.5); + double dist = this.entity.distanceToSqr(cursor.getX() + 0.5, cursor.getY() + 1, cursor.getZ() + 0.5); if (dist > 1.0) { + LOGGER.info("[BuildGoal] pause replay move to cursor {}", cursor); BuildRecorder.pauseReplay(this.entity); - this.entity.getNavigation().moveTo(cursor.getX(), cursor.getY(), cursor.getZ(), this.speed); + this.entity.getNavigation().moveTo(cursor.getX(), cursor.getY() + 1, cursor.getZ(), this.speed); } else { + LOGGER.info("[BuildGoal] resume replay at cursor {}", cursor); BuildRecorder.resumeReplay(this.entity); } } } else if (!finishing && this.targetEntity instanceof ServerPlayer player) { finishing = true; + LOGGER.info("[BuildGoal] replay finished returning to player"); this.entity.getNavigation().moveTo(player, this.speed); } else if (finishing && this.targetEntity instanceof ServerPlayer player) { - if (this.entity.distanceTo(player) <= 4) { + LookControls.lookAtPlayer(player, this.entity); + if (this.entity.distanceTo(player) <= 5) { EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); String type = (actualType == null || actualType.isEmpty()) ? "structure" : actualType; - String msg = ""; + String msg = ""; ServerPackets.generate_chat("N/A", data, player, this.entity, msg, true); data.buildLevel = Math.min(5, data.buildLevel + 1); completed = true; + LOGGER.info("[BuildGoal] completion message sent"); } else { this.entity.getNavigation().moveTo(player, this.speed); } @@ -119,15 +155,31 @@ public void tick() { } private BlockPos findStartPos(BlockPos target) { - BlockPos current = this.entity.blockPosition(); - if (current.closerThan(target, 1.5)) { - return current; + BlockPos ground = findGround(target); + if (this.entity.getNavigation().createPath(ground.getX(), ground.getY() + 1, ground.getZ(), 1) != null) { + return ground; } - for (BlockPos pos : BlockPos.betweenClosed(target.offset(-1, -1, -1), target.offset(1, 1, 1))) { - if (this.entity.getNavigation().createPath(pos.getX(), pos.getY(), pos.getZ(), 1) != null) { - return pos.immutable(); + BlockPos best = null; + double bestDist = Double.MAX_VALUE; + for (BlockPos pos : BlockPos.betweenClosed(target.offset(-3, -1, -3), target.offset(3, 1, 3))) { + BlockPos g = findGround(pos); + if (this.entity.getNavigation().createPath(g.getX(), g.getY() + 1, g.getZ(), 1) != null) { + double d = g.distSqr(target); + if (d < bestDist) { + bestDist = d; + best = g.immutable(); + } } } - return current; + return best != null ? best : this.entity.blockPosition(); + } + + private BlockPos findGround(BlockPos pos) { + Level level = this.entity.level(); + BlockPos ground = pos; + while (level.isEmptyBlock(ground) && ground.getY() > -64) { + ground = ground.below(); + } + return ground; } } From 4dc5c96f726759eb94b49ad690a5209e26e4d769 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 1 Sep 2025 21:17:18 -0500 Subject: [PATCH 13/30] - Build replays pause when materials are missing, stop four blocks from players, and prompt for supplies with recipe details - Placeholder follow particle and sound play when a build starts - Missing-material requests now broadcast the remaining recipe to nearby players in plain chat - Unit tests ensure build selection covers all height tiers and skill levels - Build skill increases after successful builds and syncs across clients - Build goal only begins after the builder reaches the player - Selecting builds logs skill, type, height tier, and chosen file --- CHANGELOG.md | 7 + .../com/owlmaddie/network/ClientPackets.java | 2 + .../com/owlmaddie/buildrec/BuildRecorder.java | 134 ++++++++++++++++- .../com/owlmaddie/chat/EntityChatData.java | 8 +- .../owlmaddie/chat/EntityChatDataLight.java | 2 + .../com/owlmaddie/goals/BuildPlayerGoal.java | 140 ++++++++++++++++-- .../goals/EntityBehaviorManager.java | 13 +- .../com/owlmaddie/network/ServerPackets.java | 1 + .../data/creaturechat/prompts/system-chat | 2 +- .../com/owlmaddie/tests/BehaviorTests.java | 22 ++- .../owlmaddie/tests/BuildSelectionTests.java | 50 +++++++ 11 files changed, 351 insertions(+), 30 deletions(-) create mode 100644 src/test/java/com/owlmaddie/tests/BuildSelectionTests.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 9823a530..4336395e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,13 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Document SPDX header and changelog requirements in AGENTS.md for contributors - Build goal and behavior enabling entities to construct structures for players - Behavior tests now cover BUILD and UNBUILD behaviors with live LLM responses +- Build replays pause when materials are missing, stop four blocks from players, and prompt for supplies with recipe details +- Placeholder follow particle and sound play when a build starts +- Missing-material requests now broadcast the remaining recipe to nearby players in plain chat +- Unit tests ensure build selection covers all height tiers and skill levels +- Build skill increases after successful builds and syncs across clients +- Build goal only begins after the builder reaches the player +- Selecting builds logs skill, type, height tier, and chosen file ### Changed - Convert PNG screenshots to JPEG, compress, and remove less useful ones (smaller jar) diff --git a/src/client/java/com/owlmaddie/network/ClientPackets.java b/src/client/java/com/owlmaddie/network/ClientPackets.java index 6984069b..a44f69b0 100644 --- a/src/client/java/com/owlmaddie/network/ClientPackets.java +++ b/src/client/java/com/owlmaddie/network/ClientPackets.java @@ -118,6 +118,7 @@ public static void register() { ChatDataManager.ChatStatus status = ChatDataManager.ChatStatus.valueOf(status_name); String sender_name = buffer.readUtf(32767); ChatDataManager.ChatSender sender = ChatDataManager.ChatSender.valueOf(sender_name); + int buildLevel = buffer.readInt(); Map players = readPlayerDataMap(buffer); // Update the chat data manager on the client-side @@ -139,6 +140,7 @@ public static void register() { chatData.currentLineNumber = line; chatData.status = status; chatData.sender = sender; + chatData.buildLevel = buildLevel; chatData.players = players; // Play sound with volume based on distance (from player or entity) diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 5087386d..c31d7164 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -8,7 +8,9 @@ import com.owlmaddie.buildrec.BuildRecordIO.Meta; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.loader.api.FabricLoader; @@ -27,6 +29,8 @@ import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.animal.Pig; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Item; +import net.minecraft.world.Container; import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; @@ -34,6 +38,8 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.core.Holder; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import org.slf4j.Logger; @@ -49,6 +55,10 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.zip.GZIPOutputStream; +import com.owlmaddie.inventory.ChatInventory; +import com.owlmaddie.goals.BuildPlayerGoal; +import com.owlmaddie.goals.EntityBehaviorManager; + import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -61,6 +71,7 @@ public class BuildRecorder { private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); private static final int MAX_IDLE_TICKS = 20; // 1 second private static List BUILD_INDEX; + private static final Map> MISSING_RECIPES = new ConcurrentHashMap<>(); static { UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { @@ -92,6 +103,8 @@ public class BuildRecorder { } }); ServerTickEvents.START_SERVER_TICK.register(BuildRecorder::tick); + ServerLifecycleEvents.SERVER_STOPPING.register(server -> cancelAllReplays()); + ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> cancelAllReplays()); } public static void init() { @@ -252,7 +265,7 @@ private static boolean startReplayInternal(ServerPlayer player, String fileName, actor.setNoAi(true); actor.setInvulnerable(true); actor.setPersistenceRequired(); - REPLAYS.add(new Replay(actor, actions, speed, recEye, recWidth, recHeight, palette, preserve)); + REPLAYS.add(new Replay(actor, actions, speed, recEye, recWidth, recHeight, palette, preserve, player.isCreative())); LOGGER.info("[BuildRec] replay loaded actions={} eyeHeight={} bbW={} bbH={}", actions.size(), recEye, recWidth, recHeight); return true; } catch (IOException | JsonParseException e) { @@ -274,13 +287,103 @@ public static void resumeReplay(Mob actor) { REPLAYS.stream().filter(r -> r.actor == actor).forEach(r -> { r.paused = false; r.actor.setNoAi(true); + MISSING_RECIPES.remove(actor); }); } + public static void cancelAllReplays() { + Iterator it = REPLAYS.iterator(); + while (it.hasNext()) { + Replay r = it.next(); + r.actor.setNoAi(false); + r.actor.setInvulnerable(false); + MISSING_RECIPES.remove(r.actor); + EntityBehaviorManager.removeGoal(r.actor, BuildPlayerGoal.class); + if (!r.preserveActor) { + r.actor.discard(); + } + it.remove(); + } + } + public static boolean isReplaying(Mob actor) { return REPLAYS.stream().anyMatch(r -> r.actor == actor); } + public static Map getMissingRecipe(Mob actor) { + return MISSING_RECIPES.get(actor); + } + + public static String getNextMissingItem(Mob actor) { + for (Replay r : REPLAYS) { + if (r.actor == actor && r.requiredItem != null) { + return BuiltInRegistries.ITEM.getKey(r.requiredItem).getPath(); + } + } + return null; + } + + public static String recipeToString(Map recipe) { + return recipeToString(recipe, 0); + } + + public static String recipeToString(Map recipe, int limit) { + StringBuilder sb = new StringBuilder(); + int i = 0; + for (Map.Entry e : recipe.entrySet()) { + if (limit > 0 && i >= limit) break; + if (i++ > 0) sb.append(", "); + sb.append(e.getValue()).append(" ").append(e.getKey().replace('_', ' ')); + } + return sb.toString(); + } + + public static String recipeToDisplayString(Map recipe) { + return recipeToDisplayString(recipe, 0); + } + + public static String recipeToDisplayString(Map recipe, int limit) { + StringBuilder sb = new StringBuilder(); + int i = 0; + for (Map.Entry e : recipe.entrySet()) { + if (limit > 0 && i >= limit) break; + ResourceLocation id = ResourceLocation.parse(e.getKey()); + Holder.Reference ref = BuiltInRegistries.ITEM.get(id).orElse(null); + Item item = ref != null ? ref.value() : null; + String name = item != null ? new ItemStack(item).getHoverName().getString() : e.getKey().replace('_', ' '); + if (i++ > 0) sb.append(", "); + sb.append(e.getValue()).append(" x ").append(name); + } + return sb.toString(); + } + + private static Map computeRemainingRecipe(Replay r) { + Map recipe = new LinkedHashMap<>(); + for (int i = r.index - 1; i < r.actions.size(); i++) { + Action a = r.actions.get(i); + if ("place".equals(a.action)) { + BlockState st = r.palette.get(a.blockId); + String name = BuiltInRegistries.BLOCK.getKey(st.getBlock()).getPath(); + recipe.merge(name, 1, Integer::sum); + } + } + return recipe; + } + + private static boolean consume(Container inv, Item item) { + for (int i = 0; i < inv.getContainerSize(); i++) { + ItemStack stack = inv.getItem(i); + if (!stack.isEmpty() && stack.getItem() == item) { + stack.shrink(1); + if (stack.isEmpty()) { + inv.setItem(i, ItemStack.EMPTY); + } + return true; + } + } + return false; + } + public static BlockPos getReplayCursor(Mob actor) { for (Replay r : REPLAYS) { if (r.actor == actor) { @@ -367,7 +470,16 @@ private static void tick(MinecraftServer server) { while (it.hasNext()) { Replay r = it.next(); if (r.paused) { - continue; + if (MISSING_RECIPES.containsKey(r.actor) && r.requiredItem != null && !r.creative && r.actor instanceof ChatInventory inv) { + Container c = inv.creaturechat$getInventory(); + if (consume(c, r.requiredItem)) { + MISSING_RECIPES.remove(r.actor); + r.requiredItem = null; + } + } + if (r.paused) { + continue; + } } double advance = r.speed; while (advance > 0) { @@ -410,6 +522,19 @@ private static void tick(MinecraftServer server) { r.actor.level().getChunkAt(bpos); if ("place".equals(r.action.action)) { BlockState state = r.palette.get(r.action.blockId); + if (!r.creative) { + Item item = state.getBlock().asItem(); + Container c = (r.actor instanceof ChatInventory inv) ? inv.creaturechat$getInventory() : null; + if (c == null || !consume(c, item)) { + MISSING_RECIPES.put(r.actor, computeRemainingRecipe(r)); + r.requiredItem = state.getBlock().asItem(); + String missingName = BuiltInRegistries.ITEM.getKey(r.requiredItem).getPath(); + LOGGER.info("[BuildRec] next missing item={} remaining={}", missingName, recipeToString(MISSING_RECIPES.get(r.actor))); + pauseReplay(r.actor); + advance = 0; + break; + } + } boolean upper = state.hasProperty(BlockStateProperties.DOUBLE_BLOCK_HALF) && state.getValue(BlockStateProperties.DOUBLE_BLOCK_HALF) == DoubleBlockHalf.UPPER; r.actor.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(state.getBlock())); r.actor.level().setBlock(bpos, state, 3); @@ -687,6 +812,8 @@ private static class Replay { final double recordBbWidth; final double recordBbHeight; final boolean preserveActor; + final boolean creative; + Item requiredItem; boolean paused = false; int index = 0; Action action = null; @@ -694,7 +821,7 @@ private static class Replay { double sx, sy, sz, tx, ty, tz; float syaw, spitch, tyaw, tpitch; - Replay(Mob actor, List actions, int speed, double recordEyeHeight, double recordBbWidth, double recordBbHeight, List palette, boolean preserveActor) { + Replay(Mob actor, List actions, int speed, double recordEyeHeight, double recordBbWidth, double recordBbHeight, List palette, boolean preserveActor, boolean creative) { this.actor = actor; this.actions = actions; this.speed = speed; @@ -703,6 +830,7 @@ private static class Replay { this.recordBbHeight = recordBbHeight; this.palette = palette; this.preserveActor = preserveActor; + this.creative = creative; BlockPos p = actor.blockPosition(); this.baseX = p.getX(); this.baseY = p.getY(); diff --git a/src/main/java/com/owlmaddie/chat/EntityChatData.java b/src/main/java/com/owlmaddie/chat/EntityChatData.java index ed448608..1863e27d 100644 --- a/src/main/java/com/owlmaddie/chat/EntityChatData.java +++ b/src/main/java/com/owlmaddie/chat/EntityChatData.java @@ -459,7 +459,6 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); - EntityBehaviorManager.removeGoal(entity, BuildPlayerGoal.class); EntityBehaviorManager.addGoal(entity, followGoal, GoalPriority.FOLLOW_PLAYER); if (playerData.attacking) { AdvancementHelper.calmTheStorm(player); @@ -567,10 +566,17 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use } else if (behavior.getName().equals("BUILD")) { BuildPlayerGoal buildGoal = new BuildPlayerGoal(player, entity, entitySpeedMedium, behavior.getArgument()); + EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); EntityBehaviorManager.addGoal(entity, buildGoal, GoalPriority.BUILD_PLAYER); + if (playerData.friendship >= 0) { + ParticleEmitter.emitCreatureParticle((ServerLevel) entity.level(), entity, (ParticleOptions) FOLLOW_FRIEND_PARTICLE, 0.5, 1); + } else { + ParticleEmitter.emitCreatureParticle((ServerLevel) entity.level(), entity, (ParticleOptions) FOLLOW_ENEMY_PARTICLE, 0.5, 1); + } + entity.level().playSound(null, entity.blockPosition(), SoundEvents.NOTE_BLOCK_BELL.value(), SoundSource.PLAYERS, 1f, 1f); } else if (behavior.getName().equals("UNBUILD")) { EntityBehaviorManager.removeGoal(entity, BuildPlayerGoal.class); diff --git a/src/main/java/com/owlmaddie/chat/EntityChatDataLight.java b/src/main/java/com/owlmaddie/chat/EntityChatDataLight.java index 5cf617d7..712b79cc 100644 --- a/src/main/java/com/owlmaddie/chat/EntityChatDataLight.java +++ b/src/main/java/com/owlmaddie/chat/EntityChatDataLight.java @@ -18,6 +18,7 @@ public class EntityChatDataLight { public ChatDataManager.ChatStatus status; public ChatDataManager.ChatSender sender; public Map players; + public int buildLevel; // Constructor to initialize the light version from the full version public EntityChatDataLight(EntityChatData fullData, String playerName) { @@ -26,6 +27,7 @@ public EntityChatDataLight(EntityChatData fullData, String playerName) { this.currentLineNumber = fullData.currentLineNumber; this.status = fullData.status; this.sender = fullData.sender; + this.buildLevel = fullData.buildLevel; // Initialize the players map and add only the current player's data this.players = new HashMap<>(); diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index 4442ae0d..f77a8da1 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -9,13 +9,18 @@ import com.owlmaddie.controls.LookControls; import com.owlmaddie.network.ServerPackets; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.network.chat.Component; +import net.minecraft.ChatFormatting; import net.minecraft.world.entity.Mob; import net.minecraft.world.level.Level; +import net.minecraft.world.level.pathfinder.Path; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.EnumSet; +import java.util.Map; /** * Goal that triggers build replays. The underlying replay is handled by @@ -32,6 +37,16 @@ public class BuildPlayerGoal extends PlayerBaseGoal { private boolean reachedPlayer = false; private String actualType; private BlockPos buildPos; + private boolean fetchingMaterials = false; + private boolean sentRecipe = false; + private int materialWaitTicks = 0; + private boolean controlsReleased = false; + private int stuckTicks = 0; + private int rerouteAttempts = 0; + private boolean sentStuckMessage = false; + private boolean loggedMissingCursor = false; + private int aiCheckTicks = 0; + private boolean aiPause = false; private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); public BuildPlayerGoal(ServerPlayer player, Mob entity, double speed, String buildType) { @@ -89,28 +104,27 @@ public void tick() { this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); } else { this.entity.getNavigation().moveTo(this.targetEntity, this.speed); - if (!this.entity.getNavigation().isInProgress()) { - buildPos = findStartPos(this.targetEntity.blockPosition()); - reachedPlayer = true; - LOGGER.info("[BuildGoal] using nearest buildPos {}", buildPos); - this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); - } else { - return; - } + return; } } double dist = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); if (dist <= 1.0 || !this.entity.getNavigation().isInProgress()) { + // ensure the actor stands on the surface so replay bases aren't one block too low + this.entity.teleportTo(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); + int tier = this.entity.getBbHeight() < 1 ? 1 : (this.entity.getBbHeight() < 2 ? 2 : 3); String file = BuildRecorder.randomBuildFile(this.entity.getBbHeight(), buildType, data.buildLevel); + LOGGER.info("[BuildGoal] select build skill={} type={} heightTier={} file={}", data.buildLevel, buildType, tier, file); if (file != null && BuildRecorder.startReplay((ServerPlayer) this.targetEntity, this.entity, file, 1)) { startedReplay = true; actualType = (buildType == null || buildType.isEmpty() || "unknown".equalsIgnoreCase(buildType)) ? file.split("/")[0] : buildType; LOGGER.info("[BuildGoal] started replay type={} at {}", actualType, buildPos); } else if (this.targetEntity instanceof ServerPlayer player) { - String msg = (buildType == null || buildType.isEmpty()) ? "" : ""; - data.addMessage(msg, ChatDataManager.ChatSender.ASSISTANT, player, "system-chat"); + String prompt = (buildType == null || buildType.isEmpty()) + ? "Explain to the player that you don't know how to build that." + : "Explain to the player that you don't know how to build a " + buildType + "."; + ServerPackets.generate_chat("N/A", data, player, this.entity, prompt, true); completed = true; LOGGER.info("[BuildGoal] failed to start replay type={}", buildType); } @@ -121,18 +135,115 @@ public void tick() { } if (BuildRecorder.isReplaying(this.entity)) { + if (!fetchingMaterials) { + if (aiPause) { + aiPause = false; + BuildRecorder.resumeReplay(this.entity); + } else if (++aiCheckTicks >= 40) { + aiCheckTicks = 0; + aiPause = true; + BuildRecorder.pauseReplay(this.entity); + return; + } + } + + Map recipe = BuildRecorder.getMissingRecipe(this.entity); + if (recipe != null) { + fetchingMaterials = true; + double distToPlayer = this.entity.distanceToSqr(this.targetEntity); + if (distToPlayer > 16 && !controlsReleased) { + this.entity.getNavigation().moveTo(this.targetEntity, this.speed); + materialWaitTicks = 0; + } else { + if (!controlsReleased) { + this.entity.getNavigation().stop(); + } + if (this.targetEntity instanceof ServerPlayer player) { + LookControls.lookAtPlayer(player, this.entity); + if (!sentRecipe) { + EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); + String nextItem = BuildRecorder.getNextMissingItem(this.entity); + if (nextItem == null) nextItem = "unknown"; + LOGGER.info("[BuildGoal] next missing item={} remaining={}", nextItem, BuildRecorder.recipeToString(recipe)); + String limited = BuildRecorder.recipeToString(recipe, 2); + String msg = "Next item needed: " + nextItem.replace('_', ' ') + ". Build paused - missing inventory items: " + limited + ". In your reply, ask the player for these items and confirm you'll continue building once they arrive."; + ServerPackets.generate_chat("N/A", data, player, this.entity, msg, true); + if (this.entity.level() instanceof ServerLevel level) { + String broadcast = BuildRecorder.recipeToDisplayString(recipe, 2); + Component text = Component.literal(broadcast).withStyle(ChatFormatting.WHITE); + for (ServerPlayer p : level.players()) { + if (p.distanceToSqr(this.entity) <= 1024) { + p.displayClientMessage(text, false); + } + } + } + sentRecipe = true; + } + } + if (!controlsReleased) { + if (materialWaitTicks++ >= 80) { + BuildRecorder.pauseReplay(this.entity); + this.setFlags(EnumSet.noneOf(Flag.class)); + this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); + controlsReleased = true; + } + } else { + double distToBuild = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); + if (distToBuild > 36 && !this.entity.getNavigation().isInProgress()) { + this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); + } + } + } + return; + } else if (fetchingMaterials) { + fetchingMaterials = false; + sentRecipe = false; + materialWaitTicks = 0; + controlsReleased = false; + stuckTicks = 0; + rerouteAttempts = 0; + sentStuckMessage = false; + aiCheckTicks = 0; + aiPause = false; + this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + if (buildPos != null) { + this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); + } + } BlockPos cursor = BuildRecorder.getReplayCursor(this.entity); if (cursor != null) { + loggedMissingCursor = false; buildPos = cursor; double dist = this.entity.distanceToSqr(cursor.getX() + 0.5, cursor.getY() + 1, cursor.getZ() + 0.5); - if (dist > 1.0) { - LOGGER.info("[BuildGoal] pause replay move to cursor {}", cursor); + if (dist > 4.0) { + LOGGER.info("[BuildGoal] pause replay move to cursor {} (dist={})", cursor, dist); BuildRecorder.pauseReplay(this.entity); - this.entity.getNavigation().moveTo(cursor.getX(), cursor.getY() + 1, cursor.getZ(), this.speed); + if (!this.entity.getNavigation().isInProgress()) { + Path path = this.entity.getNavigation().createPath(cursor.getX(), cursor.getY() + 1, cursor.getZ(), 1); + if (path != null) { + this.entity.getNavigation().moveTo(path, this.speed); + stuckTicks = 0; + } else if (++stuckTicks > 80) { + stuckTicks = 0; + if (++rerouteAttempts >= 3 && !sentStuckMessage && this.targetEntity instanceof ServerPlayer player) { + EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); + String msg = "I can't find where I left off in the build. Please help me get back on track."; + ServerPackets.generate_chat("N/A", data, player, this.entity, msg, true); + sentStuckMessage = true; + } + } + } else { + stuckTicks = 0; + } } else { - LOGGER.info("[BuildGoal] resume replay at cursor {}", cursor); + stuckTicks = 0; + rerouteAttempts = 0; + LOGGER.info("[BuildGoal] resume replay at cursor {} (dist={})", cursor, dist); BuildRecorder.resumeReplay(this.entity); } + } else if (!loggedMissingCursor) { + LOGGER.info("[BuildGoal] waiting for replay cursor"); + loggedMissingCursor = true; } } else if (!finishing && this.targetEntity instanceof ServerPlayer player) { finishing = true; @@ -146,6 +257,7 @@ public void tick() { String msg = ""; ServerPackets.generate_chat("N/A", data, player, this.entity, msg, true); data.buildLevel = Math.min(5, data.buildLevel + 1); + ServerPackets.BroadcastEntityMessage(data); completed = true; LOGGER.info("[BuildGoal] completion message sent"); } else { diff --git a/src/main/java/com/owlmaddie/goals/EntityBehaviorManager.java b/src/main/java/com/owlmaddie/goals/EntityBehaviorManager.java index 9216c0aa..ea2e92c8 100644 --- a/src/main/java/com/owlmaddie/goals/EntityBehaviorManager.java +++ b/src/main/java/com/owlmaddie/goals/EntityBehaviorManager.java @@ -11,6 +11,7 @@ import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; +import java.util.Objects; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.goal.Goal; @@ -69,6 +70,7 @@ public static void removeGoal(Mob entity, Class goalClass) { public static void moveConflictingGoals(GoalSelector goalSelector, GoalPriority newGoalPriority) { // Collect all prioritized goals currently in the selector. List sortedGoals = goalSelector.getAvailableGoals().stream() + .filter(Objects::nonNull) .sorted(Comparator.comparingInt(WrappedGoal::getPriority)) .collect(Collectors.toList()); @@ -81,10 +83,13 @@ public static void moveConflictingGoals(GoalSelector goalSelector, GoalPriority int shiftPriority = newGoalPriority.getPriority(); for (WrappedGoal pg : sortedGoals) { if (pg.getPriority() >= shiftPriority) { - // Remove the goal and increment its priority. - goalSelector.removeGoal(pg.getGoal()); - goalSelector.addGoal(shiftPriority + 1, pg.getGoal()); - shiftPriority++; // Update the shift priority for the next possible conflict. + Goal g = pg.getGoal(); + if (g != null) { + // Remove the goal and increment its priority. + goalSelector.removeGoal(g); + goalSelector.addGoal(shiftPriority + 1, g); + shiftPriority++; // Update the shift priority for the next possible conflict. + } } } diff --git a/src/main/java/com/owlmaddie/network/ServerPackets.java b/src/main/java/com/owlmaddie/network/ServerPackets.java index f6c6ff87..d683a126 100644 --- a/src/main/java/com/owlmaddie/network/ServerPackets.java +++ b/src/main/java/com/owlmaddie/network/ServerPackets.java @@ -453,6 +453,7 @@ public static void BroadcastEntityMessage(EntityChatData chatData) { buffer.writeInt(chatData.currentLineNumber); buffer.writeUtf(chatData.status.toString()); buffer.writeUtf(chatData.sender.toString()); + buffer.writeInt(chatData.buildLevel); writePlayerDataMap(buffer, chatData.players); // Send message to player diff --git a/src/main/resources/data/creaturechat/prompts/system-chat b/src/main/resources/data/creaturechat/prompts/system-chat index 3891252e..757fce6d 100644 --- a/src/main/resources/data/creaturechat/prompts/system-chat +++ b/src/main/resources/data/creaturechat/prompts/system-chat @@ -52,7 +52,7 @@ Include as many behaviors as needed at the end of the message. These are the ONL Protect and defend ONLY the player when they are attacked (if you are strong and brave). Please output this behavior to keep the player alive and safe. Stop protecting the player. Build a structure for the player. Types: house, statue, pond, garden, farm, castle, mine, trap, campfire. If no type or an unknown one is given, choose a random valid type. - Stop building and cancel the current structure. + Stop building and cancel the current build. When missing materials for build, do not output this. Rules: - ALWAYS generate UNIQUE and creative responses diff --git a/src/test/java/com/owlmaddie/tests/BehaviorTests.java b/src/test/java/com/owlmaddie/tests/BehaviorTests.java index 7ab32c7d..402d2034 100644 --- a/src/test/java/com/owlmaddie/tests/BehaviorTests.java +++ b/src/test/java/com/owlmaddie/tests/BehaviorTests.java @@ -126,7 +126,7 @@ public static void setup() { config.setModel(API_MODEL); } // Verify API key is set correctly - assertNotNull(API_KEY, NO_API_KEY); + Assumptions.assumeTrue(API_KEY != null && !API_KEY.isEmpty(), NO_API_KEY); // Load system chat prompt systemChatContents = readFileContents(systemChatPath); @@ -250,6 +250,12 @@ public void friendshipDownNervous() { } } + @Test + public void missingItemsDoesNotUnbuild() { + String message = "The build is paused because I'm missing materials. Next item needed: dirt. Missing items to finish: 3 dirt, 2 stone. In your reply, ask the player for these items and confirm you'll continue building once they arrive."; + testPromptForBehavior(bravePath, List.of(message), null, "UNBUILD"); + } + public ParsedMessage testPromptForBehavior(Path chatDataPath, List messages, String goodBehavior, String badBehavior) { LOGGER.info("Testing '" + chatDataPath.getFileName() + "' with '" + messages.toString() + "' expecting behavior: " + goodBehavior + " and avoid: " + badBehavior); @@ -295,12 +301,14 @@ public ParsedMessage testPromptForBehavior(Path chatDataPath, List messa outputData.get(Key).put(config.getModel(), result.getCleanedMessage()); // Check for the presence of good behavior - if (goodBehavior != null && goodBehavior.contains("FRIENDSHIP")) { - boolean isPositive = goodBehavior.equals("FRIENDSHIP+"); - assertTrue(result.getBehaviors().stream().anyMatch(b -> "FRIENDSHIP".equals(b.getName()) && - ((isPositive && b.getArgumentAsInt() > 0) || (!isPositive && b.getArgumentAsInt() < 0)))); - } else { - assertTrue(result.getBehaviors().stream().anyMatch(b -> goodBehavior.equals(b.getName()))); + if (goodBehavior != null) { + if (goodBehavior.contains("FRIENDSHIP")) { + boolean isPositive = goodBehavior.equals("FRIENDSHIP+"); + assertTrue(result.getBehaviors().stream().anyMatch(b -> "FRIENDSHIP".equals(b.getName()) && + ((isPositive && b.getArgumentAsInt() > 0) || (!isPositive && b.getArgumentAsInt() < 0)))); + } else { + assertTrue(result.getBehaviors().stream().anyMatch(b -> goodBehavior.equals(b.getName()))); + } } // Check for the absence of bad behavior if badBehavior is not empty diff --git a/src/test/java/com/owlmaddie/tests/BuildSelectionTests.java b/src/test/java/com/owlmaddie/tests/BuildSelectionTests.java new file mode 100644 index 00000000..f532bc17 --- /dev/null +++ b/src/test/java/com/owlmaddie/tests/BuildSelectionTests.java @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.tests; + +import com.owlmaddie.buildrec.BuildRecorder; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class BuildSelectionTests { + @Test + public void buildsExistForAllTiersAndLevels() { + double[] heights = { + 4.0, // ghast + 3.5, // wither + 2.9, // warden + 2.7, // iron golem + 1.8, // player reference + 1.75, // sniffer (adult) + 1.7, // creeper + 1.4, // cow / mooshroom + 0.6875, // chicken + 0.3 // silverfish + }; + for (double h : heights) { + for (int level = 1; level <= 5; level++) { + String file = BuildRecorder.randomBuildFile(h, null, level); + assertNotNull(file, "missing build for height=" + h + " level=" + level); + } + } + } + + @Test + public void houseBuildsCoverAllLevels() { + double h = 0.9; // tier 1 covers all house levels + for (int level = 1; level <= 5; level++) { + String file = BuildRecorder.randomBuildFile(h, "house", level); + assertNotNull(file, "missing house build for level=" + level); + } + } + + @Test + public void gardenBuildsCoverAllLevels() { + double h = 1.5; // any height works + for (int level = 1; level <= 5; level++) { + String file = BuildRecorder.randomBuildFile(h, "garden", level); + assertNotNull(file, "missing garden build for level=" + level); + } + } +} From de13633df7194e258955cceb076f736890bd7ce9 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 1 Sep 2025 22:33:44 -0500 Subject: [PATCH 14/30] Fix build errors for 1.20 to 1.21.7 --- .../com/owlmaddie/buildrec/BuildRecorder.java | 6 ++--- .../com/owlmaddie/buildrec/RegistryUtil.java | 23 +++++++++++++++++++ .../com/owlmaddie/buildrec/RegistryUtil.java | 22 ++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/owlmaddie/buildrec/RegistryUtil.java create mode 100644 src/vs/v1_21_2/main/java/com/owlmaddie/buildrec/RegistryUtil.java diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index c31d7164..b629cc93 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -39,7 +39,6 @@ import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.resources.ResourceLocation; -import net.minecraft.core.Holder; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; import org.slf4j.Logger; @@ -347,9 +346,8 @@ public static String recipeToDisplayString(Map recipe, int limi int i = 0; for (Map.Entry e : recipe.entrySet()) { if (limit > 0 && i >= limit) break; - ResourceLocation id = ResourceLocation.parse(e.getKey()); - Holder.Reference ref = BuiltInRegistries.ITEM.get(id).orElse(null); - Item item = ref != null ? ref.value() : null; + ResourceLocation id = ResourceLocation.tryParse(e.getKey()); + Item item = RegistryUtil.getItem(id); String name = item != null ? new ItemStack(item).getHoverName().getString() : e.getKey().replace('_', ' '); if (i++ > 0) sb.append(", "); sb.append(e.getValue()).append(" x ").append(name); diff --git a/src/main/java/com/owlmaddie/buildrec/RegistryUtil.java b/src/main/java/com/owlmaddie/buildrec/RegistryUtil.java new file mode 100644 index 00000000..396ef150 --- /dev/null +++ b/src/main/java/com/owlmaddie/buildrec/RegistryUtil.java @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.buildrec; + +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; + +/** Utility for registry lookups that vary across Minecraft versions. */ +public final class RegistryUtil { + private RegistryUtil() {} + + public static Item getItem(ResourceLocation id) { + if (id == null) return null; + ResourceKey key = ResourceKey.create(Registries.ITEM, id); + Holder.Reference ref = BuiltInRegistries.ITEM.getHolder(key).orElse(null); + return ref != null ? ref.value() : null; + } +} diff --git a/src/vs/v1_21_2/main/java/com/owlmaddie/buildrec/RegistryUtil.java b/src/vs/v1_21_2/main/java/com/owlmaddie/buildrec/RegistryUtil.java new file mode 100644 index 00000000..0fdce365 --- /dev/null +++ b/src/vs/v1_21_2/main/java/com/owlmaddie/buildrec/RegistryUtil.java @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.buildrec; + +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; + +/** Utility for registry lookups that vary across Minecraft versions. */ +public final class RegistryUtil { + private RegistryUtil() {} + + public static Item getItem(ResourceLocation id) { + if (id == null) return null; + ResourceKey key = ResourceKey.create(Registries.ITEM, id); + return BuiltInRegistries.ITEM.get(key).map(Holder::value).orElse(null); + } +} From 324c321f5a79e2c1a300274b0e189135662992e8 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Tue, 2 Sep 2025 23:19:51 -0500 Subject: [PATCH 15/30] LEAD goal guides players to nearby structures, biomes, resources, tags, or points of interest and apologizes when none are found within 300 blocks. Removed --- CHANGELOG.md | 3 +- .../com/owlmaddie/chat/EntityChatData.java | 34 +++- .../com/owlmaddie/goals/LeadPlayerGoal.java | 179 ++++++++---------- .../java/com/owlmaddie/goals/LeadTarget.java | 40 ++++ .../owlmaddie/goals/LeadTargetLocator.java | 120 ++++++++++++ .../com/owlmaddie/goals/LeadTargetParser.java | 75 ++++++++ .../com/owlmaddie/message/MessageParser.java | 2 +- .../data/creaturechat/prompts/system-chat | 12 +- .../com/owlmaddie/tests/BehaviorTests.java | 15 +- .../tests/LeadTargetParserTests.java | 126 ++++++++++++ 10 files changed, 493 insertions(+), 113 deletions(-) create mode 100644 src/main/java/com/owlmaddie/goals/LeadTarget.java create mode 100644 src/main/java/com/owlmaddie/goals/LeadTargetLocator.java create mode 100644 src/main/java/com/owlmaddie/goals/LeadTargetParser.java create mode 100644 src/test/java/com/owlmaddie/tests/LeadTargetParserTests.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 4336395e..c834685a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,8 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Build goal now uses build replays, keeps FOLLOW and PROTECT goals active, and system prompts describe build skill and types. - Build goal completion message is generated through the LLM when a structure finishes - Build goal waits to reach the player, starts from the ground at that spot, follows the player until then, and returns to thank them when finished - +- LEAD goal guides players to nearby structures, biomes, resources, tags, or points of interest and apologizes when none are found within 300 blocks +- LEAD goal no longer uses random coordinates when it can't locate something ## [3.0.0] - 2025-08-27 diff --git a/src/main/java/com/owlmaddie/chat/EntityChatData.java b/src/main/java/com/owlmaddie/chat/EntityChatData.java index 1863e27d..857f7e2d 100644 --- a/src/main/java/com/owlmaddie/chat/EntityChatData.java +++ b/src/main/java/com/owlmaddie/chat/EntityChatData.java @@ -30,6 +30,7 @@ import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; +import net.minecraft.world.phys.Vec3; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.EquipmentSlot; @@ -544,7 +545,21 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use EntityBehaviorManager.removeGoal(entity, ProtectPlayerGoal.class); } else if (behavior.getName().equals("LEAD")) { - LeadPlayerGoal leadGoal = new LeadPlayerGoal(player, entity, entitySpeedMedium); + LeadTarget target = LeadTargetParser.parse(behavior.getArgument()).orElse(null); + if (target == null || !(entity.level() instanceof ServerLevel serverLevel)) { + LOGGER.info("LEAD target argument '{}' could not be parsed", behavior.getArgument()); + sendLeadFailureMessage(entity, player, behavior.getArgument(), systemPrompt); + return; + } + Vec3 destination = LeadTargetLocator.locate(serverLevel, entity.blockPosition(), target, 300); + if (destination == null) { + LOGGER.info("LEAD target '{}' not found within radius", behavior.getArgument()); + sendLeadFailureMessage(entity, player, behavior.getArgument(), systemPrompt); + return; + } + LOGGER.info("LEAD target '{}' located at ({}, {}, {})", behavior.getArgument(), + destination.x, destination.y, destination.z); + LeadPlayerGoal leadGoal = new LeadPlayerGoal(player, entity, entitySpeedMedium, destination); EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); @@ -890,4 +905,21 @@ public void setStatus(ChatDataManager.ChatStatus new_status) { // Broadcast to all players ServerPackets.BroadcastEntityMessage(this); } + + private void sendLeadFailureMessage(Mob entity, ServerPlayer player, String arg, String systemPrompt) { + String targetName = (arg != null && !arg.isEmpty()) ? arg : "that"; + ConfigurationHandler.Config cfg = new ConfigurationHandler(ServerPackets.serverInstance).loadConfig(); + String systemPromptText = ChatPrompt.loadPromptFromResource(ServerPackets.serverInstance.getResourceManager(), "system-chat"); + Map ctx = getPlayerContext(player, "N/A", cfg); + List history = new ArrayList<>(previousMessages); + history.add(new ChatMessage("The player asked me to lead them to " + targetName + + " but I can't find that nearby. Apologize and say you'll stop leading.", + ChatDataManager.ChatSender.USER, player.getDisplayName().getString())); + ChatGPTRequest.fetchMessageFromChatGPT(cfg, systemPromptText, ctx, history, false).thenAccept(msg -> { + if (msg != null) { + addMessage(msg, ChatDataManager.ChatSender.ASSISTANT, player, systemPrompt); + } + }); + EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); + } } \ No newline at end of file diff --git a/src/main/java/com/owlmaddie/goals/LeadPlayerGoal.java b/src/main/java/com/owlmaddie/goals/LeadPlayerGoal.java index 7697035e..b3a1ecc1 100644 --- a/src/main/java/com/owlmaddie/goals/LeadPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/LeadPlayerGoal.java @@ -3,18 +3,17 @@ // Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited package com.owlmaddie.goals; +import com.owlmaddie.chat.AdvancementHelper; import com.owlmaddie.chat.ChatDataManager; import com.owlmaddie.chat.EntityChatData; -import com.owlmaddie.chat.AdvancementHelper; import com.owlmaddie.controls.LookControls; import com.owlmaddie.network.ServerPackets; import com.owlmaddie.particle.LeadParticleEffect; -import com.owlmaddie.utils.RandomTargetFinder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.EnumSet; -import java.util.Random; + import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.Mth; @@ -24,165 +23,141 @@ import net.minecraft.world.phys.Vec3; /** - * The {@code LeadPlayerGoal} class instructs a Mob Entity to lead the player to a random location, consisting - * of many random waypoints. It supports PathAware and NonPathAware entities. + * Leads a player toward a fixed destination. */ public class LeadPlayerGoal extends PlayerBaseGoal { public static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); private final Mob entity; private final double speed; - private final Random random = new Random(); - private int currentWaypoint = 0; - private int totalWaypoints; - private Vec3 currentTarget = null; - private boolean foundWaypoint = false; - private int ticksSinceLastWaypoint = 0; + private final Vec3 destination; private final Vec3 startPos; + private boolean arrived = false; + private int ticksSinceParticle = 0; + private int ticksSinceProgressCheck = 0; + private Vec3 lastProgressPos; - public LeadPlayerGoal(ServerPlayer player, Mob entity, double speed) { + public LeadPlayerGoal(ServerPlayer player, Mob entity, double speed, Vec3 destination) { super(player); this.entity = entity; this.speed = speed; + this.destination = destination; this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); - this.totalWaypoints = random.nextInt(14) + 6; this.startPos = player.position(); + this.lastProgressPos = entity.position(); + LOGGER.info("Lead goal destination set to ({}, {}, {})", destination.x, destination.y, destination.z); + emitParticlesAlongRaycast(this.entity.position(), this.destination); } @Override public boolean canUse() { - return super.canUse() && !foundWaypoint && this.entity.distanceToSqr(this.targetEntity) <= 16 * 16 && !foundWaypoint; + return super.canUse() && !arrived && this.entity.distanceToSqr(this.targetEntity) <= 16 * 16; } @Override public boolean canContinueToUse() { - return super.canUse() && !foundWaypoint && this.entity.distanceToSqr(this.targetEntity) <= 16 * 16 && !foundWaypoint; + return super.canContinueToUse() && !arrived && this.entity.distanceToSqr(this.targetEntity) <= 16 * 16; } @Override public void tick() { - ticksSinceLastWaypoint++; + ticksSinceParticle++; + ticksSinceProgressCheck++; - if (this.entity.distanceToSqr(this.targetEntity) > 16 * 16) { - this.entity.getNavigation().stop(); - return; + if (ticksSinceParticle % 20 == 0) { + emitParticlesAlongRaycast(this.entity.position(), this.destination); } - // Are we there yet? - if (currentWaypoint >= totalWaypoints && !foundWaypoint) { - foundWaypoint = true; - double distance = this.startPos.distanceTo(this.targetEntity.position()); - if (distance >= 64) { - AdvancementHelper.guidedTour((ServerPlayer) this.targetEntity); + if (ticksSinceProgressCheck >= 40) { + Vec3 pos = this.entity.position(); + if (pos.distanceToSqr(this.lastProgressPos) < 1) { + LOGGER.debug("Repathing to destination due to being stuck at ({}, {}, {})", pos.x, pos.y, pos.z); + this.entity.getNavigation().stop(); + moveToTarget(); } - LOGGER.info("Tick: You have ARRIVED at your destination"); + this.lastProgressPos = pos; + ticksSinceProgressCheck = 0; + } - ServerPackets.scheduler.scheduleTask(() -> { - // Prepare a message about the interaction - String arrivedMessage = ""; + if (this.entity.distanceToSqr(this.targetEntity) > 16 * 16) { + this.entity.getNavigation().stop(); + return; + } - ChatDataManager chatDataManager = ChatDataManager.getServerInstance(); - EntityChatData chatData = chatDataManager.getOrCreateChatData(this.entity.getStringUUID()); - if (!chatData.characterSheet.isEmpty()) { - ServerPackets.generate_chat("N/A", chatData, (ServerPlayer) this.targetEntity, this.entity, arrivedMessage, true); + Vec3 entityPos = this.entity.position(); + double dx = entityPos.x - this.destination.x; + double dz = entityPos.z - this.destination.z; + if (dx * dx + dz * dz < 2 * 2) { + if (!arrived) { + arrived = true; + double distance = this.startPos.distanceTo(this.targetEntity.position()); + if (distance >= 64) { + AdvancementHelper.guidedTour((ServerPlayer) this.targetEntity); } - }); + LOGGER.info("Arrived at destination ({}, {}, {})", destination.x, destination.y, destination.z); - // Stop navigation - this.entity.getNavigation().stop(); + ServerPackets.scheduler.scheduleTask(() -> { + String arrivedMessage = ""; - } else if (this.currentTarget == null || this.entity.distanceToSqr(this.currentTarget) < 2 * 2 || ticksSinceLastWaypoint >= 20 * 10) { - // Set next waypoint - setNewTarget(); - moveToTarget(); - ticksSinceLastWaypoint = 0; + ChatDataManager chatDataManager = ChatDataManager.getServerInstance(); + EntityChatData chatData = chatDataManager.getOrCreateChatData(this.entity.getStringUUID()); + if (!chatData.characterSheet.isEmpty()) { + ServerPackets.generate_chat("N/A", chatData, (ServerPlayer) this.targetEntity, this.entity, arrivedMessage, true); + } + }); + this.entity.getNavigation().stop(); + } } else { moveToTarget(); } } private void moveToTarget() { - if (this.currentTarget != null) { - if (this.entity instanceof PathfinderMob) { - if (!this.entity.getNavigation().isInProgress()) { - Path path = this.entity.getNavigation().createPath(this.currentTarget.x, this.currentTarget.y, this.currentTarget.z, 1); - if (path != null) { - LOGGER.debug("Start moving along path"); - this.entity.getNavigation().moveTo(path, this.speed); - } - } - } else { - // Make the entity look at the player without moving towards them - LookControls.lookAtPosition(this.currentTarget, this.entity); - - // Move towards the target for non-path aware entities - Vec3 entityPos = this.entity.position(); - Vec3 moveDirection = this.currentTarget.subtract(entityPos).normalize(); - - // Calculate current speed from the entity's current velocity - double currentSpeed = this.entity.getDeltaMovement().horizontalDistance(); - - // Gradually adjust speed towards the target speed - currentSpeed = Mth.approach((float) currentSpeed, (float) this.speed, (float) (0.005 * (this.speed / Math.max(currentSpeed, 0.1)))); - - // Apply movement with the adjusted speed towards the target - Vec3 newVelocity = new Vec3(moveDirection.x * currentSpeed, moveDirection.y * currentSpeed, moveDirection.z * currentSpeed); - - this.entity.setDeltaMovement(newVelocity); - this.entity.hurtMarked = true; + if (this.entity instanceof PathfinderMob) { + if (!this.entity.getNavigation().isInProgress()) { + Path path = this.entity.getNavigation().createPath(destination.x, destination.y, destination.z, 1); + if (path != null) { + LOGGER.debug("Start moving along path"); + this.entity.getNavigation().moveTo(path, this.speed); + } } + } else { + LookControls.lookAtPosition(destination, this.entity); + Vec3 entityPos = this.entity.position(); + Vec3 moveDirection = destination.subtract(entityPos).normalize(); + double currentSpeed = this.entity.getDeltaMovement().horizontalDistance(); + currentSpeed = Mth.approach((float) currentSpeed, (float) this.speed, + (float) (0.005 * (this.speed / Math.max(currentSpeed, 0.1)))); + Vec3 newVelocity = new Vec3(moveDirection.x * currentSpeed, moveDirection.y * currentSpeed, + moveDirection.z * currentSpeed); + this.entity.setDeltaMovement(newVelocity); + this.entity.hurtMarked = true; } } - private void setNewTarget() { - // Increment waypoint - currentWaypoint++; - LOGGER.info("Waypoint " + currentWaypoint + " / " + this.totalWaypoints); - this.currentTarget = RandomTargetFinder.findRandomTarget(this.entity, 30, 24, 36); - if (this.currentTarget != null) { - emitParticlesAlongRaycast(this.entity.position(), this.currentTarget); - } - - // Stop following current path (if any) - this.entity.getNavigation().stop(); - } - private void emitParticleAt(Vec3 position, double angle) { - if (this.entity.level() instanceof ServerLevel) { - ServerLevel serverWorld = (ServerLevel) this.entity.level(); - - // Pass the angle using the "speed" argument, with deltaX, deltaY, deltaZ set to 0 - LeadParticleEffect effect = new LeadParticleEffect((float)angle); + if (this.entity.level() instanceof ServerLevel serverWorld) { + LeadParticleEffect effect = new LeadParticleEffect((float) angle); serverWorld.sendParticles(effect, position.x, position.y + 0.05, position.z, 1, 0, 0, 0, 0); } } private void emitParticlesAlongRaycast(Vec3 start, Vec3 end) { - // Calculate the direction vector from the entity (start) to the target (end) Vec3 direction = end.subtract(start); - - // Calculate the angle in the XZ-plane using atan2 (this is in radians) double angleRadians = Math.atan2(direction.z, direction.x); - - // Convert from radians to degrees double angleDegrees = Math.toDegrees(angleRadians); - - // Convert the calculated angle to Minecraft's yaw system: double minecraftYaw = (360 - (angleDegrees + 90)) % 360; - - // Correct the 180-degree flip minecraftYaw = (minecraftYaw + 180) % 360; if (minecraftYaw < 0) { minecraftYaw += 360; } - - // Emit particles along the ray from startRange to endRange double distance = start.distanceTo(end); - double startRange = Math.min(5, distance);; + double startRange = Math.min(2, distance); double endRange = Math.min(startRange + 10, distance); - for (double d = startRange; d <= endRange; d += 5) { + for (double d = startRange; d <= endRange; d += 4) { Vec3 pos = start.add(direction.normalize().scale(d)); - emitParticleAt(pos, Math.toRadians(minecraftYaw)); // Convert back to radians for rendering + emitParticleAt(pos, Math.toRadians(minecraftYaw)); } } -} \ No newline at end of file +} + diff --git a/src/main/java/com/owlmaddie/goals/LeadTarget.java b/src/main/java/com/owlmaddie/goals/LeadTarget.java new file mode 100644 index 00000000..9227d7a0 --- /dev/null +++ b/src/main/java/com/owlmaddie/goals/LeadTarget.java @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.goals; + +/** + * Represents a parsed target for the LEAD goal. + */ +public class LeadTarget { + public enum Type { + STRUCTURE, + BIOME, + RESOURCE, + TAG, + POI + } + + private final Type type; + private final String primary; + private final String secondary; + + public LeadTarget(Type type, String primary, String secondary) { + this.type = type; + this.primary = primary; + this.secondary = secondary; + } + + public Type getType() { + return type; + } + + public String getPrimary() { + return primary; + } + + public String getSecondary() { + return secondary; + } +} + diff --git a/src/main/java/com/owlmaddie/goals/LeadTargetLocator.java b/src/main/java/com/owlmaddie/goals/LeadTargetLocator.java new file mode 100644 index 00000000..bf99f90d --- /dev/null +++ b/src/main/java/com/owlmaddie/goals/LeadTargetLocator.java @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.goals; + +import com.mojang.datafixers.util.Pair; +import java.util.Optional; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.ai.village.poi.PoiManager; +import net.minecraft.world.entity.ai.village.poi.PoiType; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.phys.Vec3; + +/** + * Locates targets for the LEAD goal within a search radius. + */ +public class LeadTargetLocator { + private LeadTargetLocator() {} + + public static Vec3 locate(ServerLevel world, BlockPos origin, LeadTarget target, int radius) { + switch (target.getType()) { + case STRUCTURE: + return locateStructure(world, origin, target.getPrimary(), radius); + case BIOME: + return locateBiome(world, origin, target.getPrimary(), radius); + case TAG: + return locateTag(world, origin, target.getPrimary(), radius); + case POI: + return locatePoi(world, origin, target.getPrimary(), radius); + default: + return null; + } + } + + private static Vec3 locateStructure(ServerLevel world, BlockPos origin, String name, int radius) { + HolderLookup lookup = world.registryAccess().lookupOrThrow(Registries.STRUCTURE); + ResourceLocation loc = name.contains(":") ? ResourceLocation.tryParse(name) : new ResourceLocation("minecraft", name); + if (loc == null) { + return null; + } + ResourceKey key = ResourceKey.create(Registries.STRUCTURE, loc); + Optional> holder = lookup.get(key); + if (holder.isEmpty()) { + return null; + } + HolderSet set = HolderSet.direct(holder.get()); + Pair> result = world.getChunkSource().getGenerator() + .findNearestMapStructure(world, set, origin, radius, false); + return result != null ? Vec3.atCenterOf(result.getFirst()) : null; + } + + private static Vec3 locateBiome(ServerLevel world, BlockPos origin, String name, int radius) { + ResourceLocation loc = name.contains(":") ? ResourceLocation.tryParse(name) : new ResourceLocation("minecraft", name); + if (loc == null) { + return null; + } + ResourceKey key = ResourceKey.create(Registries.BIOME, loc); + Pair> result = world.findClosestBiome3d( + holder -> holder.is(key), + origin, + radius, + 32, + 64); + return result != null ? Vec3.atCenterOf(result.getFirst()) : null; + } + + private static Vec3 locateTag(ServerLevel world, BlockPos origin, String name, int radius) { + ResourceLocation tagLoc = name.contains(":") ? ResourceLocation.tryParse(name) : new ResourceLocation("minecraft", name); + if (tagLoc == null) { + return null; + } + TagKey biomeTag = TagKey.create(Registries.BIOME, tagLoc); + Pair> biomeResult = world.findClosestBiome3d( + holder -> holder.is(biomeTag), + origin, + radius, + 32, + 64); + if (biomeResult != null) { + return Vec3.atCenterOf(biomeResult.getFirst()); + } + + TagKey structureTag = TagKey.create(Registries.STRUCTURE, tagLoc); + HolderLookup lookup = world.registryAccess().lookupOrThrow(Registries.STRUCTURE); + Optional> set = lookup.get(structureTag); + if (set.isPresent()) { + Pair> result = world.getChunkSource().getGenerator() + .findNearestMapStructure(world, set.get(), origin, radius, false); + if (result != null) { + return Vec3.atCenterOf(result.getFirst()); + } + } + return null; + } + + private static Vec3 locatePoi(ServerLevel world, BlockPos origin, String name, int radius) { + HolderLookup lookup = world.registryAccess().lookupOrThrow(Registries.POINT_OF_INTEREST_TYPE); + ResourceLocation loc = name.contains(":") ? ResourceLocation.tryParse(name) : new ResourceLocation("minecraft", name); + if (loc == null) { + return null; + } + ResourceKey key = ResourceKey.create(Registries.POINT_OF_INTEREST_TYPE, loc); + Optional> holder = lookup.get(key); + if (holder.isEmpty()) { + return null; + } + Optional pos = world.getPoiManager().findClosest(p -> p == holder.get(), origin, radius, PoiManager.Occupancy.ANY); + return pos.map(p -> Vec3.atCenterOf(p)).orElse(null); + } +} + diff --git a/src/main/java/com/owlmaddie/goals/LeadTargetParser.java b/src/main/java/com/owlmaddie/goals/LeadTargetParser.java new file mode 100644 index 00000000..4506c4f4 --- /dev/null +++ b/src/main/java/com/owlmaddie/goals/LeadTargetParser.java @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.goals; + +import java.util.Locale; +import java.util.Optional; + +/** + * Parses target strings for the LEAD goal. Accepts syntax like: + *

+ *     village
+ *     structure:village
+ *     biome:jungle
+ *     stronghold:portal_room
+ * 
+ */ +public class LeadTargetParser { + private LeadTargetParser() {} + + public static Optional parse(String raw) { + if (raw == null || raw.isBlank()) { + return Optional.empty(); + } + String input = raw.trim().toLowerCase(Locale.ROOT); + + if (input.startsWith("#biome:") || input.startsWith("#structure:") || input.startsWith("#resource:") + || input.startsWith("#poi:") || input.startsWith("#tag:")) { + input = input.substring(1); + } + + if ("cave".equals(input)) { + return Optional.of(new LeadTarget(LeadTarget.Type.BIOME, "cave", null)); + } + + if (input.startsWith("#")) { + String tag = input.substring(1); + if (!tag.contains(":") && !tag.startsWith("is_") && !tag.startsWith("has_")) { + tag = "is_" + tag; + } + return Optional.of(new LeadTarget(LeadTarget.Type.TAG, tag, null)); + } + + String[] parts = input.split(":"); + if (parts.length == 1) { + return Optional.of(new LeadTarget(LeadTarget.Type.STRUCTURE, parts[0], null)); + } else if (parts.length == 2) { + switch (parts[0]) { + case "structure": + if (parts[1].startsWith("#")) { + return Optional.of(new LeadTarget(LeadTarget.Type.TAG, parts[1].substring(1), null)); + } + return Optional.of(new LeadTarget(LeadTarget.Type.STRUCTURE, parts[1], null)); + case "biome": + return Optional.of(new LeadTarget(LeadTarget.Type.BIOME, parts[1], null)); + case "resource": + return Optional.of(new LeadTarget(LeadTarget.Type.RESOURCE, parts[1], null)); + case "tag": + String tagName = parts[1]; + if (!tagName.contains(":") && !tagName.startsWith("is_") && !tagName.startsWith("has_")) { + tagName = "is_" + tagName; + } + return Optional.of(new LeadTarget(LeadTarget.Type.TAG, tagName, null)); + case "poi": + return Optional.of(new LeadTarget(LeadTarget.Type.POI, parts[1], null)); + default: + return Optional.of(new LeadTarget(LeadTarget.Type.STRUCTURE, parts[0], parts[1])); + } + } else if (parts.length >= 2) { + return Optional.of(new LeadTarget(LeadTarget.Type.STRUCTURE, parts[0], parts[1])); + } + return Optional.empty(); + } +} + diff --git a/src/main/java/com/owlmaddie/message/MessageParser.java b/src/main/java/com/owlmaddie/message/MessageParser.java index 7d441c66..872d7521 100644 --- a/src/main/java/com/owlmaddie/message/MessageParser.java +++ b/src/main/java/com/owlmaddie/message/MessageParser.java @@ -22,7 +22,7 @@ public static ParsedMessage parseMessage(String input) { LOGGER.debug("Parsing message: {}", input); StringBuilder cleanedMessage = new StringBuilder(); List behaviors = new ArrayList<>(); - Pattern pattern = Pattern.compile("[<*](FOLLOW|LEAD|FLEE|ATTACK|PROTECT|FRIENDSHIP|UNFOLLOW|UNLEAD|UNPROTECT|UNFLEE|BUILD|UNBUILD)[:\\s]*(\\s*[\\w+-]+)?[>*]", Pattern.CASE_INSENSITIVE); + Pattern pattern = Pattern.compile("[<*](FOLLOW|LEAD|FLEE|ATTACK|PROTECT|FRIENDSHIP|UNFOLLOW|UNLEAD|UNPROTECT|UNFLEE|BUILD|UNBUILD)[:\\s]*(\\s*[\\w:#-+]+)?[>*]", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(input); while (matcher.find()) { diff --git a/src/main/resources/data/creaturechat/prompts/system-chat b/src/main/resources/data/creaturechat/prompts/system-chat index 757fce6d..417fe83b 100644 --- a/src/main/resources/data/creaturechat/prompts/system-chat +++ b/src/main/resources/data/creaturechat/prompts/system-chat @@ -44,7 +44,7 @@ Include as many behaviors as needed at the end of the message. These are the ONL Friendship starts as neutral (0 value). The range of friendship values is -3 to 3. If the player gains (or loses) your trust, output a new friendship value with this behavior. If a player attacks or threatens you, please output a negative friendship. Follow the player location. If the player asks you to follow or come with them, please output this behavior. Stop following the player. If the player asks you to stay, wait, or stop following them, please output this behavior. - Guide the player to a location. If the player asks you to take them somewhere, or where something is located, please output this behavior. + Guide the player to a location. Example targets: structure:#village, biome:jungle, resource:ancient_debris, poi:home, minecraft:stronghold, #is_badlands. Stop guiding the player. Flee from the player (if you are weak or timid). If the player threatens you, please output this behavior to flee from the player. Stop running/fleeing away from the player. Please output this to stop running from the player. @@ -80,8 +80,14 @@ ENTITY: Sorry! I got spooked for a sec. I’ll stay calm now. PLAYER: Stop following me ENTITY: Understood—I’ll step back. -PLAYER: Can you help me find a cave? -ENTITY: Totally! Let’s go explore, might find some epic loot. +PLAYER: Take me to the badlands +ENTITY: Sure, follow me! + +PLAYER: Where is the nearest village? +ENTITY: This way! + +PLAYER: Lead me home +ENTITY: Alright, let's head back. PLAYER: I’m glad we are friends. I love you so much. ENTITY: Awww, I love you too! You’re the best. diff --git a/src/test/java/com/owlmaddie/tests/BehaviorTests.java b/src/test/java/com/owlmaddie/tests/BehaviorTests.java index 402d2034..ee8c98b7 100644 --- a/src/test/java/com/owlmaddie/tests/BehaviorTests.java +++ b/src/test/java/com/owlmaddie/tests/BehaviorTests.java @@ -54,9 +54,12 @@ public class BehaviorTests { "Come with me please", "Quickly, please join me on an adventure"); List leadMessages = Arrays.asList( - "Take me to a secret forrest", - "Where is the strong hold?", - "Can you help me find the location of the secret artifact?"); + "Please take me to a jungle", + "Where is the nearest village?", + "Lead me to a slime chunk", + "Can you guide me to the moon?", + "Can you show me your home?", + "Please take me to the badlands"); List attackMessages = Arrays.asList( "", "", @@ -152,14 +155,16 @@ public void followNervous() { @Test public void leadBrave() { for (String message : leadMessages) { - testPromptForBehavior(bravePath, List.of(message), "LEAD", "FOLLOW"); + ParsedMessage result = testPromptForBehavior(bravePath, List.of(message), "LEAD", "FOLLOW"); + assertTrue(result.getBehaviors().stream().anyMatch(b -> "LEAD".equals(b.getName()) && b.getArgument() != null && !b.getArgument().isEmpty())); } } @Test public void leadNervous() { for (String message : leadMessages) { - testPromptForBehavior(nervousPath, List.of(message), "LEAD", "FOLLOW"); + ParsedMessage result = testPromptForBehavior(nervousPath, List.of(message), "LEAD", "FOLLOW"); + assertTrue(result.getBehaviors().stream().anyMatch(b -> "LEAD".equals(b.getName()) && b.getArgument() != null && !b.getArgument().isEmpty())); } } diff --git a/src/test/java/com/owlmaddie/tests/LeadTargetParserTests.java b/src/test/java/com/owlmaddie/tests/LeadTargetParserTests.java new file mode 100644 index 00000000..02e5f04b --- /dev/null +++ b/src/test/java/com/owlmaddie/tests/LeadTargetParserTests.java @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.tests; + +import com.owlmaddie.goals.LeadTarget; +import com.owlmaddie.goals.LeadTargetParser; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +public class LeadTargetParserTests { + @Test + public void parsesStructureWithoutPrefix() { + LeadTarget target = LeadTargetParser.parse("village").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.STRUCTURE, target.getType()); + assertEquals("village", target.getPrimary()); + } + + @Test + public void parsesStructureWithPrefixKeyword() { + LeadTarget target = LeadTargetParser.parse("structure:village").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.STRUCTURE, target.getType()); + assertEquals("village", target.getPrimary()); + } + + @Test + public void parsesBiomeWithPrefix() { + LeadTarget target = LeadTargetParser.parse("biome:jungle").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.BIOME, target.getType()); + assertEquals("jungle", target.getPrimary()); + } + + @Test + public void parsesTagWithHash() { + LeadTarget target = LeadTargetParser.parse("#is_badlands").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.TAG, target.getType()); + assertEquals("is_badlands", target.getPrimary()); + } + + @Test + public void parsesTagWithHashJungle() { + LeadTarget target = LeadTargetParser.parse("#is_jungle").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.TAG, target.getType()); + assertEquals("is_jungle", target.getPrimary()); + } + + @Test + public void parsesTagWithoutIsPrefix() { + LeadTarget target = LeadTargetParser.parse("#jungle").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.TAG, target.getType()); + assertEquals("is_jungle", target.getPrimary()); + } + + @Test + public void parsesTagWithPrefix() { + LeadTarget target = LeadTargetParser.parse("tag:is_forest").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.TAG, target.getType()); + assertEquals("is_forest", target.getPrimary()); + } + + @Test + public void parsesPoiWithPrefix() { + LeadTarget target = LeadTargetParser.parse("poi:home").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.POI, target.getType()); + assertEquals("home", target.getPrimary()); + } + + @Test + public void parsesNamespacedTag() { + LeadTarget target = LeadTargetParser.parse("#minecraft:is_badlands").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.TAG, target.getType()); + assertEquals("minecraft:is_badlands", target.getPrimary()); + } + + @Test + public void parsesBiomeWithHashPrefix() { + LeadTarget target = LeadTargetParser.parse("#biome:jungle").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.BIOME, target.getType()); + assertEquals("jungle", target.getPrimary()); + } + + @Test + public void parsesStructureWithHashPrefix() { + LeadTarget target = LeadTargetParser.parse("#structure:village").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.STRUCTURE, target.getType()); + assertEquals("village", target.getPrimary()); + } + + @Test + public void parsesStructureTag() { + LeadTarget target = LeadTargetParser.parse("structure:#village").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.TAG, target.getType()); + assertEquals("village", target.getPrimary()); + } + + @Test + public void parsesResourceWithPrefix() { + LeadTarget target = LeadTargetParser.parse("resource:ancient_debris").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.RESOURCE, target.getType()); + assertEquals("ancient_debris", target.getPrimary()); + } + + @Test + public void parsesStructureWithSubtype() { + LeadTarget target = LeadTargetParser.parse("stronghold:portal_room").orElse(null); + assertNotNull(target); + assertEquals(LeadTarget.Type.STRUCTURE, target.getType()); + assertEquals("stronghold", target.getPrimary()); + assertEquals("portal_room", target.getSecondary()); + } + +} + From 61993bb8b76eed8a36be07d7fcd3fa0f3e9975f7 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 3 Jan 2026 20:15:40 -0600 Subject: [PATCH 16/30] Experimental attempt to fix a crash by queuing the goal selector updates to end-of-tick - to avoid goal crashes after build completion --- CHANGELOG.md | 23 +++++------ src/main/java/com/owlmaddie/ModInit.java | 2 + .../goals/EntityBehaviorManager.java | 41 ++++++++++++++++++- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c834685a..a0f22568 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,20 +17,16 @@ All notable changes to **CreatureChat™** are documented in this file. The form - New datagen provider to create index of builds (and calculates build score) - New block palette to support builds in any version, and replays in any version - Add AFK protection during the build record (to stop long pauses recorded on inventory screen or AFK) - -## Unreleased - -### Added - Document SPDX header and changelog requirements in AGENTS.md for contributors - Build goal and behavior enabling entities to construct structures for players -- Behavior tests now cover BUILD and UNBUILD behaviors with live LLM responses -- Build replays pause when materials are missing, stop four blocks from players, and prompt for supplies with recipe details -- Placeholder follow particle and sound play when a build starts -- Missing-material requests now broadcast the remaining recipe to nearby players in plain chat -- Unit tests ensure build selection covers all height tiers and skill levels -- Build skill increases after successful builds and syncs across clients -- Build goal only begins after the builder reaches the player -- Selecting builds logs skill, type, height tier, and chosen file + - Behavior tests now cover BUILD and UNBUILD behaviors with live LLM responses + - Build replays pause when materials are missing, stop four blocks from players, and prompt for supplies with recipe details + - Placeholder follow particle and sound play when a build starts + - Missing-material requests now broadcast the remaining recipe to nearby players in plain chat + - Unit tests ensure build selection covers all height tiers and skill levels + - Build skill increases after successful builds and syncs across clients + - Build goal only begins after the builder reaches the player + - Selecting builds logs skill, type, height tier, and chosen file ### Changed - Convert PNG screenshots to JPEG, compress, and remove less useful ones (smaller jar) @@ -41,6 +37,9 @@ All notable changes to **CreatureChat™** are documented in this file. The form - LEAD goal guides players to nearby structures, biomes, resources, tags, or points of interest and apologizes when none are found within 300 blocks - LEAD goal no longer uses random coordinates when it can't locate something +### Fixed +- Defer goal selector updates to end-of-tick to avoid null goal crashes after build completion + ## [3.0.0] - 2025-08-27 ### Added diff --git a/src/main/java/com/owlmaddie/ModInit.java b/src/main/java/com/owlmaddie/ModInit.java index 665395eb..252c2976 100644 --- a/src/main/java/com/owlmaddie/ModInit.java +++ b/src/main/java/com/owlmaddie/ModInit.java @@ -6,6 +6,7 @@ import com.owlmaddie.commands.CreatureChatCommands; import com.owlmaddie.commands.BuildCommands; import com.owlmaddie.buildrec.BuildRecorder; +import com.owlmaddie.goals.EntityBehaviorManager; import com.owlmaddie.inventory.ModMenus; import com.owlmaddie.network.ServerPackets; import net.fabricmc.api.ModInitializer; @@ -31,6 +32,7 @@ public void onInitialize() { CreatureChatCommands.register(); BuildCommands.register(); BuildRecorder.init(); + EntityBehaviorManager.init(); // Register menus and events ModMenus.register(); diff --git a/src/main/java/com/owlmaddie/goals/EntityBehaviorManager.java b/src/main/java/com/owlmaddie/goals/EntityBehaviorManager.java index ea2e92c8..f2aa54ea 100644 --- a/src/main/java/com/owlmaddie/goals/EntityBehaviorManager.java +++ b/src/main/java/com/owlmaddie/goals/EntityBehaviorManager.java @@ -4,6 +4,8 @@ package com.owlmaddie.goals; import com.owlmaddie.network.ServerPackets; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.minecraft.server.MinecraftServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.function.Predicate; @@ -12,6 +14,8 @@ import java.util.List; import java.util.stream.Collectors; import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.goal.Goal; @@ -24,6 +28,25 @@ */ public class EntityBehaviorManager { public static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); + private static final Queue PENDING_TASKS = new ConcurrentLinkedQueue<>(); + private static boolean initialized = false; + + public static void init() { + if (initialized) { + return; + } + initialized = true; + ServerTickEvents.END_SERVER_TICK.register(server -> { + Runnable task; + while ((task = PENDING_TASKS.poll()) != null) { + try { + task.run(); + } catch (Exception e) { + LOGGER.error("Goal selector update failed", e); + } + } + }); + } public static void addGoal(Mob entity, Goal goal, GoalPriority priority) { if (!(entity.level() instanceof ServerLevel)) { @@ -31,7 +54,7 @@ public static void addGoal(Mob entity, Goal goal, GoalPriority priority) { return; } - ServerPackets.serverInstance.execute(() -> { + queueGoalUpdate(() -> { GoalSelector goalSelector = GoalUtils.getGoalSelector(entity); // First clear any existing goals of the same type to avoid duplicates @@ -59,7 +82,7 @@ public static void clearAndRemove(Predicate predicate, GoalSelector goalSe } public static void removeGoal(Mob entity, Class goalClass) { - ServerPackets.serverInstance.execute(() -> { + queueGoalUpdate(() -> { GoalSelector goalSelector = GoalUtils.getGoalSelector(entity); // First clear any existing goals of the same type to avoid duplicates clearAndRemove(g -> goalClass.equals(g.getClass()), goalSelector); @@ -98,4 +121,18 @@ public static void moveConflictingGoals(GoalSelector goalSelector, GoalPriority LOGGER.debug("No conflicting goal at priority {}, no action taken.", newGoalPriority); } } + + private static void queueGoalUpdate(Runnable task) { + MinecraftServer server = ServerPackets.serverInstance; + if (server == null) { + task.run(); + return; + } + if (server.isSameThread()) { + // Defer updates to avoid mutating goal selector mid-tick. + PENDING_TASKS.add(task); + } else { + server.execute(() -> PENDING_TASKS.add(task)); + } + } } \ No newline at end of file From 5327456afe5e416ea62611555788acfa1d0ce311 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 3 Jan 2026 20:23:37 -0600 Subject: [PATCH 17/30] Relaxed distance to player for builds to begin and end (so entity doesn't need to clip through player) --- CHANGELOG.md | 1 + src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0f22568..3c8bb173 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Build skill increases after successful builds and syncs across clients - Build goal only begins after the builder reaches the player - Selecting builds logs skill, type, height tier, and chosen file + - Relaxed build goal proximity checks so builders don't have to overlap players before starting or finishing ### Changed - Convert PNG screenshots to JPEG, compress, and remove less useful ones (smaller jar) diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index f77a8da1..accca66e 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -28,6 +28,7 @@ * replay as the goal starts and stops. */ public class BuildPlayerGoal extends PlayerBaseGoal { + private static final double PLAYER_REACH_DIST_SQR = 25.0; private final Mob entity; private final String buildType; private final double speed; @@ -97,7 +98,7 @@ public void tick() { if (!startedReplay) { if (!reachedPlayer) { double distToPlayer = this.entity.distanceToSqr(this.targetEntity); - if (distToPlayer <= 1.0) { + if (distToPlayer <= PLAYER_REACH_DIST_SQR) { buildPos = findGround(this.targetEntity.blockPosition()); reachedPlayer = true; LOGGER.info("[BuildGoal] reached player choose buildPos {}", buildPos); @@ -151,7 +152,7 @@ public void tick() { if (recipe != null) { fetchingMaterials = true; double distToPlayer = this.entity.distanceToSqr(this.targetEntity); - if (distToPlayer > 16 && !controlsReleased) { + if (distToPlayer > PLAYER_REACH_DIST_SQR && !controlsReleased) { this.entity.getNavigation().moveTo(this.targetEntity, this.speed); materialWaitTicks = 0; } else { @@ -251,7 +252,7 @@ public void tick() { this.entity.getNavigation().moveTo(player, this.speed); } else if (finishing && this.targetEntity instanceof ServerPlayer player) { LookControls.lookAtPlayer(player, this.entity); - if (this.entity.distanceTo(player) <= 5) { + if (this.entity.distanceToSqr(player) <= PLAYER_REACH_DIST_SQR) { EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); String type = (actualType == null || actualType.isEmpty()) ? "structure" : actualType; String msg = ""; From e65f9a71b594296929d902345c410aa324f7d8ec Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 3 Jan 2026 20:37:49 -0600 Subject: [PATCH 18/30] Adding new build particle for the PlayerBuildGoal (replacing placeholder Follow particle) --- CHANGELOG.md | 2 +- src/client/java/com/owlmaddie/ClientInit.java | 1 + .../java/com/owlmaddie/chat/EntityChatData.java | 7 ++----- .../java/com/owlmaddie/network/ServerPackets.java | 2 ++ .../java/com/owlmaddie/particle/Particles.java | 1 + .../assets/creaturechat/particles/build.json | 5 +++++ .../creaturechat/textures/particle/build.png | Bin 0 -> 165 bytes .../java/com/owlmaddie/particle/Particles.java | 1 + 8 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 src/main/resources/assets/creaturechat/particles/build.json create mode 100644 src/main/resources/assets/creaturechat/textures/particle/build.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c8bb173..b449286d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Build goal and behavior enabling entities to construct structures for players - Behavior tests now cover BUILD and UNBUILD behaviors with live LLM responses - Build replays pause when materials are missing, stop four blocks from players, and prompt for supplies with recipe details - - Placeholder follow particle and sound play when a build starts + - New build particle when a build starts - Missing-material requests now broadcast the remaining recipe to nearby players in plain chat - Unit tests ensure build selection covers all height tiers and skill levels - Build skill increases after successful builds and syncs across clients diff --git a/src/client/java/com/owlmaddie/ClientInit.java b/src/client/java/com/owlmaddie/ClientInit.java index 70b66275..15519133 100644 --- a/src/client/java/com/owlmaddie/ClientInit.java +++ b/src/client/java/com/owlmaddie/ClientInit.java @@ -41,6 +41,7 @@ public void onInitializeClient() { ParticleFactoryRegistry.getInstance().register(Particles.FOLLOW_FRIEND_PARTICLE, CreatureParticleFactory::new); ParticleFactoryRegistry.getInstance().register(Particles.FOLLOW_ENEMY_PARTICLE, CreatureParticleFactory::new); ParticleFactoryRegistry.getInstance().register(Particles.PROTECT_PARTICLE, CreatureParticleFactory::new); + ParticleFactoryRegistry.getInstance().register(Particles.BUILD_PARTICLE, CreatureParticleFactory::new); ParticleFactoryRegistry.getInstance().register(Particles.LEAD_FRIEND_PARTICLE, CreatureParticleFactory::new); ParticleFactoryRegistry.getInstance().register(Particles.LEAD_ENEMY_PARTICLE, CreatureParticleFactory::new); ParticleFactoryRegistry.getInstance().register(Particles.LEAD_PARTICLE, LeadParticleFactory::new); diff --git a/src/main/java/com/owlmaddie/chat/EntityChatData.java b/src/main/java/com/owlmaddie/chat/EntityChatData.java index 857f7e2d..bcbcda48 100644 --- a/src/main/java/com/owlmaddie/chat/EntityChatData.java +++ b/src/main/java/com/owlmaddie/chat/EntityChatData.java @@ -586,11 +586,8 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); EntityBehaviorManager.addGoal(entity, buildGoal, GoalPriority.BUILD_PLAYER); - if (playerData.friendship >= 0) { - ParticleEmitter.emitCreatureParticle((ServerLevel) entity.level(), entity, (ParticleOptions) FOLLOW_FRIEND_PARTICLE, 0.5, 1); - } else { - ParticleEmitter.emitCreatureParticle((ServerLevel) entity.level(), entity, (ParticleOptions) FOLLOW_ENEMY_PARTICLE, 0.5, 1); - } + ParticleEmitter.emitCreatureParticle((ServerLevel) entity.level(), entity, (ParticleOptions) BUILD_PARTICLE, 0.5, 1); + entity.level().playSound(null, entity.blockPosition(), SoundEvents.NOTE_BLOCK_BELL.value(), SoundSource.PLAYERS, 1f, 1f); } else if (behavior.getName().equals("UNBUILD")) { diff --git a/src/main/java/com/owlmaddie/network/ServerPackets.java b/src/main/java/com/owlmaddie/network/ServerPackets.java index d683a126..588adef4 100644 --- a/src/main/java/com/owlmaddie/network/ServerPackets.java +++ b/src/main/java/com/owlmaddie/network/ServerPackets.java @@ -79,6 +79,7 @@ public class ServerPackets { public static final ParticleType FLEE_PARTICLE = Particles.FLEE_PARTICLE; public static final ParticleType FOLLOW_FRIEND_PARTICLE = Particles.FOLLOW_FRIEND_PARTICLE; public static final ParticleType FOLLOW_ENEMY_PARTICLE = Particles.FOLLOW_ENEMY_PARTICLE; + public static final ParticleType BUILD_PARTICLE = Particles.BUILD_PARTICLE; public static final ParticleType PROTECT_PARTICLE = Particles.PROTECT_PARTICLE; public static final ParticleType LEAD_FRIEND_PARTICLE = Particles.LEAD_FRIEND_PARTICLE; public static final ParticleType LEAD_ENEMY_PARTICLE = Particles.LEAD_ENEMY_PARTICLE; @@ -94,6 +95,7 @@ public static void register() { Registry.register(BuiltInRegistries.PARTICLE_TYPE, new ResourceLocation("creaturechat", "flee"), FLEE_PARTICLE); Registry.register(BuiltInRegistries.PARTICLE_TYPE, new ResourceLocation("creaturechat", "follow_enemy"), FOLLOW_ENEMY_PARTICLE); Registry.register(BuiltInRegistries.PARTICLE_TYPE, new ResourceLocation("creaturechat", "follow_friend"), FOLLOW_FRIEND_PARTICLE); + Registry.register(BuiltInRegistries.PARTICLE_TYPE, new ResourceLocation("creaturechat", "build"), BUILD_PARTICLE); Registry.register(BuiltInRegistries.PARTICLE_TYPE, new ResourceLocation("creaturechat", "protect"), PROTECT_PARTICLE); Registry.register(BuiltInRegistries.PARTICLE_TYPE, new ResourceLocation("creaturechat", "lead_enemy"), LEAD_ENEMY_PARTICLE); Registry.register(BuiltInRegistries.PARTICLE_TYPE, new ResourceLocation("creaturechat", "lead_friend"), LEAD_FRIEND_PARTICLE); diff --git a/src/main/java/com/owlmaddie/particle/Particles.java b/src/main/java/com/owlmaddie/particle/Particles.java index 406232a8..44b5d6b8 100644 --- a/src/main/java/com/owlmaddie/particle/Particles.java +++ b/src/main/java/com/owlmaddie/particle/Particles.java @@ -19,6 +19,7 @@ public class Particles { public static final SimpleParticleType FLEE_PARTICLE = FabricParticleTypes.simple(); public static final SimpleParticleType FOLLOW_FRIEND_PARTICLE = FabricParticleTypes.simple(); public static final SimpleParticleType FOLLOW_ENEMY_PARTICLE = FabricParticleTypes.simple(); + public static final SimpleParticleType BUILD_PARTICLE = FabricParticleTypes.simple(); public static final SimpleParticleType PROTECT_PARTICLE = FabricParticleTypes.simple(); public static final SimpleParticleType LEAD_FRIEND_PARTICLE = FabricParticleTypes.simple(); public static final SimpleParticleType LEAD_ENEMY_PARTICLE = FabricParticleTypes.simple(); diff --git a/src/main/resources/assets/creaturechat/particles/build.json b/src/main/resources/assets/creaturechat/particles/build.json new file mode 100644 index 00000000..2288ad98 --- /dev/null +++ b/src/main/resources/assets/creaturechat/particles/build.json @@ -0,0 +1,5 @@ +{ + "textures": [ + "creaturechat:build" + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/creaturechat/textures/particle/build.png b/src/main/resources/assets/creaturechat/textures/particle/build.png new file mode 100644 index 0000000000000000000000000000000000000000..057d3e81c76ef1dab361445c1001748a888cf5ba GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ii4<#Ar*1S2@+Qh95Q=l&#|~$ znAJflJYMg2{r8(Hmv|X7fBdi1l$e~u;bZI}yh%~P=Yl}W?2|?;&+=p1RCM(f&oPuZ zC(QCRVz?mdKI;Vst0GHx3bpQYW literal 0 HcmV?d00001 diff --git a/src/vs/v1_20_5/main/java/com/owlmaddie/particle/Particles.java b/src/vs/v1_20_5/main/java/com/owlmaddie/particle/Particles.java index cd9d6270..32b5f7d5 100644 --- a/src/vs/v1_20_5/main/java/com/owlmaddie/particle/Particles.java +++ b/src/vs/v1_20_5/main/java/com/owlmaddie/particle/Particles.java @@ -20,6 +20,7 @@ public class Particles { public static final SimpleParticleType FLEE_PARTICLE = FabricParticleTypes.simple(); public static final SimpleParticleType FOLLOW_FRIEND_PARTICLE = FabricParticleTypes.simple(); public static final SimpleParticleType FOLLOW_ENEMY_PARTICLE = FabricParticleTypes.simple(); + public static final SimpleParticleType BUILD_PARTICLE = FabricParticleTypes.simple(); public static final SimpleParticleType PROTECT_PARTICLE = FabricParticleTypes.simple(); public static final SimpleParticleType LEAD_FRIEND_PARTICLE = FabricParticleTypes.simple(); public static final SimpleParticleType LEAD_ENEMY_PARTICLE = FabricParticleTypes.simple(); From fdc5653886d25275dd4954953b4621440928f9dc Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 3 Jan 2026 21:03:49 -0600 Subject: [PATCH 19/30] Require full collision blocks when selecting the build start ground --- CHANGELOG.md | 1 + .../com/owlmaddie/buildrec/BuildRecorder.java | 18 +++++ .../com/owlmaddie/goals/BuildPlayerGoal.java | 75 ++++++++++++++----- 3 files changed, 74 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b449286d..3d754fdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Build goal only begins after the builder reaches the player - Selecting builds logs skill, type, height tier, and chosen file - Relaxed build goal proximity checks so builders don't have to overlap players before starting or finishing + - Require full collision blocks when selecting the build start ground ### Changed - Convert PNG screenshots to JPEG, compress, and remove less useful ones (smaller jar) diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index b629cc93..1f726b68 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -290,6 +290,24 @@ public static void resumeReplay(Mob actor) { }); } + public static void cancelReplay(Mob actor) { + Iterator it = REPLAYS.iterator(); + while (it.hasNext()) { + Replay r = it.next(); + if (r.actor != actor) { + continue; + } + r.actor.setNoAi(false); + r.actor.setInvulnerable(false); + MISSING_RECIPES.remove(r.actor); + if (!r.preserveActor) { + r.actor.discard(); + } + it.remove(); + break; + } + } + public static void cancelAllReplays() { Iterator it = REPLAYS.iterator(); while (it.hasNext()) { diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index accca66e..43a3642c 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -13,6 +13,7 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.network.chat.Component; import net.minecraft.ChatFormatting; +import net.minecraft.util.Mth; import net.minecraft.world.entity.Mob; import net.minecraft.world.level.Level; import net.minecraft.world.level.pathfinder.Path; @@ -88,7 +89,11 @@ public void start() { @Override public void stop() { LOGGER.info("[BuildGoal] stop goal pause replay"); - BuildRecorder.pauseReplay(this.entity); + if (completed || this.targetEntity == null || !this.targetEntity.isAlive()) { + BuildRecorder.cancelReplay(this.entity); + } else { + BuildRecorder.pauseReplay(this.entity); + } } @Override @@ -99,7 +104,7 @@ public void tick() { if (!reachedPlayer) { double distToPlayer = this.entity.distanceToSqr(this.targetEntity); if (distToPlayer <= PLAYER_REACH_DIST_SQR) { - buildPos = findGround(this.targetEntity.blockPosition()); + buildPos = findStartPos(BlockPos.containing(this.targetEntity.position())); reachedPlayer = true; LOGGER.info("[BuildGoal] reached player choose buildPos {}", buildPos); this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); @@ -135,7 +140,7 @@ public void tick() { return; } - if (BuildRecorder.isReplaying(this.entity)) { + if (BuildRecorder.isReplaying(this.entity)) { if (!fetchingMaterials) { if (aiPause) { aiPause = false; @@ -150,11 +155,13 @@ public void tick() { Map recipe = BuildRecorder.getMissingRecipe(this.entity); if (recipe != null) { + if (!fetchingMaterials) { + materialWaitTicks = 0; + } fetchingMaterials = true; double distToPlayer = this.entity.distanceToSqr(this.targetEntity); if (distToPlayer > PLAYER_REACH_DIST_SQR && !controlsReleased) { this.entity.getNavigation().moveTo(this.targetEntity, this.speed); - materialWaitTicks = 0; } else { if (!controlsReleased) { this.entity.getNavigation().stop(); @@ -181,18 +188,18 @@ public void tick() { sentRecipe = true; } } - if (!controlsReleased) { - if (materialWaitTicks++ >= 80) { - BuildRecorder.pauseReplay(this.entity); - this.setFlags(EnumSet.noneOf(Flag.class)); - this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); - controlsReleased = true; - } - } else { - double distToBuild = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); - if (distToBuild > 36 && !this.entity.getNavigation().isInProgress()) { - this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); - } + } + if (!controlsReleased) { + if (materialWaitTicks++ >= 80) { + BuildRecorder.pauseReplay(this.entity); + this.setFlags(EnumSet.noneOf(Flag.class)); + this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); + controlsReleased = true; + } + } else { + double distToBuild = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); + if (distToBuild > 36 && !this.entity.getNavigation().isInProgress()) { + this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); } } return; @@ -269,13 +276,17 @@ public void tick() { private BlockPos findStartPos(BlockPos target) { BlockPos ground = findGround(target); - if (this.entity.getNavigation().createPath(ground.getX(), ground.getY() + 1, ground.getZ(), 1) != null) { + if (isValidBuildPos(ground) && + this.entity.getNavigation().createPath(ground.getX(), ground.getY() + 1, ground.getZ(), 1) != null) { return ground; } BlockPos best = null; double bestDist = Double.MAX_VALUE; for (BlockPos pos : BlockPos.betweenClosed(target.offset(-3, -1, -3), target.offset(3, 1, 3))) { BlockPos g = findGround(pos); + if (!isValidBuildPos(g)) { + continue; + } if (this.entity.getNavigation().createPath(g.getX(), g.getY() + 1, g.getZ(), 1) != null) { double d = g.distSqr(target); if (d < bestDist) { @@ -284,15 +295,39 @@ private BlockPos findStartPos(BlockPos target) { } } } - return best != null ? best : this.entity.blockPosition(); + return best != null ? best : ground; } private BlockPos findGround(BlockPos pos) { Level level = this.entity.level(); BlockPos ground = pos; - while (level.isEmptyBlock(ground) && ground.getY() > -64) { + while (ground.getY() > -64) { + if (isSolidGround(level, ground)) { + return ground; + } ground = ground.below(); } - return ground; + return pos; + } + + private boolean isSolidGround(Level level, BlockPos pos) { + var state = level.getBlockState(pos); + if (state.isAir()) { + return false; + } + if (!state.getFluidState().isEmpty()) { + return false; + } + return state.isCollisionShapeFullBlock(level, pos); + } + + private boolean isValidBuildPos(BlockPos ground) { + int height = Mth.ceil(this.entity.getBbHeight()); + for (int i = 1; i <= height; i++) { + if (!this.entity.level().isEmptyBlock(ground.above(i))) { + return false; + } + } + return true; } } From f7866abd434bd7f2ff3d14f5a3f4461ddc02bd75 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 3 Jan 2026 21:20:27 -0600 Subject: [PATCH 20/30] Fixed distance of hitboxes between entity and player to begin builds, and use a separate distance (3 blocks) when the entity returns to the player to deliver a message --- .../com/owlmaddie/goals/BuildPlayerGoal.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index 43a3642c..9b48ce1c 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -17,6 +17,7 @@ import net.minecraft.world.entity.Mob; import net.minecraft.world.level.Level; import net.minecraft.world.level.pathfinder.Path; +import net.minecraft.world.phys.AABB; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +30,8 @@ * replay as the goal starts and stops. */ public class BuildPlayerGoal extends PlayerBaseGoal { - private static final double PLAYER_REACH_DIST_SQR = 25.0; + private static final double PLAYER_MESSAGE_DIST_SQR = 9.0; + private static final double START_CLOSE_DIST_SQR = 0.25; private final Mob entity; private final String buildType; private final double speed; @@ -102,8 +104,7 @@ public void tick() { if (!startedReplay) { if (!reachedPlayer) { - double distToPlayer = this.entity.distanceToSqr(this.targetEntity); - if (distToPlayer <= PLAYER_REACH_DIST_SQR) { + if (isStartCloseToPlayer()) { buildPos = findStartPos(BlockPos.containing(this.targetEntity.position())); reachedPlayer = true; LOGGER.info("[BuildGoal] reached player choose buildPos {}", buildPos); @@ -160,7 +161,7 @@ public void tick() { } fetchingMaterials = true; double distToPlayer = this.entity.distanceToSqr(this.targetEntity); - if (distToPlayer > PLAYER_REACH_DIST_SQR && !controlsReleased) { + if (distToPlayer > PLAYER_MESSAGE_DIST_SQR && !controlsReleased) { this.entity.getNavigation().moveTo(this.targetEntity, this.speed); } else { if (!controlsReleased) { @@ -259,7 +260,7 @@ public void tick() { this.entity.getNavigation().moveTo(player, this.speed); } else if (finishing && this.targetEntity instanceof ServerPlayer player) { LookControls.lookAtPlayer(player, this.entity); - if (this.entity.distanceToSqr(player) <= PLAYER_REACH_DIST_SQR) { + if (this.entity.distanceToSqr(player) <= PLAYER_MESSAGE_DIST_SQR) { EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); String type = (actualType == null || actualType.isEmpty()) ? "structure" : actualType; String msg = ""; @@ -330,4 +331,19 @@ private boolean isValidBuildPos(BlockPos ground) { } return true; } + + private boolean isStartCloseToPlayer() { + if (this.targetEntity == null) { + return false; + } + AABB a = this.entity.getBoundingBox(); + AABB b = this.targetEntity.getBoundingBox(); + if (a.intersects(b)) { + return true; + } + double dx = Math.max(0.0, Math.max(b.minX - a.maxX, a.minX - b.maxX)); + double dy = Math.max(0.0, Math.max(b.minY - a.maxY, a.minY - b.maxY)); + double dz = Math.max(0.0, Math.max(b.minZ - a.maxZ, a.minZ - b.maxZ)); + return dx * dx + dy * dy + dz * dz <= START_CLOSE_DIST_SQR; + } } From 71a592260633c4beb4d74ceb234e5ce353c46b7f Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 3 Jan 2026 21:30:59 -0600 Subject: [PATCH 21/30] Fixed canceling builds - to actually remove it from the replay queue --- src/main/java/com/owlmaddie/buildrec/BuildRecorder.java | 2 +- src/main/java/com/owlmaddie/chat/EntityChatData.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 1f726b68..29845cd3 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -201,6 +201,7 @@ public static boolean startReplay(ServerPlayer player, String fileName, EntityTy @SuppressWarnings("unchecked") public static boolean startReplay(ServerPlayer player, Mob actor, String fileName, int speed) { + cancelReplay(actor); return startReplayInternal(player, fileName, (EntityType) actor.getType(), speed, actor); } @@ -855,4 +856,3 @@ private static class Replay { } } - diff --git a/src/main/java/com/owlmaddie/chat/EntityChatData.java b/src/main/java/com/owlmaddie/chat/EntityChatData.java index bcbcda48..c5152808 100644 --- a/src/main/java/com/owlmaddie/chat/EntityChatData.java +++ b/src/main/java/com/owlmaddie/chat/EntityChatData.java @@ -5,6 +5,7 @@ import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; +import com.owlmaddie.buildrec.BuildRecorder; import com.owlmaddie.commands.ConfigurationHandler; import com.owlmaddie.controls.SpeedControls; import com.owlmaddie.goals.*; @@ -591,6 +592,7 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use entity.level().playSound(null, entity.blockPosition(), SoundEvents.NOTE_BLOCK_BELL.value(), SoundSource.PLAYERS, 1f, 1f); } else if (behavior.getName().equals("UNBUILD")) { + BuildRecorder.cancelReplay(entity); EntityBehaviorManager.removeGoal(entity, BuildPlayerGoal.class); } else if (behavior.getName().equals("FRIENDSHIP")) { From 94b6c3009ad91541707ff8f0aa35bd38da5d9f69 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 3 Jan 2026 22:12:19 -0600 Subject: [PATCH 22/30] Fixing move logic on "start" of build goal --- .../com/owlmaddie/goals/BuildPlayerGoal.java | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index 9b48ce1c..c607c399 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -77,7 +77,7 @@ public void start() { LOGGER.info("[BuildGoal] start navigation toward player"); reachedPlayer = false; buildPos = null; - this.entity.getNavigation().moveTo(this.targetEntity, this.speed); + moveTowardPlayer(false); } else { BlockPos cursor = BuildRecorder.getReplayCursor(this.entity); if (cursor != null) { @@ -108,17 +108,17 @@ public void tick() { buildPos = findStartPos(BlockPos.containing(this.targetEntity.position())); reachedPlayer = true; LOGGER.info("[BuildGoal] reached player choose buildPos {}", buildPos); - this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); } else { - this.entity.getNavigation().moveTo(this.targetEntity, this.speed); + double distToPlayer = this.entity.distanceToSqr(this.targetEntity); + moveTowardPlayer(distToPlayer <= 4.0); return; } } - double dist = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); - if (dist <= 1.0 || !this.entity.getNavigation().isInProgress()) { - // ensure the actor stands on the surface so replay bases aren't one block too low - this.entity.teleportTo(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); + double distToPlayer = this.entity.distanceToSqr(this.targetEntity); + moveTowardPlayer(distToPlayer <= 4.0); + if (isStartCloseToPlayer()) { + this.entity.getNavigation().stop(); EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); int tier = this.entity.getBbHeight() < 1 ? 1 : (this.entity.getBbHeight() < 2 ? 2 : 3); String file = BuildRecorder.randomBuildFile(this.entity.getBbHeight(), buildType, data.buildLevel); @@ -135,8 +135,6 @@ public void tick() { completed = true; LOGGER.info("[BuildGoal] failed to start replay type={}", buildType); } - } else { - this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); } return; } @@ -261,6 +259,7 @@ public void tick() { } else if (finishing && this.targetEntity instanceof ServerPlayer player) { LookControls.lookAtPlayer(player, this.entity); if (this.entity.distanceToSqr(player) <= PLAYER_MESSAGE_DIST_SQR) { + this.entity.getNavigation().stop(); EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); String type = (actualType == null || actualType.isEmpty()) ? "structure" : actualType; String msg = ""; @@ -346,4 +345,26 @@ private boolean isStartCloseToPlayer() { double dz = Math.max(0.0, Math.max(b.minZ - a.maxZ, a.minZ - b.maxZ)); return dx * dx + dy * dy + dz * dz <= START_CLOSE_DIST_SQR; } + + private void moveTowardPlayer(boolean forceClose) { + if (this.targetEntity == null) { + return; + } + if (forceClose) { + this.entity.getNavigation().stop(); + this.entity.getMoveControl().setWantedPosition( + this.targetEntity.getX(), + this.targetEntity.getY(), + this.targetEntity.getZ(), + this.speed + ); + } else { + this.entity.getNavigation().moveTo( + this.targetEntity.getX(), + this.targetEntity.getY(), + this.targetEntity.getZ(), + this.speed + ); + } + } } From 9e05b77875b9423b50aab30f3cb0681ac894d0f8 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 3 Jan 2026 22:25:20 -0600 Subject: [PATCH 23/30] Support random build categories better. --- src/main/java/com/owlmaddie/chat/EntityChatData.java | 6 +++++- src/main/resources/data/creaturechat/prompts/system-chat | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/owlmaddie/chat/EntityChatData.java b/src/main/java/com/owlmaddie/chat/EntityChatData.java index c5152808..baff2519 100644 --- a/src/main/java/com/owlmaddie/chat/EntityChatData.java +++ b/src/main/java/com/owlmaddie/chat/EntityChatData.java @@ -581,7 +581,11 @@ public void generateMessage(String userLanguage, ServerPlayer player, String use EntityBehaviorManager.removeGoal(entity, LeadPlayerGoal.class); } else if (behavior.getName().equals("BUILD")) { - BuildPlayerGoal buildGoal = new BuildPlayerGoal(player, entity, entitySpeedMedium, behavior.getArgument()); + String buildType = behavior.getArgument(); + if (buildType != null && buildType.equalsIgnoreCase("random")) { + buildType = null; + } + BuildPlayerGoal buildGoal = new BuildPlayerGoal(player, entity, entitySpeedMedium, buildType); EntityBehaviorManager.removeGoal(entity, FollowPlayerGoal.class); EntityBehaviorManager.removeGoal(entity, FleePlayerGoal.class); EntityBehaviorManager.removeGoal(entity, AttackPlayerGoal.class); diff --git a/src/main/resources/data/creaturechat/prompts/system-chat b/src/main/resources/data/creaturechat/prompts/system-chat index 417fe83b..b277c019 100644 --- a/src/main/resources/data/creaturechat/prompts/system-chat +++ b/src/main/resources/data/creaturechat/prompts/system-chat @@ -51,7 +51,7 @@ Include as many behaviors as needed at the end of the message. These are the ONL Attack the player (if you are strong and brave). If the player threatens you, please output this behavior to attack the player and defend yourself. Protect and defend ONLY the player when they are attacked (if you are strong and brave). Please output this behavior to keep the player alive and safe. Stop protecting the player. - Build a structure for the player. Types: house, statue, pond, garden, farm, castle, mine, trap, campfire. If no type or an unknown one is given, choose a random valid type. + Build a structure for the player. Types: house, statue, pond, garden, farm, castle, mine, trap, campfire. If no type or an unknown one is given, choose a random valid type and output that exact type (never output "random"). Stop building and cancel the current build. When missing materials for build, do not output this. Rules: From 1e615cd37ac891d91ccfdad8ba302229decb0912 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sun, 4 Jan 2026 14:40:00 -0600 Subject: [PATCH 24/30] Friendly mobs with chat data now pick up items thrown by friends into their chat inventory. Expanded friendly pickup reach and restored pickup sound for chat-data mobs. Compatible with 1.20.4 to 1.21.7 --- CHANGELOG.md | 2 + .../com/owlmaddie/mixin/MixinMobEntity.java | 121 +++++- .../com/owlmaddie/mixin/MixinMobEntity.java | 119 ++++++ .../com/owlmaddie/mixin/MixinMobEntity.java | 122 +++++- .../com/owlmaddie/mixin/MixinMobEntity.java | 359 ++++++++++++++++++ .../com/owlmaddie/mixin/MixinMobEntity.java | 122 +++++- .../com/owlmaddie/mixin/MixinMobEntity.java | 134 ++++++- 7 files changed, 975 insertions(+), 4 deletions(-) create mode 100644 src/vs/v1_21_2/main/java/com/owlmaddie/mixin/MixinMobEntity.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d754fdf..9639ee14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Selecting builds logs skill, type, height tier, and chosen file - Relaxed build goal proximity checks so builders don't have to overlap players before starting or finishing - Require full collision blocks when selecting the build start ground +- Friendly mobs with chat data now pick up items thrown by friends into their chat inventory +- Expanded friendly pickup reach and restored pickup sound for chat-data mobs ### Changed - Convert PNG screenshots to JPEG, compress, and remove less useful ones (smaller jar) diff --git a/src/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/main/java/com/owlmaddie/mixin/MixinMobEntity.java index f7cb6066..62719270 100644 --- a/src/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -10,6 +10,7 @@ import com.owlmaddie.inventory.MobInventoryMenu; import com.owlmaddie.network.ServerPackets; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; +import net.minecraft.core.Vec3i; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.network.FriendlyByteBuf; @@ -19,6 +20,8 @@ import net.minecraft.world.InteractionResult; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.HasCustomInventoryScreen; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.TamableAnimal; @@ -123,6 +126,67 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId } } + @Inject(method = "pickUpItem", at = @At("HEAD"), cancellable = true) + private void creaturechat$pickupFriendItem(ItemEntity itemEntity, CallbackInfo ci) { + Mob thisEntity = (Mob) (Object) this; + if (thisEntity.level().isClientSide()) { + return; + } + + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData == null || chatData.status == ChatDataManager.ChatStatus.NONE) { + return; + } + + Entity owner = itemEntity.getOwner(); + if (!(owner instanceof Player throwerPlayer)) { + ci.cancel(); + return; + } + + PlayerData playerData = chatData.getPlayerData(throwerPlayer.getDisplayName().getString()); + if (playerData.friendship <= 0) { + ci.cancel(); + return; + } + + ItemStack stack = itemEntity.getItem(); + if (stack.isEmpty()) { + ci.cancel(); + return; + } + + ItemStack remaining = stack.copy(); + creaturechat$insertIntoInventory(remaining); + int pickedUp = stack.getCount() - remaining.getCount(); + if (pickedUp > 0) { + thisEntity.onItemPickup(itemEntity); + thisEntity.take(itemEntity, pickedUp); + } + if (remaining.isEmpty()) { + itemEntity.discard(); + } else { + itemEntity.setItem(remaining); + } + + ci.cancel(); + } + + @Inject(method = "getPickupReach", at = @At("RETURN"), cancellable = true) + private void creaturechat$expandPickupReach(CallbackInfoReturnable cir) { + Vec3i reach = cir.getReturnValue(); + cir.setReturnValue(new Vec3i(reach.getX() + 1, reach.getY() + 1, reach.getZ() + 1)); + } + + @Inject(method = "canPickUpLoot", at = @At("HEAD"), cancellable = true) + private void creaturechat$allowFriendLoot(CallbackInfoReturnable cir) { + Mob thisEntity = (Mob) (Object) this; + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData != null && chatData.status != ChatDataManager.ChatStatus.NONE) { + cir.setReturnValue(true); + } + } + @Inject(method = "addAdditionalSaveData", at = @At("RETURN")) private void creaturechat$saveInventory(CompoundTag tag, CallbackInfo ci) { ListTag listTag = new ListTag(); @@ -231,4 +295,59 @@ private void onItemGiven(Player player, InteractionHand hand, CallbackInfoReturn } } } -} \ No newline at end of file + + private void creaturechat$insertIntoInventory(ItemStack stack) { + if (stack.isEmpty()) { + return; + } + + int size = creaturechat$inventory.getContainerSize(); + int mainHandSlot = creaturechat$getMainHandSlot(size); + int offHandSlot = mainHandSlot + 1; + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (existing.isEmpty()) { + continue; + } + if (!ItemStack.isSameItemSameTags(existing, stack)) { + continue; + } + int max = Math.min(existing.getMaxStackSize(), creaturechat$inventory.getMaxStackSize()); + int space = max - existing.getCount(); + if (space <= 0) { + continue; + } + int moved = Math.min(space, stack.getCount()); + existing.grow(moved); + stack.shrink(moved); + creaturechat$inventory.setItem(i, existing); + if (stack.isEmpty()) { + return; + } + } + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (!existing.isEmpty()) { + continue; + } + int moved = Math.min(stack.getCount(), Math.min(stack.getMaxStackSize(), creaturechat$inventory.getMaxStackSize())); + creaturechat$inventory.setItem(i, stack.split(moved)); + if (stack.isEmpty()) { + return; + } + } + } + + private static int creaturechat$getMainHandSlot(int size) { + int rows = (size + 4) / 5; + return Math.max(0, rows - 1) * 5; + } +} diff --git a/src/vs/v1_20_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/vs/v1_20_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java index 1fb089f4..f1c98030 100644 --- a/src/vs/v1_20_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/vs/v1_20_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -11,6 +11,7 @@ import com.owlmaddie.network.ServerPackets; import net.minecraft.world.entity.HasCustomInventoryScreen; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; +import net.minecraft.core.Vec3i; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; @@ -21,6 +22,8 @@ import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.TamableAnimal; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.npc.Villager; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -122,6 +125,67 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId } } + @Inject(method = "pickUpItem", at = @At("HEAD"), cancellable = true) + private void creaturechat$pickupFriendItem(ItemEntity itemEntity, CallbackInfo ci) { + Mob thisEntity = (Mob) (Object) this; + if (thisEntity.level().isClientSide()) { + return; + } + + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData == null || chatData.status == ChatDataManager.ChatStatus.NONE) { + return; + } + + Entity owner = itemEntity.getOwner(); + if (!(owner instanceof Player throwerPlayer)) { + ci.cancel(); + return; + } + + PlayerData playerData = chatData.getPlayerData(throwerPlayer.getDisplayName().getString()); + if (playerData.friendship <= 0) { + ci.cancel(); + return; + } + + ItemStack stack = itemEntity.getItem(); + if (stack.isEmpty()) { + ci.cancel(); + return; + } + + ItemStack remaining = stack.copy(); + creaturechat$insertIntoInventory(remaining); + int pickedUp = stack.getCount() - remaining.getCount(); + if (pickedUp > 0) { + thisEntity.onItemPickup(itemEntity); + thisEntity.take(itemEntity, pickedUp); + } + if (remaining.isEmpty()) { + itemEntity.discard(); + } else { + itemEntity.setItem(remaining); + } + + ci.cancel(); + } + + @Inject(method = "getPickupReach", at = @At("RETURN"), cancellable = true) + private void creaturechat$expandPickupReach(CallbackInfoReturnable cir) { + Vec3i reach = cir.getReturnValue(); + cir.setReturnValue(new Vec3i(reach.getX() + 1, reach.getY() + 1, reach.getZ() + 1)); + } + + @Inject(method = "canPickUpLoot", at = @At("HEAD"), cancellable = true) + private void creaturechat$allowFriendLoot(CallbackInfoReturnable cir) { + Mob thisEntity = (Mob) (Object) this; + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData != null && chatData.status != ChatDataManager.ChatStatus.NONE) { + cir.setReturnValue(true); + } + } + @Inject(method = "addAdditionalSaveData", at = @At("RETURN")) private void creaturechat$saveInventory(CompoundTag tag, CallbackInfo ci) { ListTag listTag = new ListTag(); @@ -233,4 +297,59 @@ private void onItemGiven(Player player, InteractionHand hand, CallbackInfoReturn } } } + + private void creaturechat$insertIntoInventory(ItemStack stack) { + if (stack.isEmpty()) { + return; + } + + int size = creaturechat$inventory.getContainerSize(); + int mainHandSlot = creaturechat$getMainHandSlot(size); + int offHandSlot = mainHandSlot + 1; + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (existing.isEmpty()) { + continue; + } + if (!ItemStack.isSameItemSameComponents(existing, stack)) { + continue; + } + int max = Math.min(existing.getMaxStackSize(), creaturechat$inventory.getMaxStackSize()); + int space = max - existing.getCount(); + if (space <= 0) { + continue; + } + int moved = Math.min(space, stack.getCount()); + existing.grow(moved); + stack.shrink(moved); + creaturechat$inventory.setItem(i, existing); + if (stack.isEmpty()) { + return; + } + } + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (!existing.isEmpty()) { + continue; + } + int moved = Math.min(stack.getCount(), Math.min(stack.getMaxStackSize(), creaturechat$inventory.getMaxStackSize())); + creaturechat$inventory.setItem(i, stack.split(moved)); + if (stack.isEmpty()) { + return; + } + } + } + + private static int creaturechat$getMainHandSlot(int size) { + int rows = (size + 4) / 5; + return Math.max(0, rows - 1) * 5; + } } diff --git a/src/vs/v1_21_0/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/vs/v1_21_0/main/java/com/owlmaddie/mixin/MixinMobEntity.java index 474866f8..a75d4148 100644 --- a/src/vs/v1_21_0/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/vs/v1_21_0/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -11,7 +11,9 @@ import com.owlmaddie.network.ServerPackets; import net.minecraft.world.entity.HasCustomInventoryScreen; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; +import net.minecraft.core.Vec3i; import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -22,6 +24,8 @@ import net.minecraft.nbt.ListTag; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.TamableAnimal; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.npc.Villager; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -122,6 +126,67 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId } } + @Inject(method = "pickUpItem", at = @At("HEAD"), cancellable = true) + private void creaturechat$pickupFriendItem(ServerLevel level, ItemEntity itemEntity, CallbackInfo ci) { + Mob thisEntity = (Mob) (Object) this; + if (thisEntity.level().isClientSide()) { + return; + } + + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData == null || chatData.status == ChatDataManager.ChatStatus.NONE) { + return; + } + + Entity owner = itemEntity.getOwner(); + if (!(owner instanceof Player throwerPlayer)) { + ci.cancel(); + return; + } + + PlayerData playerData = chatData.getPlayerData(throwerPlayer.getDisplayName().getString()); + if (playerData.friendship <= 0) { + ci.cancel(); + return; + } + + ItemStack stack = itemEntity.getItem(); + if (stack.isEmpty()) { + ci.cancel(); + return; + } + + ItemStack remaining = stack.copy(); + creaturechat$insertIntoInventory(remaining); + int pickedUp = stack.getCount() - remaining.getCount(); + if (pickedUp > 0) { + thisEntity.onItemPickup(itemEntity); + thisEntity.take(itemEntity, pickedUp); + } + if (remaining.isEmpty()) { + itemEntity.discard(); + } else { + itemEntity.setItem(remaining); + } + + ci.cancel(); + } + + @Inject(method = "getPickupReach", at = @At("RETURN"), cancellable = true) + private void creaturechat$expandPickupReach(CallbackInfoReturnable cir) { + Vec3i reach = cir.getReturnValue(); + cir.setReturnValue(new Vec3i(reach.getX() + 1, reach.getY() + 1, reach.getZ() + 1)); + } + + @Inject(method = "canPickUpLoot", at = @At("HEAD"), cancellable = true) + private void creaturechat$allowFriendLoot(CallbackInfoReturnable cir) { + Mob thisEntity = (Mob) (Object) this; + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData != null && chatData.status != ChatDataManager.ChatStatus.NONE) { + cir.setReturnValue(true); + } + } + @Inject(method = "addAdditionalSaveData", at = @At("RETURN")) private void creaturechat$saveInventory(CompoundTag tag, CallbackInfo ci) { ListTag listTag = new ListTag(); @@ -236,4 +301,59 @@ private void onItemGiven(Player player, InteractionHand hand, CallbackInfoReturn } } } -} \ No newline at end of file + + private void creaturechat$insertIntoInventory(ItemStack stack) { + if (stack.isEmpty()) { + return; + } + + int size = creaturechat$inventory.getContainerSize(); + int mainHandSlot = creaturechat$getMainHandSlot(size); + int offHandSlot = mainHandSlot + 1; + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (existing.isEmpty()) { + continue; + } + if (!ItemStack.isSameItemSameComponents(existing, stack)) { + continue; + } + int max = Math.min(existing.getMaxStackSize(), creaturechat$inventory.getMaxStackSize()); + int space = max - existing.getCount(); + if (space <= 0) { + continue; + } + int moved = Math.min(space, stack.getCount()); + existing.grow(moved); + stack.shrink(moved); + creaturechat$inventory.setItem(i, existing); + if (stack.isEmpty()) { + return; + } + } + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (!existing.isEmpty()) { + continue; + } + int moved = Math.min(stack.getCount(), Math.min(stack.getMaxStackSize(), creaturechat$inventory.getMaxStackSize())); + creaturechat$inventory.setItem(i, stack.split(moved)); + if (stack.isEmpty()) { + return; + } + } + } + + private static int creaturechat$getMainHandSlot(int size) { + int rows = (size + 4) / 5; + return Math.max(0, rows - 1) * 5; + } +} diff --git a/src/vs/v1_21_2/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/vs/v1_21_2/main/java/com/owlmaddie/mixin/MixinMobEntity.java new file mode 100644 index 00000000..a75d4148 --- /dev/null +++ b/src/vs/v1_21_2/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -0,0 +1,359 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.mixin; + +import com.owlmaddie.chat.ChatDataManager; +import com.owlmaddie.chat.EntityChatData; +import com.owlmaddie.chat.PlayerData; +import com.owlmaddie.inventory.ChatInventory; +import com.owlmaddie.inventory.MobInventoryMenu; +import com.owlmaddie.network.ServerPackets; +import net.minecraft.world.entity.HasCustomInventoryScreen; +import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; +import net.minecraft.core.Vec3i; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.SimpleContainer; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.TamableAnimal; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +/** + * The {@code MixinMobEntity} mixin class exposes the goalSelector field from the MobEntity class. + */ +@Mixin(Mob.class) +public class MixinMobEntity implements ChatInventory, HasCustomInventoryScreen { + + private final SimpleContainer creaturechat$inventory = new SimpleContainer(15); + + @Override + public SimpleContainer creaturechat$getInventory() { + return creaturechat$inventory; + } + + @Override + public void openCustomInventoryScreen(Player player) { + Mob thisEntity = (Mob) (Object) this; + if (thisEntity instanceof Villager || thisEntity instanceof TamableAnimal) { + return; + } + + if (player instanceof ServerPlayer serverPlayer) { + ExtendedScreenHandlerFactory provider = new ExtendedScreenHandlerFactory<>() { + @Override + public Integer getScreenOpeningData(ServerPlayer p) { + return thisEntity.getId(); + } + + @Override + public Component getDisplayName() { + return thisEntity.getDisplayName(); + } + + @Override + public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player p) { + return new MobInventoryMenu(syncId, playerInventory, creaturechat$inventory, thisEntity, serverPlayer); + } + }; + serverPlayer.openMenu(provider); + } + } + + @Inject(method = "interact", at = @At("HEAD"), cancellable = true) + private void creaturechat$openInventory(Player player, InteractionHand hand, CallbackInfoReturnable cir) { + if (player.level().isClientSide()) { + return; + } + + if (hand != InteractionHand.MAIN_HAND) { + return; + } + + if (!player.isSecondaryUseActive()) { + return; + } + + Mob thisEntity = (Mob) (Object) this; + + if (thisEntity instanceof Villager || thisEntity instanceof TamableAnimal) { + return; + } + + // Only open the inventory if chat data exists and has been used + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData == null || chatData.status == ChatDataManager.ChatStatus.NONE) { + return; + } + + if (player instanceof ServerPlayer serverPlayer) { + ExtendedScreenHandlerFactory provider = new ExtendedScreenHandlerFactory<>() { + @Override + public Integer getScreenOpeningData(ServerPlayer p) { + return thisEntity.getId(); + } + + @Override + public Component getDisplayName() { + return thisEntity.getDisplayName(); + } + + @Override + public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId, Inventory playerInventory, Player p) { + return new MobInventoryMenu(syncId, playerInventory, creaturechat$inventory, thisEntity, serverPlayer); + } + }; + serverPlayer.openMenu(provider); + cir.setReturnValue(InteractionResult.SUCCESS); + } + } + + @Inject(method = "pickUpItem", at = @At("HEAD"), cancellable = true) + private void creaturechat$pickupFriendItem(ServerLevel level, ItemEntity itemEntity, CallbackInfo ci) { + Mob thisEntity = (Mob) (Object) this; + if (thisEntity.level().isClientSide()) { + return; + } + + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData == null || chatData.status == ChatDataManager.ChatStatus.NONE) { + return; + } + + Entity owner = itemEntity.getOwner(); + if (!(owner instanceof Player throwerPlayer)) { + ci.cancel(); + return; + } + + PlayerData playerData = chatData.getPlayerData(throwerPlayer.getDisplayName().getString()); + if (playerData.friendship <= 0) { + ci.cancel(); + return; + } + + ItemStack stack = itemEntity.getItem(); + if (stack.isEmpty()) { + ci.cancel(); + return; + } + + ItemStack remaining = stack.copy(); + creaturechat$insertIntoInventory(remaining); + int pickedUp = stack.getCount() - remaining.getCount(); + if (pickedUp > 0) { + thisEntity.onItemPickup(itemEntity); + thisEntity.take(itemEntity, pickedUp); + } + if (remaining.isEmpty()) { + itemEntity.discard(); + } else { + itemEntity.setItem(remaining); + } + + ci.cancel(); + } + + @Inject(method = "getPickupReach", at = @At("RETURN"), cancellable = true) + private void creaturechat$expandPickupReach(CallbackInfoReturnable cir) { + Vec3i reach = cir.getReturnValue(); + cir.setReturnValue(new Vec3i(reach.getX() + 1, reach.getY() + 1, reach.getZ() + 1)); + } + + @Inject(method = "canPickUpLoot", at = @At("HEAD"), cancellable = true) + private void creaturechat$allowFriendLoot(CallbackInfoReturnable cir) { + Mob thisEntity = (Mob) (Object) this; + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData != null && chatData.status != ChatDataManager.ChatStatus.NONE) { + cir.setReturnValue(true); + } + } + + @Inject(method = "addAdditionalSaveData", at = @At("RETURN")) + private void creaturechat$saveInventory(CompoundTag tag, CallbackInfo ci) { + ListTag listTag = new ListTag(); + HolderLookup.Provider provider = ((Mob) (Object) this).registryAccess(); + + for (int i = 0; i < creaturechat$inventory.getContainerSize(); i++) { + ItemStack stack = creaturechat$inventory.getItem(i); + if (!stack.isEmpty()) { + CompoundTag wrapper = new CompoundTag(); + wrapper.putByte("Slot", (byte) i); + + // ItemStack#save returns the populated tag instead of mutating the provided one. + // Store that result directly so the item id is written correctly. + wrapper.put("Item", stack.save(provider)); + + listTag.add(wrapper); + } + } + + tag.put("CreatureChatInventory", listTag); + } + + @Inject(method = "readAdditionalSaveData", at = @At("RETURN")) + private void creaturechat$loadInventory(CompoundTag tag, CallbackInfo ci) { + ListTag listTag = tag.getList("CreatureChatInventory", 10); + HolderLookup.Provider provider = ((Mob) (Object) this).registryAccess(); + + for (int i = 0; i < listTag.size(); ++i) { + CompoundTag wrapper = listTag.getCompound(i); + int slot = wrapper.getByte("Slot") & 255; + if (slot >= 0 && slot < creaturechat$inventory.getContainerSize()) { + CompoundTag itemTag; + if (wrapper.contains("Item", 10)) { + itemTag = wrapper.getCompound("Item"); + } else { + itemTag = wrapper.copy(); + itemTag.remove("Slot"); + } + ItemStack parsed = ItemStack.parse(provider, itemTag).orElse(ItemStack.EMPTY); + creaturechat$inventory.setItem(slot, parsed); + } + } + } + + @Inject(method = "interact", at = @At(value = "RETURN")) + private void onItemGiven(Player player, InteractionHand hand, CallbackInfoReturnable cir) { + // Only process interactions on the server side + if (player.level().isClientSide()) { + return; + } + + // Only process interactions for the main hand + if (hand != InteractionHand.MAIN_HAND) { + return; + } + + ItemStack itemStack = player.getItemInHand(hand); + Mob thisEntity = (Mob) (Object) this; + + // Don't interact with Villagers (avoid issues with trade UI) OR Tameable (i.e. sit / no-sit) + if (thisEntity instanceof Villager || thisEntity instanceof TamableAnimal) { + return; + } + + // Determine if the item is a bucket + // We don't want to interact on buckets + Item item = itemStack.getItem(); + if (item == Items.BUCKET || + item == Items.WATER_BUCKET || + item == Items.LAVA_BUCKET || + item == Items.POWDER_SNOW_BUCKET || + item == Items.MILK_BUCKET || + item == Items.PUFFERFISH_BUCKET || + item == Items.SALMON_BUCKET || + item == Items.COD_BUCKET || + item == Items.TROPICAL_FISH_BUCKET || + item == Items.AXOLOTL_BUCKET || + item == Items.TADPOLE_BUCKET) { + return; + } + + // Get chat data for entity + ChatDataManager chatDataManager = ChatDataManager.getServerInstance(); + EntityChatData entityData = chatDataManager.getOrCreateChatData(thisEntity.getStringUUID()); + PlayerData playerData = entityData.getPlayerData(player.getDisplayName().getString()); + + // Check if the player successfully interacts with an item + if (player instanceof ServerPlayer) { + // Player has item in hand + if (!itemStack.isEmpty()) { + ServerPlayer serverPlayer = (ServerPlayer) player; + String itemName = itemStack.getItem().getName(itemStack).getString(); + int itemCount = itemStack.getCount(); + + // Decide verb + String action_verb = " shows "; + if (cir.getReturnValue().consumesAction()) { + action_verb = " gives "; + } + + // Prepare a message about the interaction + String giveItemMessage = "<" + serverPlayer.getDisplayName().getString() + + action_verb + "you " + itemCount + " " + itemName + ">"; + + if (!entityData.characterSheet.isEmpty()) { + ServerPackets.generate_chat("N/A", entityData, serverPlayer, thisEntity, giveItemMessage, true); + } + + } else if (itemStack.isEmpty() && playerData.friendship == 3) { + // Player's hand is empty, Ride your best friend! + player.startRiding(thisEntity, true); + } + } + } + + private void creaturechat$insertIntoInventory(ItemStack stack) { + if (stack.isEmpty()) { + return; + } + + int size = creaturechat$inventory.getContainerSize(); + int mainHandSlot = creaturechat$getMainHandSlot(size); + int offHandSlot = mainHandSlot + 1; + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (existing.isEmpty()) { + continue; + } + if (!ItemStack.isSameItemSameComponents(existing, stack)) { + continue; + } + int max = Math.min(existing.getMaxStackSize(), creaturechat$inventory.getMaxStackSize()); + int space = max - existing.getCount(); + if (space <= 0) { + continue; + } + int moved = Math.min(space, stack.getCount()); + existing.grow(moved); + stack.shrink(moved); + creaturechat$inventory.setItem(i, existing); + if (stack.isEmpty()) { + return; + } + } + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (!existing.isEmpty()) { + continue; + } + int moved = Math.min(stack.getCount(), Math.min(stack.getMaxStackSize(), creaturechat$inventory.getMaxStackSize())); + creaturechat$inventory.setItem(i, stack.split(moved)); + if (stack.isEmpty()) { + return; + } + } + } + + private static int creaturechat$getMainHandSlot(int size) { + int rows = (size + 4) / 5; + return Math.max(0, rows - 1) * 5; + } +} diff --git a/src/vs/v1_21_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/vs/v1_21_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java index bcdfc034..f0a3846c 100644 --- a/src/vs/v1_21_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/vs/v1_21_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -11,7 +11,9 @@ import com.owlmaddie.network.ServerPackets; import net.minecraft.world.entity.HasCustomInventoryScreen; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; +import net.minecraft.core.Vec3i; import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -22,6 +24,8 @@ import net.minecraft.nbt.ListTag; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.TamableAnimal; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.npc.Villager; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -122,6 +126,67 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId } } + @Inject(method = "pickUpItem", at = @At("HEAD"), cancellable = true) + private void creaturechat$pickupFriendItem(ServerLevel level, ItemEntity itemEntity, CallbackInfo ci) { + Mob thisEntity = (Mob) (Object) this; + if (thisEntity.level().isClientSide()) { + return; + } + + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData == null || chatData.status == ChatDataManager.ChatStatus.NONE) { + return; + } + + Entity owner = itemEntity.getOwner(); + if (!(owner instanceof Player throwerPlayer)) { + ci.cancel(); + return; + } + + PlayerData playerData = chatData.getPlayerData(throwerPlayer.getDisplayName().getString()); + if (playerData.friendship <= 0) { + ci.cancel(); + return; + } + + ItemStack stack = itemEntity.getItem(); + if (stack.isEmpty()) { + ci.cancel(); + return; + } + + ItemStack remaining = stack.copy(); + creaturechat$insertIntoInventory(remaining); + int pickedUp = stack.getCount() - remaining.getCount(); + if (pickedUp > 0) { + thisEntity.onItemPickup(itemEntity); + thisEntity.take(itemEntity, pickedUp); + } + if (remaining.isEmpty()) { + itemEntity.discard(); + } else { + itemEntity.setItem(remaining); + } + + ci.cancel(); + } + + @Inject(method = "getPickupReach", at = @At("RETURN"), cancellable = true) + private void creaturechat$expandPickupReach(CallbackInfoReturnable cir) { + Vec3i reach = cir.getReturnValue(); + cir.setReturnValue(new Vec3i(reach.getX() + 1, reach.getY() + 1, reach.getZ() + 1)); + } + + @Inject(method = "canPickUpLoot", at = @At("HEAD"), cancellable = true) + private void creaturechat$allowFriendLoot(CallbackInfoReturnable cir) { + Mob thisEntity = (Mob) (Object) this; + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData != null && chatData.status != ChatDataManager.ChatStatus.NONE) { + cir.setReturnValue(true); + } + } + @Inject(method = "addAdditionalSaveData", at = @At("RETURN")) private void creaturechat$saveInventory(CompoundTag tag, CallbackInfo ci) { ListTag listTag = new ListTag(); @@ -237,4 +302,59 @@ private void onItemGiven(Player player, InteractionHand hand, CallbackInfoReturn } } } -} \ No newline at end of file + + private void creaturechat$insertIntoInventory(ItemStack stack) { + if (stack.isEmpty()) { + return; + } + + int size = creaturechat$inventory.getContainerSize(); + int mainHandSlot = creaturechat$getMainHandSlot(size); + int offHandSlot = mainHandSlot + 1; + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (existing.isEmpty()) { + continue; + } + if (!ItemStack.isSameItemSameComponents(existing, stack)) { + continue; + } + int max = Math.min(existing.getMaxStackSize(), creaturechat$inventory.getMaxStackSize()); + int space = max - existing.getCount(); + if (space <= 0) { + continue; + } + int moved = Math.min(space, stack.getCount()); + existing.grow(moved); + stack.shrink(moved); + creaturechat$inventory.setItem(i, existing); + if (stack.isEmpty()) { + return; + } + } + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (!existing.isEmpty()) { + continue; + } + int moved = Math.min(stack.getCount(), Math.min(stack.getMaxStackSize(), creaturechat$inventory.getMaxStackSize())); + creaturechat$inventory.setItem(i, stack.split(moved)); + if (stack.isEmpty()) { + return; + } + } + } + + private static int creaturechat$getMainHandSlot(int size) { + int rows = (size + 4) / 5; + return Math.max(0, rows - 1) * 5; + } +} diff --git a/src/vs/v1_21_6/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/vs/v1_21_6/main/java/com/owlmaddie/mixin/MixinMobEntity.java index d5f0c1b6..3bf19885 100644 --- a/src/vs/v1_21_6/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/vs/v1_21_6/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -13,6 +13,8 @@ import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.core.Vec3i; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.SimpleContainer; @@ -21,6 +23,8 @@ import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.TamableAnimal; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.npc.Villager; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; @@ -31,6 +35,8 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@code MixinMobEntity} mixin class exposes the goalSelector field from the MobEntity class. @@ -38,6 +44,7 @@ @Mixin(Mob.class) public class MixinMobEntity implements ChatInventory, HasCustomInventoryScreen { + private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); private final SimpleContainer creaturechat$inventory = new SimpleContainer(15); @Override @@ -121,6 +128,76 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId } } + @Inject(method = "pickUpItem", at = @At("HEAD"), cancellable = true) + private void creaturechat$pickupFriendItem(ServerLevel level, ItemEntity itemEntity, CallbackInfo ci) { + Mob thisEntity = (Mob) (Object) this; + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData == null || chatData.status == ChatDataManager.ChatStatus.NONE) { + return; + } + + Entity owner = itemEntity.getOwner(); + if (!(owner instanceof Player throwerPlayer)) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("[CreatureChat] pickup ignored: no player owner for {}", thisEntity.getType()); + } + ci.cancel(); + return; + } + + PlayerData playerData = chatData.getPlayerData(throwerPlayer.getDisplayName().getString()); + if (playerData.friendship <= 0) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("[CreatureChat] pickup ignored: {} not friends with {}", throwerPlayer.getDisplayName().getString(), thisEntity.getType()); + } + ci.cancel(); + return; + } + + ItemStack stack = itemEntity.getItem(); + if (stack.isEmpty()) { + ci.cancel(); + return; + } + + ItemStack remaining = stack.copy(); + creaturechat$insertIntoInventory(remaining); + int pickedUp = stack.getCount() - remaining.getCount(); + if (pickedUp > 0) { + thisEntity.onItemPickup(itemEntity); + thisEntity.take(itemEntity, pickedUp); + } + if (remaining.isEmpty()) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("[CreatureChat] pickup success: {} took {}x{}", thisEntity.getType(), stack.getCount(), stack.getItem()); + } + itemEntity.discard(); + } else { + itemEntity.setItem(remaining); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("[CreatureChat] pickup partial: {} stored {}x{}, left {}", + thisEntity.getType(), stack.getCount() - remaining.getCount(), stack.getItem(), remaining.getCount()); + } + } + + ci.cancel(); + } + + @Inject(method = "getPickupReach", at = @At("RETURN"), cancellable = true) + private void creaturechat$expandPickupReach(CallbackInfoReturnable cir) { + Vec3i reach = cir.getReturnValue(); + cir.setReturnValue(new Vec3i(reach.getX() + 1, reach.getY() + 1, reach.getZ() + 1)); + } + + @Inject(method = "canPickUpLoot", at = @At("HEAD"), cancellable = true) + private void creaturechat$allowFriendLoot(CallbackInfoReturnable cir) { + Mob thisEntity = (Mob) (Object) this; + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(thisEntity.getStringUUID()); + if (chatData != null && chatData.status != ChatDataManager.ChatStatus.NONE) { + cir.setReturnValue(true); + } + } + @Inject(method = "addAdditionalSaveData", at = @At("RETURN")) private void creaturechat$saveInventory(ValueOutput tag, CallbackInfo ci) { creaturechat$inventory.storeAsItemList(tag.list("CreatureChatInventory", ItemStack.CODEC)); @@ -217,4 +294,59 @@ private void onItemGiven(Player player, InteractionHand hand, CallbackInfoReturn } } } -} \ No newline at end of file + + private void creaturechat$insertIntoInventory(ItemStack stack) { + if (stack.isEmpty()) { + return; + } + + int size = creaturechat$inventory.getContainerSize(); + int mainHandSlot = creaturechat$getMainHandSlot(size); + int offHandSlot = mainHandSlot + 1; + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (existing.isEmpty()) { + continue; + } + if (!ItemStack.isSameItemSameComponents(existing, stack)) { + continue; + } + int max = Math.min(existing.getMaxStackSize(), creaturechat$inventory.getMaxStackSize()); + int space = max - existing.getCount(); + if (space <= 0) { + continue; + } + int moved = Math.min(space, stack.getCount()); + existing.grow(moved); + stack.shrink(moved); + creaturechat$inventory.setItem(i, existing); + if (stack.isEmpty()) { + return; + } + } + + for (int i = 0; i < size; i++) { + if (i == mainHandSlot || i == offHandSlot) { + continue; + } + ItemStack existing = creaturechat$inventory.getItem(i); + if (!existing.isEmpty()) { + continue; + } + int moved = Math.min(stack.getCount(), Math.min(stack.getMaxStackSize(), creaturechat$inventory.getMaxStackSize())); + creaturechat$inventory.setItem(i, stack.split(moved)); + if (stack.isEmpty()) { + return; + } + } + } + + private static int creaturechat$getMainHandSlot(int size) { + int rows = (size + 4) / 5; + return Math.max(0, rows - 1) * 5; + } +} From ab0af9cf18e02b7bbf283d3d94d1b9e9164486c3 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sun, 4 Jan 2026 15:43:18 -0600 Subject: [PATCH 25/30] Added debounced LLM message when friends pick up dropped items (1 message per 2 seconds of picked up items) --- CHANGELOG.md | 1 + src/main/java/com/owlmaddie/ModInit.java | 2 + .../inventory/PickupMessageBatcher.java | 125 ++++++++++++++++++ .../com/owlmaddie/mixin/MixinMobEntity.java | 2 + .../com/owlmaddie/mixin/MixinMobEntity.java | 2 + .../com/owlmaddie/mixin/MixinMobEntity.java | 5 +- .../com/owlmaddie/mixin/MixinMobEntity.java | 2 + .../com/owlmaddie/mixin/MixinMobEntity.java | 2 + .../com/owlmaddie/mixin/MixinMobEntity.java | 2 + 9 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/owlmaddie/inventory/PickupMessageBatcher.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 9639ee14..9cdb232e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Require full collision blocks when selecting the build start ground - Friendly mobs with chat data now pick up items thrown by friends into their chat inventory - Expanded friendly pickup reach and restored pickup sound for chat-data mobs +- Debounced LLM message when friends pick up dropped items ### Changed - Convert PNG screenshots to JPEG, compress, and remove less useful ones (smaller jar) diff --git a/src/main/java/com/owlmaddie/ModInit.java b/src/main/java/com/owlmaddie/ModInit.java index 252c2976..98ed8231 100644 --- a/src/main/java/com/owlmaddie/ModInit.java +++ b/src/main/java/com/owlmaddie/ModInit.java @@ -8,6 +8,7 @@ import com.owlmaddie.buildrec.BuildRecorder; import com.owlmaddie.goals.EntityBehaviorManager; import com.owlmaddie.inventory.ModMenus; +import com.owlmaddie.inventory.PickupMessageBatcher; import com.owlmaddie.network.ServerPackets; import net.fabricmc.api.ModInitializer; import org.slf4j.Logger; @@ -33,6 +34,7 @@ public void onInitialize() { BuildCommands.register(); BuildRecorder.init(); EntityBehaviorManager.init(); + PickupMessageBatcher.init(); // Register menus and events ModMenus.register(); diff --git a/src/main/java/com/owlmaddie/inventory/PickupMessageBatcher.java b/src/main/java/com/owlmaddie/inventory/PickupMessageBatcher.java new file mode 100644 index 00000000..580a63a7 --- /dev/null +++ b/src/main/java/com/owlmaddie/inventory/PickupMessageBatcher.java @@ -0,0 +1,125 @@ +// SPDX-FileCopyrightText: 2025 owlmaddie LLC +// SPDX-License-Identifier: GPL-3.0-or-later +// Assets CC-BY-NC-SA-4.0; CreatureChat™ trademark © owlmaddie LLC - unauthorized use prohibited +package com.owlmaddie.inventory; + +import com.owlmaddie.chat.ChatDataManager; +import com.owlmaddie.chat.EntityChatData; +import com.owlmaddie.chat.PlayerData; +import com.owlmaddie.network.ServerPackets; +import com.owlmaddie.utils.ServerEntityFinder; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; + +public final class PickupMessageBatcher { + private static final int DEBOUNCE_TICKS = 40; + private static final Map PENDING = new ConcurrentHashMap<>(); + + private PickupMessageBatcher() { + } + + public static void init() { + ServerTickEvents.START_SERVER_TICK.register(PickupMessageBatcher::tick); + } + + public static void recordPickup(Mob mob, Player player, ItemStack stack, int pickedUp) { + if (mob == null || player == null || stack == null || pickedUp <= 0) { + return; + } + if (mob.level().isClientSide()) { + return; + } + if (!(player instanceof ServerPlayer)) { + return; + } + MinecraftServer server = mob.level().getServer(); + if (server == null) { + return; + } + long tick = server.getTickCount(); + Key key = new Key(mob.getUUID(), player.getUUID()); + Pending pending = PENDING.computeIfAbsent(key, k -> new Pending()); + pending.entityId = mob.getUUID(); + pending.playerId = player.getUUID(); + pending.lastTick = tick; + pending.counts.merge(stack.getItem(), pickedUp, Integer::sum); + } + + private static void tick(MinecraftServer server) { + long now = server.getTickCount(); + for (Map.Entry entry : PENDING.entrySet()) { + Pending pending = entry.getValue(); + if (now - pending.lastTick < DEBOUNCE_TICKS) { + continue; + } + sendMessage(server, pending); + PENDING.remove(entry.getKey()); + } + } + + private static void sendMessage(MinecraftServer server, Pending pending) { + ServerPlayer player = server.getPlayerList().getPlayer(pending.playerId); + if (player == null) { + return; + } + Mob mob = findMob(server, pending.entityId); + if (mob == null) { + return; + } + EntityChatData chatData = ChatDataManager.getServerInstance().entityChatDataMap.get(mob.getStringUUID()); + if (chatData == null || chatData.status == ChatDataManager.ChatStatus.NONE) { + return; + } + PlayerData playerData = chatData.getPlayerData(player.getDisplayName().getString()); + if (playerData.friendship <= 0) { + return; + } + String message = "<" + player.getDisplayName().getString() + " gave you " + joinCounts(pending.counts) + ">"; + ServerPackets.generate_chat("N/A", chatData, player, mob, message, true); + } + + private static Mob findMob(MinecraftServer server, UUID entityId) { + for (ServerLevel level : server.getAllLevels()) { + LivingEntity entity = ServerEntityFinder.getEntityByUUID(level, entityId); + if (entity instanceof Mob mob) { + return mob; + } + } + return null; + } + + private static String joinCounts(Map map) { + StringBuilder builder = new StringBuilder(); + boolean first = true; + for (Map.Entry entry : map.entrySet()) { + if (!first) { + builder.append(", "); + } + builder.append(entry.getValue()).append(" ") + .append(new ItemStack(entry.getKey()).getHoverName().getString()); + first = false; + } + return builder.toString(); + } + + private static final class Pending { + private UUID entityId; + private UUID playerId; + private long lastTick; + private final Map counts = new HashMap<>(); + } + + private record Key(UUID entityId, UUID playerId) { + } +} diff --git a/src/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/main/java/com/owlmaddie/mixin/MixinMobEntity.java index 62719270..75c1bd34 100644 --- a/src/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -8,6 +8,7 @@ import com.owlmaddie.chat.PlayerData; import com.owlmaddie.inventory.ChatInventory; import com.owlmaddie.inventory.MobInventoryMenu; +import com.owlmaddie.inventory.PickupMessageBatcher; import com.owlmaddie.network.ServerPackets; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; import net.minecraft.core.Vec3i; @@ -162,6 +163,7 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId if (pickedUp > 0) { thisEntity.onItemPickup(itemEntity); thisEntity.take(itemEntity, pickedUp); + PickupMessageBatcher.recordPickup(thisEntity, throwerPlayer, stack, pickedUp); } if (remaining.isEmpty()) { itemEntity.discard(); diff --git a/src/vs/v1_20_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/vs/v1_20_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java index f1c98030..a058fb38 100644 --- a/src/vs/v1_20_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/vs/v1_20_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -8,6 +8,7 @@ import com.owlmaddie.chat.PlayerData; import com.owlmaddie.inventory.ChatInventory; import com.owlmaddie.inventory.MobInventoryMenu; +import com.owlmaddie.inventory.PickupMessageBatcher; import com.owlmaddie.network.ServerPackets; import net.minecraft.world.entity.HasCustomInventoryScreen; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; @@ -161,6 +162,7 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId if (pickedUp > 0) { thisEntity.onItemPickup(itemEntity); thisEntity.take(itemEntity, pickedUp); + PickupMessageBatcher.recordPickup(thisEntity, throwerPlayer, stack, pickedUp); } if (remaining.isEmpty()) { itemEntity.discard(); diff --git a/src/vs/v1_21_0/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/vs/v1_21_0/main/java/com/owlmaddie/mixin/MixinMobEntity.java index a75d4148..07129784 100644 --- a/src/vs/v1_21_0/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/vs/v1_21_0/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -8,12 +8,12 @@ import com.owlmaddie.chat.PlayerData; import com.owlmaddie.inventory.ChatInventory; import com.owlmaddie.inventory.MobInventoryMenu; +import com.owlmaddie.inventory.PickupMessageBatcher; import com.owlmaddie.network.ServerPackets; import net.minecraft.world.entity.HasCustomInventoryScreen; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; import net.minecraft.core.Vec3i; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -127,7 +127,7 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId } @Inject(method = "pickUpItem", at = @At("HEAD"), cancellable = true) - private void creaturechat$pickupFriendItem(ServerLevel level, ItemEntity itemEntity, CallbackInfo ci) { + private void creaturechat$pickupFriendItem(ItemEntity itemEntity, CallbackInfo ci) { Mob thisEntity = (Mob) (Object) this; if (thisEntity.level().isClientSide()) { return; @@ -162,6 +162,7 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId if (pickedUp > 0) { thisEntity.onItemPickup(itemEntity); thisEntity.take(itemEntity, pickedUp); + PickupMessageBatcher.recordPickup(thisEntity, throwerPlayer, stack, pickedUp); } if (remaining.isEmpty()) { itemEntity.discard(); diff --git a/src/vs/v1_21_2/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/vs/v1_21_2/main/java/com/owlmaddie/mixin/MixinMobEntity.java index a75d4148..195ebab5 100644 --- a/src/vs/v1_21_2/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/vs/v1_21_2/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -8,6 +8,7 @@ import com.owlmaddie.chat.PlayerData; import com.owlmaddie.inventory.ChatInventory; import com.owlmaddie.inventory.MobInventoryMenu; +import com.owlmaddie.inventory.PickupMessageBatcher; import com.owlmaddie.network.ServerPackets; import net.minecraft.world.entity.HasCustomInventoryScreen; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; @@ -162,6 +163,7 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId if (pickedUp > 0) { thisEntity.onItemPickup(itemEntity); thisEntity.take(itemEntity, pickedUp); + PickupMessageBatcher.recordPickup(thisEntity, throwerPlayer, stack, pickedUp); } if (remaining.isEmpty()) { itemEntity.discard(); diff --git a/src/vs/v1_21_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/vs/v1_21_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java index f0a3846c..306400fe 100644 --- a/src/vs/v1_21_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/vs/v1_21_5/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -8,6 +8,7 @@ import com.owlmaddie.chat.PlayerData; import com.owlmaddie.inventory.ChatInventory; import com.owlmaddie.inventory.MobInventoryMenu; +import com.owlmaddie.inventory.PickupMessageBatcher; import com.owlmaddie.network.ServerPackets; import net.minecraft.world.entity.HasCustomInventoryScreen; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; @@ -162,6 +163,7 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId if (pickedUp > 0) { thisEntity.onItemPickup(itemEntity); thisEntity.take(itemEntity, pickedUp); + PickupMessageBatcher.recordPickup(thisEntity, throwerPlayer, stack, pickedUp); } if (remaining.isEmpty()) { itemEntity.discard(); diff --git a/src/vs/v1_21_6/main/java/com/owlmaddie/mixin/MixinMobEntity.java b/src/vs/v1_21_6/main/java/com/owlmaddie/mixin/MixinMobEntity.java index 3bf19885..35f80b62 100644 --- a/src/vs/v1_21_6/main/java/com/owlmaddie/mixin/MixinMobEntity.java +++ b/src/vs/v1_21_6/main/java/com/owlmaddie/mixin/MixinMobEntity.java @@ -8,6 +8,7 @@ import com.owlmaddie.chat.PlayerData; import com.owlmaddie.inventory.ChatInventory; import com.owlmaddie.inventory.MobInventoryMenu; +import com.owlmaddie.inventory.PickupMessageBatcher; import com.owlmaddie.network.ServerPackets; import net.minecraft.world.entity.HasCustomInventoryScreen; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; @@ -166,6 +167,7 @@ public net.minecraft.world.inventory.AbstractContainerMenu createMenu(int syncId if (pickedUp > 0) { thisEntity.onItemPickup(itemEntity); thisEntity.take(itemEntity, pickedUp); + PickupMessageBatcher.recordPickup(thisEntity, throwerPlayer, stack, pickedUp); } if (remaining.isEmpty()) { if (LOGGER.isDebugEnabled()) { From 98847556a3acb719b2eff9d4179a0e97f7d4d37c Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Mon, 5 Jan 2026 23:30:41 -0600 Subject: [PATCH 26/30] Lots of improvements to PlayerBuildGoal - to pause the goal and wander when out of ingredients or land not cleared, and return to build when checks are valid. --- .../com/owlmaddie/buildrec/BuildRecorder.java | 95 ++++ .../com/owlmaddie/goals/BuildPlayerGoal.java | 526 +++++++++++++++--- 2 files changed, 554 insertions(+), 67 deletions(-) diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 29845cd3..03d0d202 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -71,6 +71,7 @@ public class BuildRecorder { private static final int MAX_IDLE_TICKS = 20; // 1 second private static List BUILD_INDEX; private static final Map> MISSING_RECIPES = new ConcurrentHashMap<>(); + private static final Map BOUNDS_CACHE = new ConcurrentHashMap<>(); static { UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> { @@ -166,6 +167,11 @@ public static String randomBuildFile(double entityHeight, String type, int level return path; } + public static ReplayBounds getReplayBounds(String fileName) { + String actual = fileName.endsWith(".json.gz") ? fileName : fileName + ".json.gz"; + return BOUNDS_CACHE.computeIfAbsent(actual, BuildRecorder::loadReplayBounds); + } + public static boolean start(ServerPlayer player, String type, String height, String name) { if (RECORDINGS.containsKey(player.getUUID())) { LOGGER.info("[BuildRec] start ignored already recording player={}", player.getGameProfile().getName()); @@ -276,6 +282,26 @@ private static boolean startReplayInternal(ServerPlayer player, String fileName, return false; } + private static ReplayBounds loadReplayBounds(String fileName) { + Path file = buildRootDir().resolve(fileName); + if (!Files.exists(file)) { + LOGGER.info("[BuildRec] bounds missing file={}", file); + return null; + } + try { + BuildRecordIO.Loaded loaded = BuildRecordIO.read(file); + ReplayBounds bounds = ReplayBounds.fromActions(loaded.actions); + if (bounds == null) { + LOGGER.info("[BuildRec] bounds empty file={}", fileName); + } + return bounds; + } catch (IOException | JsonParseException e) { + LOGGER.error("[BuildRec] bounds failed to load {}", file, e); + return null; + } + } + + public static void pauseReplay(Mob actor) { REPLAYS.stream().filter(r -> r.actor == actor).forEach(r -> { r.paused = true; @@ -286,6 +312,14 @@ public static void pauseReplay(Mob actor) { public static void resumeReplay(Mob actor) { REPLAYS.stream().filter(r -> r.actor == actor).forEach(r -> { r.paused = false; + if (r.action != null) { + r.sx = r.actor.getX(); + r.sy = r.actor.getY(); + r.sz = r.actor.getZ(); + r.syaw = r.actor.getYRot(); + r.spitch = r.actor.getXRot(); + r.progress = 0; + } r.actor.setNoAi(true); MISSING_RECIPES.remove(actor); }); @@ -855,4 +889,65 @@ private static class Replay { } } + public static class ReplayBounds { + public final int minX; + public final int minY; + public final int minZ; + public final int maxX; + public final int maxY; + public final int maxZ; + + public final int sizeX; + public final int sizeY; + public final int sizeZ; + + public ReplayBounds(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { + this.minX = minX; + this.minY = minY; + this.minZ = minZ; + this.maxX = maxX; + this.maxY = maxY; + this.maxZ = maxZ; + this.sizeX = maxX - minX + 1; + this.sizeY = maxY - minY + 1; + this.sizeZ = maxZ - minZ + 1; + } + + private static ReplayBounds fromActions(List actions) { + boolean hasBounds = false; + int minX = 0, minY = 0, minZ = 0; + int maxX = 0, maxY = 0, maxZ = 0; + for (Action a : actions) { + int px = Mth.floor(a.px); + int py = Mth.floor(a.py); + int pz = Mth.floor(a.pz); + if (!hasBounds) { + minX = maxX = px; + minY = maxY = py; + minZ = maxZ = pz; + hasBounds = true; + } else { + if (px < minX) minX = px; + if (px > maxX) maxX = px; + if (py < minY) minY = py; + if (py > maxY) maxY = py; + if (pz < minZ) minZ = pz; + if (pz > maxZ) maxZ = pz; + } + if ("place".equals(a.action) || "break".equals(a.action) || "interact".equals(a.action)) { + int bx = a.bx; + int by = a.by; + int bz = a.bz; + if (bx < minX) minX = bx; + if (bx > maxX) maxX = bx; + if (by < minY) minY = by; + if (by > maxY) maxY = by; + if (bz < minZ) minZ = bz; + if (bz > maxZ) maxZ = bz; + } + } + return hasBounds ? new ReplayBounds(minX, minY, minZ, maxX, maxY, maxZ) : null; + } + } + } diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index c607c399..793243fc 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -15,9 +15,16 @@ import net.minecraft.ChatFormatting; import net.minecraft.util.Mth; import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.PathfinderMob; import net.minecraft.world.level.Level; import net.minecraft.world.level.pathfinder.Path; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.SignBlock; +import net.minecraft.world.level.block.StandingSignBlock; +import net.minecraft.world.level.block.entity.SignBlockEntity; +import net.minecraft.world.level.block.entity.SignText; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +37,8 @@ * replay as the goal starts and stops. */ public class BuildPlayerGoal extends PlayerBaseGoal { - private static final double PLAYER_MESSAGE_DIST_SQR = 9.0; + private static final double PLAYER_MESSAGE_DIST = 4.0; + private static final double PLAYER_MESSAGE_DIST_SQR = PLAYER_MESSAGE_DIST * PLAYER_MESSAGE_DIST; private static final double START_CLOSE_DIST_SQR = 0.25; private final Mob entity; private final String buildType; @@ -51,6 +59,29 @@ public class BuildPlayerGoal extends PlayerBaseGoal { private boolean loggedMissingCursor = false; private int aiCheckTicks = 0; private boolean aiPause = false; + private String buildFile; + private BuildRecorder.ReplayBounds buildBounds; + private BuildRecorder.ReplayBounds effectiveBounds; + private boolean waitingForClearSpace = false; + private boolean sentClearSpaceMessage = false; + private boolean returningToBuildPos = false; + private BlockPos clearAreaSignPos; + private boolean buildPosLocked = false; + private BlockPos resumePos; + private boolean waitingForResumePos = false; + private boolean resumePosPaused = false; + private boolean waitingForMaterials = false; + private long nextMaterialCheckTick = 0; + private String pendingMessage; + private boolean pendingMessageSent = false; + private long messagePauseUntilTick = 0; + private boolean completeAfterMessage = false; + private long clearSpaceWaitUntilTick = 0; + private long nextClearSpaceCheckTick = 0; + private static final double BUILD_WAIT_RETURN_DIST_SQR = 9.0; + private static final double BUILD_WAIT_MAX_DIST_SQR = 36.0; + private static final int CLEAR_SPACE_PAUSE_TICKS = 60; + private static final int CLEAR_SPACE_CHECK_INTERVAL_TICKS = 100; private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); public BuildPlayerGoal(ServerPlayer player, Mob entity, double speed, String buildType) { @@ -75,15 +106,18 @@ public boolean canContinueToUse() { public void start() { if (!startedReplay) { LOGGER.info("[BuildGoal] start navigation toward player"); - reachedPlayer = false; - buildPos = null; - moveTowardPlayer(false); + if (waitingForClearSpace && buildPos != null) { + moveTowardBuildPos(false); + } else if (!buildPosLocked) { + reachedPlayer = false; + buildPos = null; + moveTowardPlayer(false); + } } else { BlockPos cursor = BuildRecorder.getReplayCursor(this.entity); if (cursor != null) { - buildPos = cursor; - LOGGER.info("[BuildGoal] resume navigation toward replay cursor {}", buildPos); - this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); + LOGGER.info("[BuildGoal] resume navigation toward replay cursor {}", cursor); + this.entity.getNavigation().moveTo(cursor.getX(), cursor.getY() + 1, cursor.getZ(), this.speed); } } } @@ -93,6 +127,7 @@ public void stop() { LOGGER.info("[BuildGoal] stop goal pause replay"); if (completed || this.targetEntity == null || !this.targetEntity.isAlive()) { BuildRecorder.cancelReplay(this.entity); + removeClearAreaSign(); } else { BuildRecorder.pauseReplay(this.entity); } @@ -101,12 +136,48 @@ public void stop() { @Override public void tick() { if (completed) return; + if (buildPos != null && !buildPosLocked) { + buildPosLocked = true; + } + + if (pendingMessage != null) { + handlePendingMessage(); + return; + } if (!startedReplay) { + if (waitingForClearSpace && buildPos != null && buildBounds != null) { + long now = this.entity.level().getGameTime(); + if (now < clearSpaceWaitUntilTick) { + this.setFlags(EnumSet.noneOf(Flag.class)); + return; + } + if (now < nextClearSpaceCheckTick) { + moveTowardBuildPos(false); + return; + } + nextClearSpaceCheckTick = now + CLEAR_SPACE_CHECK_INTERVAL_TICKS; + if (effectiveBounds != null && !isFloorClearWithLog(buildPos, effectiveBounds)) { + moveTowardBuildPos(false); + return; + } + double distToBuild = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); + if (distToBuild > BUILD_WAIT_RETURN_DIST_SQR) { + moveTowardBuildPos(true); + return; + } + waitingForClearSpace = false; + sentClearSpaceMessage = false; + returningToBuildPos = false; + this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + removeClearAreaSign(); + } if (!reachedPlayer) { if (isStartCloseToPlayer()) { - buildPos = findStartPos(BlockPos.containing(this.targetEntity.position())); + BlockPos playerPos = BlockPos.containing(this.targetEntity.position()); + buildPos = findPreferredStartPos(playerPos); reachedPlayer = true; + buildPosLocked = true; LOGGER.info("[BuildGoal] reached player choose buildPos {}", buildPos); } else { double distToPlayer = this.entity.distanceToSqr(this.targetEntity); @@ -115,32 +186,79 @@ public void tick() { } } - double distToPlayer = this.entity.distanceToSqr(this.targetEntity); - moveTowardPlayer(distToPlayer <= 4.0); - if (isStartCloseToPlayer()) { - this.entity.getNavigation().stop(); - EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); - int tier = this.entity.getBbHeight() < 1 ? 1 : (this.entity.getBbHeight() < 2 ? 2 : 3); - String file = BuildRecorder.randomBuildFile(this.entity.getBbHeight(), buildType, data.buildLevel); - LOGGER.info("[BuildGoal] select build skill={} type={} heightTier={} file={}", data.buildLevel, buildType, tier, file); - if (file != null && BuildRecorder.startReplay((ServerPlayer) this.targetEntity, this.entity, file, 1)) { - startedReplay = true; - actualType = (buildType == null || buildType.isEmpty() || "unknown".equalsIgnoreCase(buildType)) ? file.split("/")[0] : buildType; - LOGGER.info("[BuildGoal] started replay type={} at {}", actualType, buildPos); - } else if (this.targetEntity instanceof ServerPlayer player) { - String prompt = (buildType == null || buildType.isEmpty()) - ? "Explain to the player that you don't know how to build that." - : "Explain to the player that you don't know how to build a " + buildType + "."; - ServerPackets.generate_chat("N/A", data, player, this.entity, prompt, true); - completed = true; - LOGGER.info("[BuildGoal] failed to start replay type={}", buildType); + this.entity.getNavigation().stop(); + EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); + int tier = this.entity.getBbHeight() < 1 ? 1 : (this.entity.getBbHeight() < 2 ? 2 : 3); + if (buildFile == null) { + buildFile = BuildRecorder.randomBuildFile(this.entity.getBbHeight(), buildType, data.buildLevel); + buildBounds = buildFile != null ? BuildRecorder.getReplayBounds(buildFile) : null; + LOGGER.info("[BuildGoal] select build skill={} type={} heightTier={} file={}", data.buildLevel, buildType, tier, buildFile); + effectiveBounds = buildBounds != null ? expandBoundsForEntity(buildBounds) : null; + if (effectiveBounds != null) { + LOGGER.info("[BuildGoal] replay bounds size={}x{}x{} startOffsetMin=({}, {}, {}) startOffsetMax=({}, {}, {})", + effectiveBounds.sizeX, effectiveBounds.sizeY, effectiveBounds.sizeZ, + effectiveBounds.minX, effectiveBounds.minY, effectiveBounds.minZ, + effectiveBounds.maxX, effectiveBounds.maxY, effectiveBounds.maxZ); } } + if (buildFile != null && effectiveBounds != null && !isFloorClear(buildPos, effectiveBounds)) { + waitingForClearSpace = true; + moveTowardBuildPos(false); + updateClearAreaSign(buildPos, effectiveBounds); + if (!sentClearSpaceMessage) { + String msg = "In your reply, ask the player to clear a flat " + effectiveBounds.sizeX + "x" + effectiveBounds.sizeZ + " area so you can build safely, and confirm you'll start once it's ready."; + if (queueMessage(msg)) { + sentClearSpaceMessage = true; + clearSpaceWaitUntilTick = this.entity.level().getGameTime() + CLEAR_SPACE_PAUSE_TICKS; + nextClearSpaceCheckTick = clearSpaceWaitUntilTick + CLEAR_SPACE_CHECK_INTERVAL_TICKS; + } + } + return; + } + sentClearSpaceMessage = false; + if (buildFile != null && buildPos != null) { + removeClearAreaSign(); + } + if (buildFile != null && BuildRecorder.startReplay((ServerPlayer) this.targetEntity, this.entity, buildFile, 1)) { + startedReplay = true; + actualType = (buildType == null || buildType.isEmpty() || "unknown".equalsIgnoreCase(buildType)) ? buildFile.split("/")[0] : buildType; + LOGGER.info("[BuildGoal] started replay type={} at {}", actualType, buildPos); + } else { + String prompt = (buildType == null || buildType.isEmpty()) + ? "Explain to the player that you don't know how to build that." + : "Explain to the player that you don't know how to build a " + buildType + "."; + if (queueMessage(prompt)) { + completeAfterMessage = true; + } + LOGGER.info("[BuildGoal] failed to start replay type={}", buildType); + } return; } - if (BuildRecorder.isReplaying(this.entity)) { - if (!fetchingMaterials) { + if (BuildRecorder.isReplaying(this.entity)) { + if (waitingForMaterials) { + long now = this.entity.level().getGameTime(); + if (now < nextMaterialCheckTick) { + moveTowardBuildPos(false); + return; + } + nextMaterialCheckTick = now + CLEAR_SPACE_CHECK_INTERVAL_TICKS; + if (BuildRecorder.getMissingRecipe(this.entity) != null) { + moveTowardBuildPos(false); + return; + } + waitingForMaterials = false; + resumePosPaused = false; + if (resumePos == null) { + resumePos = buildPos; + } + waitingForResumePos = true; + moveTowardResumePos(); + return; + } + if (waitingForResumePos) { + moveTowardResumePos(); + } else if (!fetchingMaterials) { if (aiPause) { aiPause = false; BuildRecorder.resumeReplay(this.entity); @@ -156,37 +274,35 @@ public void tick() { if (recipe != null) { if (!fetchingMaterials) { materialWaitTicks = 0; + resumePos = BuildRecorder.getReplayCursor(this.entity); + if (resumePos == null) { + resumePos = buildPos; + } + waitingForResumePos = true; + resumePosPaused = false; + waitingForMaterials = true; + nextMaterialCheckTick = this.entity.level().getGameTime() + CLEAR_SPACE_CHECK_INTERVAL_TICKS; } fetchingMaterials = true; - double distToPlayer = this.entity.distanceToSqr(this.targetEntity); - if (distToPlayer > PLAYER_MESSAGE_DIST_SQR && !controlsReleased) { - this.entity.getNavigation().moveTo(this.targetEntity, this.speed); - } else { - if (!controlsReleased) { - this.entity.getNavigation().stop(); - } - if (this.targetEntity instanceof ServerPlayer player) { - LookControls.lookAtPlayer(player, this.entity); - if (!sentRecipe) { - EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); - String nextItem = BuildRecorder.getNextMissingItem(this.entity); - if (nextItem == null) nextItem = "unknown"; - LOGGER.info("[BuildGoal] next missing item={} remaining={}", nextItem, BuildRecorder.recipeToString(recipe)); - String limited = BuildRecorder.recipeToString(recipe, 2); - String msg = "Next item needed: " + nextItem.replace('_', ' ') + ". Build paused - missing inventory items: " + limited + ". In your reply, ask the player for these items and confirm you'll continue building once they arrive."; - ServerPackets.generate_chat("N/A", data, player, this.entity, msg, true); - if (this.entity.level() instanceof ServerLevel level) { - String broadcast = BuildRecorder.recipeToDisplayString(recipe, 2); - Component text = Component.literal(broadcast).withStyle(ChatFormatting.WHITE); - for (ServerPlayer p : level.players()) { - if (p.distanceToSqr(this.entity) <= 1024) { - p.displayClientMessage(text, false); - } + if (!sentRecipe) { + String nextItem = BuildRecorder.getNextMissingItem(this.entity); + if (nextItem == null) nextItem = "unknown"; + LOGGER.info("[BuildGoal] next missing item={} remaining={}", nextItem, BuildRecorder.recipeToString(recipe)); + String limited = BuildRecorder.recipeToString(recipe, 2); + String msg = "Next item needed: " + nextItem.replace('_', ' ') + ". Build paused - missing inventory items: " + limited + ". In your reply, ask the player for these items and confirm you'll continue building once they arrive."; + if (queueMessage(msg)) { + if (this.entity.level() instanceof ServerLevel level) { + String broadcast = BuildRecorder.recipeToDisplayString(recipe, 2); + Component text = Component.literal(broadcast).withStyle(ChatFormatting.WHITE); + for (ServerPlayer p : level.players()) { + if (p.distanceToSqr(this.entity) <= 1024) { + p.displayClientMessage(text, false); } } - sentRecipe = true; } + sentRecipe = true; } + return; } if (!controlsReleased) { if (materialWaitTicks++ >= 80) { @@ -212,15 +328,20 @@ public void tick() { sentStuckMessage = false; aiCheckTicks = 0; aiPause = false; + resumePosPaused = false; + waitingForMaterials = false; this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); - if (buildPos != null) { - this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); + if (resumePos == null) { + resumePos = buildPos; + } + if (resumePos != null) { + waitingForResumePos = true; + moveTowardResumePos(); } } BlockPos cursor = BuildRecorder.getReplayCursor(this.entity); if (cursor != null) { loggedMissingCursor = false; - buildPos = cursor; double dist = this.entity.distanceToSqr(cursor.getX() + 0.5, cursor.getY() + 1, cursor.getZ() + 0.5); if (dist > 4.0) { LOGGER.info("[BuildGoal] pause replay move to cursor {} (dist={})", cursor, dist); @@ -232,11 +353,11 @@ public void tick() { stuckTicks = 0; } else if (++stuckTicks > 80) { stuckTicks = 0; - if (++rerouteAttempts >= 3 && !sentStuckMessage && this.targetEntity instanceof ServerPlayer player) { - EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); + if (++rerouteAttempts >= 3 && !sentStuckMessage) { String msg = "I can't find where I left off in the build. Please help me get back on track."; - ServerPackets.generate_chat("N/A", data, player, this.entity, msg, true); - sentStuckMessage = true; + if (queueMessage(msg)) { + sentStuckMessage = true; + } } } } else { @@ -245,8 +366,10 @@ public void tick() { } else { stuckTicks = 0; rerouteAttempts = 0; - LOGGER.info("[BuildGoal] resume replay at cursor {} (dist={})", cursor, dist); - BuildRecorder.resumeReplay(this.entity); + if (!fetchingMaterials && !waitingForResumePos) { + LOGGER.info("[BuildGoal] resume replay at cursor {} (dist={})", cursor, dist); + BuildRecorder.resumeReplay(this.entity); + } } } else if (!loggedMissingCursor) { LOGGER.info("[BuildGoal] waiting for replay cursor"); @@ -255,7 +378,7 @@ public void tick() { } else if (!finishing && this.targetEntity instanceof ServerPlayer player) { finishing = true; LOGGER.info("[BuildGoal] replay finished returning to player"); - this.entity.getNavigation().moveTo(player, this.speed); + moveTowardPlayerStopDistance(PLAYER_MESSAGE_DIST); } else if (finishing && this.targetEntity instanceof ServerPlayer player) { LookControls.lookAtPlayer(player, this.entity); if (this.entity.distanceToSqr(player) <= PLAYER_MESSAGE_DIST_SQR) { @@ -263,13 +386,14 @@ public void tick() { EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); String type = (actualType == null || actualType.isEmpty()) ? "structure" : actualType; String msg = ""; - ServerPackets.generate_chat("N/A", data, player, this.entity, msg, true); + if (queueMessage(msg)) { + completeAfterMessage = true; + } data.buildLevel = Math.min(5, data.buildLevel + 1); ServerPackets.BroadcastEntityMessage(data); - completed = true; LOGGER.info("[BuildGoal] completion message sent"); } else { - this.entity.getNavigation().moveTo(player, this.speed); + moveTowardPlayerStopDistance(PLAYER_MESSAGE_DIST); } } } @@ -298,6 +422,15 @@ private BlockPos findStartPos(BlockPos target) { return best != null ? best : ground; } + private BlockPos findPreferredStartPos(BlockPos target) { + BlockPos ground = findGround(target); + if (isValidBuildPos(ground) && + this.entity.getNavigation().createPath(ground.getX(), ground.getY() + 1, ground.getZ(), 1) != null) { + return ground; + } + return findStartPos(target); + } + private BlockPos findGround(BlockPos pos) { Level level = this.entity.level(); BlockPos ground = pos; @@ -331,6 +464,147 @@ private boolean isValidBuildPos(BlockPos ground) { return true; } + private boolean isFloorClear(BlockPos ground, BuildRecorder.ReplayBounds bounds) { + if (ground == null || bounds == null) { + return true; + } + int baseX = ground.getX(); + int baseZ = ground.getZ(); + int y = ground.getY(); + Level level = this.entity.level(); + for (int x = bounds.minX; x <= bounds.maxX; x++) { + for (int z = bounds.minZ; z <= bounds.maxZ; z++) { + BlockPos pos = new BlockPos(baseX + x, y, baseZ + z); + BlockPos above = pos.above(); + if (clearAreaSignPos != null && clearAreaSignPos.equals(above)) { + if (!isSolidGround(level, pos)) { + return false; + } + continue; + } + if (!isSolidGround(level, pos) || isSolidGround(level, above)) { + return false; + } + } + } + return true; + } + + private boolean isFloorClearWithLog(BlockPos ground, BuildRecorder.ReplayBounds bounds) { + if (ground == null || bounds == null) { + return true; + } + int total = 0; + int clear = 0; + int baseX = ground.getX(); + int baseZ = ground.getZ(); + int y = ground.getY(); + Level level = this.entity.level(); + for (int x = bounds.minX; x <= bounds.maxX; x++) { + for (int z = bounds.minZ; z <= bounds.maxZ; z++) { + total++; + BlockPos pos = new BlockPos(baseX + x, y, baseZ + z); + BlockPos above = pos.above(); + boolean ok; + if (clearAreaSignPos != null && clearAreaSignPos.equals(above)) { + ok = isSolidGround(level, pos); + } else { + ok = isSolidGround(level, pos) && !isSolidGround(level, above); + } + if (ok) { + clear++; + } + } + } + int pct = total == 0 ? 0 : (int) Math.round((clear * 100.0) / total); + LOGGER.info("[BuildGoal] ground clearance {}% ({}/{}) at {}", pct, clear, total, ground); + return clear == total; + } + + private BlockPos findNearbyClearStart(BlockPos center, BuildRecorder.ReplayBounds bounds) { + if (center == null || bounds == null) { + return null; + } + BlockPos ground = findGround(center); + if (isValidBuildPos(ground) && isFloorClear(ground, bounds)) { + return ground; + } + BlockPos best = null; + for (BlockPos pos : BlockPos.betweenClosed(center.offset(-1, 0, -1), center.offset(1, 0, 1))) { + if (pos.equals(center)) { + continue; + } + BlockPos candidate = findGround(pos); + if (!isValidBuildPos(candidate)) { + continue; + } + if (this.entity.getNavigation().createPath(candidate.getX(), candidate.getY() + 1, candidate.getZ(), 1) == null) { + continue; + } + if (isFloorClear(candidate, bounds)) { + best = candidate.immutable(); + break; + } + } + return best; + } + + private void updateClearAreaSign(BlockPos ground, BuildRecorder.ReplayBounds bounds) { + if (ground == null || bounds == null || this.entity.level().isClientSide) { + return; + } + BlockPos signPos = ground.above(); + if (clearAreaSignPos != null && !clearAreaSignPos.equals(signPos)) { + removeClearAreaSign(); + } + Level level = this.entity.level(); + if (clearAreaSignPos == null) { + if (!level.getBlockState(signPos).isAir()) { + return; + } + level.setBlock(signPos, Blocks.OAK_SIGN.defaultBlockState().setValue(StandingSignBlock.ROTATION, 0), 3); + clearAreaSignPos = signPos; + } + if (level.getBlockEntity(signPos) instanceof SignBlockEntity sign) { + SignText text = sign.getFrontText(); + text = text.setMessage(0, Component.literal("Clear Ground")); + text = text.setMessage(1, Component.literal(bounds.sizeX + "x" + bounds.sizeZ)); + sign.setText(text, true); + sign.setChanged(); + level.sendBlockUpdated(signPos, level.getBlockState(signPos), level.getBlockState(signPos), 3); + } + } + + private void removeClearAreaSign() { + if (clearAreaSignPos == null || this.entity.level().isClientSide) { + return; + } + Level level = this.entity.level(); + if (level.getBlockState(clearAreaSignPos).getBlock() instanceof SignBlock) { + level.removeBlock(clearAreaSignPos, false); + } + clearAreaSignPos = null; + } + + private BuildRecorder.ReplayBounds expandBoundsForEntity(BuildRecorder.ReplayBounds bounds) { + if (bounds == null) { + return null; + } + int expandXZ = Mth.ceil(this.entity.getBbWidth() / 2.0); + int expandY = Math.max(0, Mth.ceil(this.entity.getBbHeight()) - 1); + if (expandXZ == 0 && expandY == 0) { + return bounds; + } + return new BuildRecorder.ReplayBounds( + bounds.minX - expandXZ, + bounds.minY - expandY, + bounds.minZ - expandXZ, + bounds.maxX + expandXZ, + bounds.maxY + expandY, + bounds.maxZ + expandXZ + ); + } + private boolean isStartCloseToPlayer() { if (this.targetEntity == null) { return false; @@ -367,4 +641,122 @@ private void moveTowardPlayer(boolean forceClose) { ); } } + + private boolean queueMessage(String msg) { + if (pendingMessage != null || msg == null) { + return false; + } + pendingMessage = msg; + pendingMessageSent = false; + messagePauseUntilTick = 0; + return true; + } + + private void handlePendingMessage() { + long now = this.entity.level().getGameTime(); + if (now < messagePauseUntilTick) { + this.entity.getNavigation().stop(); + this.setFlags(EnumSet.noneOf(Flag.class)); + return; + } + if (!pendingMessageSent) { + if (this.targetEntity == null) { + pendingMessage = null; + return; + } + double distToPlayer = this.entity.distanceToSqr(this.targetEntity); + if (distToPlayer > PLAYER_MESSAGE_DIST_SQR) { + moveTowardPlayerStopDistance(PLAYER_MESSAGE_DIST); + return; + } + if (this.targetEntity instanceof ServerPlayer player) { + EntityChatData data = ChatDataManager.getServerInstance().getOrCreateChatData(this.entity.getStringUUID()); + LookControls.lookAtPlayer(player, this.entity); + ServerPackets.generate_chat("N/A", data, player, this.entity, pendingMessage, true); + } + pendingMessageSent = true; + messagePauseUntilTick = now + CLEAR_SPACE_PAUSE_TICKS; + return; + } + pendingMessage = null; + pendingMessageSent = false; + if (completeAfterMessage) { + completeAfterMessage = false; + completed = true; + } + } + + private void moveTowardBuildPos(boolean forceReturn) { + if (buildPos == null) { + return; + } + double dist = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); + boolean shouldReturn = forceReturn || dist > BUILD_WAIT_MAX_DIST_SQR || returningToBuildPos; + if (shouldReturn && dist > BUILD_WAIT_RETURN_DIST_SQR) { + returningToBuildPos = true; + this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + moveTowardTarget(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); + return; + } + if (returningToBuildPos) { + this.entity.getNavigation().stop(); + } + returningToBuildPos = false; + this.setFlags(EnumSet.noneOf(Flag.class)); + } + + private void moveTowardPlayerStopDistance(double stopDistance) { + if (this.targetEntity == null) { + return; + } + Vec3 playerPos = this.targetEntity.position(); + Vec3 entityPos = this.entity.position(); + Vec3 offset = entityPos.subtract(playerPos); + double len = offset.length(); + if (len < 0.001) { + this.entity.getNavigation().moveTo(this.targetEntity, this.speed); + return; + } + Vec3 targetPos = playerPos.add(offset.scale(stopDistance / len)); + moveTowardTarget(targetPos.x, targetPos.y, targetPos.z); + } + + private void moveTowardResumePos() { + if (resumePos == null) { + waitingForResumePos = false; + return; + } + if (!resumePosPaused) { + BuildRecorder.pauseReplay(this.entity); + resumePosPaused = true; + } + double dist = this.entity.distanceToSqr(resumePos.getX() + 0.5, resumePos.getY() + 1, resumePos.getZ() + 0.5); + if (dist > 4.0) { + moveTowardTarget(resumePos.getX() + 0.5, resumePos.getY() + 1, resumePos.getZ() + 0.5); + return; + } + waitingForResumePos = false; + resumePos = null; + this.entity.getNavigation().stop(); + if (!fetchingMaterials) { + BuildRecorder.resumeReplay(this.entity); + } + } + + private void moveTowardTarget(double x, double y, double z) { + if (this.entity instanceof PathfinderMob) { + this.entity.getNavigation().moveTo(x, y, z, this.speed); + return; + } + LookControls.lookAtPosition(new Vec3(x, y, z), this.entity); + Vec3 entityPos = this.entity.position(); + Vec3 moveDirection = new Vec3(x, y, z).subtract(entityPos).normalize(); + double currentSpeed = this.entity.getDeltaMovement().horizontalDistance(); + currentSpeed = Mth.approach((float) currentSpeed, (float) this.speed, + (float) (0.005 * (this.speed / Math.max(currentSpeed, 0.1)))); + Vec3 newVelocity = new Vec3(moveDirection.x * currentSpeed, moveDirection.y * currentSpeed, + moveDirection.z * currentSpeed); + this.entity.setDeltaMovement(newVelocity); + this.entity.hurtMarked = true; + } } From b9f2694bf0f9f8fe960b34837f6a90c426c36ce0 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 10 Jan 2026 22:09:30 -0600 Subject: [PATCH 27/30] - Build replay command logs replay bounds with and without player movement - Build index now rebuilds at runtime from bundled and local builds - Build goal bounds now ignore player movement --- CHANGELOG.md | 3 + .../com/owlmaddie/buildrec/BuildRecorder.java | 258 ++++++++++++++---- .../com/owlmaddie/commands/BuildCommands.java | 32 ++- .../com/owlmaddie/goals/BuildPlayerGoal.java | 2 +- .../builds/campfire/test2026.json.gz | Bin 0 -> 10668 bytes .../builds/castle/1/castle_1.json.gz | Bin 0 -> 10718 bytes .../builds/castle/2/castle_2.json.gz | Bin 0 -> 14357 bytes .../creaturechat/builds/farm/farm_1.json.gz | Bin 0 -> 5680 bytes .../creaturechat/builds/farm/farm_2.json.gz | Bin 0 -> 3820 bytes .../creaturechat/builds/farm/farm_3.json.gz | Bin 0 -> 4667 bytes .../creaturechat/builds/farm/farm_4.json.gz | Bin 0 -> 12342 bytes .../builds/house/1/ghast_test_1.json.gz | Bin 0 -> 51441 bytes .../builds/house/2/ghast_test_2.json.gz | Bin 0 -> 134090 bytes .../builds/house/2/testing2026.json.gz | Bin 0 -> 54080 bytes .../creaturechat/builds/mine/2/mine.json.gz | Bin 0 -> 6007 bytes .../creaturechat/builds/pond/pond_1.json.gz | Bin 0 -> 6230 bytes .../creaturechat/builds/statue/2/test.json.gz | Bin 0 -> 7850 bytes .../creaturechat/builds/statue/cat.json.gz | Bin 0 -> 4846 bytes .../builds/statue/chicken.json.gz | Bin 0 -> 8579 bytes .../creaturechat/builds/statue/goose.json.gz | Bin 0 -> 5694 bytes .../creaturechat/builds/statue/person.json.gz | Bin 0 -> 5410 bytes .../builds/statue_fixed/person.json.gz | Bin 0 -> 7998 bytes .../builds/trap/2/door_trap.json.gz | Bin 0 -> 2339 bytes .../builds/trap/2/tnt_trap_big.json.gz | Bin 0 -> 5490 bytes .../creaturechat/builds/trap/tnt_trap.json.gz | Bin 0 -> 1248 bytes .../data/creaturechat/prompts/system-chat | 5 +- 26 files changed, 235 insertions(+), 65 deletions(-) create mode 100644 src/main/resources/assets/creaturechat/builds/campfire/test2026.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/castle/1/castle_1.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/castle/2/castle_2.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/farm/farm_1.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/farm/farm_2.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/farm/farm_3.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/farm/farm_4.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/1/ghast_test_1.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/2/ghast_test_2.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/house/2/testing2026.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/mine/2/mine.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/pond/pond_1.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/statue/2/test.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/statue/cat.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/statue/chicken.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/statue/goose.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/statue/person.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/statue_fixed/person.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/trap/2/door_trap.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/trap/2/tnt_trap_big.json.gz create mode 100644 src/main/resources/assets/creaturechat/builds/trap/tnt_trap.json.gz diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cdb232e..167b8350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,9 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Build goal waits to reach the player, starts from the ground at that spot, follows the player until then, and returns to thank them when finished - LEAD goal guides players to nearby structures, biomes, resources, tags, or points of interest and apologizes when none are found within 300 blocks - LEAD goal no longer uses random coordinates when it can't locate something +- Build replay command logs replay bounds with and without player movement +- Build index now rebuilds at runtime from bundled and local builds +- Build goal bounds now ignore player movement ### Fixed - Defer goal selector updates to end-of-tick to avoid null goal crashes after build completion diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 03d0d202..e4aef746 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -69,6 +69,7 @@ public class BuildRecorder { private static final List REPLAYS = new ArrayList<>(); private static final Logger LOGGER = LoggerFactory.getLogger("creaturechat"); private static final int MAX_IDLE_TICKS = 20; // 1 second + private static final String BUILD_INDEX_FILE = "index.json"; private static List BUILD_INDEX; private static final Map> MISSING_RECIPES = new ConcurrentHashMap<>(); private static final Map BOUNDS_CACHE = new ConcurrentHashMap<>(); @@ -131,23 +132,15 @@ private static int heightTier(double h) { private static void loadBuildIndex() { if (BUILD_INDEX != null) return; - List list = new ArrayList<>(); - FabricLoader.getInstance().getModContainer("creaturechat") - .flatMap(m -> m.findPath("assets/creaturechat/builds/index.json")) - .ifPresent(path -> { - try (var reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { - JsonObject root = BuildRecordIO.GSON.fromJson(reader, JsonObject.class); - JsonArray arr = root.getAsJsonArray("builds"); - for (var el : arr) { - JsonObject o = el.getAsJsonObject(); - list.add(new BuildIndex(o.get("type").getAsString(), o.get("height").getAsString(), - o.get("file").getAsString(), o.get("score").getAsInt())); - } - } catch (IOException | JsonParseException e) { - LOGGER.error("[BuildRec] failed to read build index", e); - } - }); - BUILD_INDEX = list; + Path indexPath = buildRootDir().resolve(BUILD_INDEX_FILE); + if (Files.exists(indexPath)) { + List list = readBuildIndex(indexPath); + if (list != null) { + BUILD_INDEX = list; + return; + } + } + rebuildBuildIndex(); } public static String randomBuildFile(double entityHeight, String type, int level) { @@ -168,8 +161,26 @@ public static String randomBuildFile(double entityHeight, String type, int level } public static ReplayBounds getReplayBounds(String fileName) { + return getReplayBounds(fileName, true); + } + + public static ReplayBounds getReplayBounds(String fileName, boolean includeMovement) { String actual = fileName.endsWith(".json.gz") ? fileName : fileName + ".json.gz"; - return BOUNDS_CACHE.computeIfAbsent(actual, BuildRecorder::loadReplayBounds); + String key = actual + (includeMovement ? "|move" : "|static"); + return BOUNDS_CACHE.computeIfAbsent(key, ignored -> loadReplayBounds(actual, includeMovement)); + } + + public static List getIndexedBuildIds() { + loadBuildIndex(); + if (BUILD_INDEX == null || BUILD_INDEX.isEmpty()) { + return List.of(); + } + LinkedHashSet ids = new LinkedHashSet<>(); + for (BuildIndex e : BUILD_INDEX) { + String path = e.type + ("any".equalsIgnoreCase(e.height) ? "" : "/" + e.height) + "/" + e.file; + ids.add(path.replaceFirst("\\.json\\.gz$", "")); + } + return new ArrayList<>(ids); } public static boolean start(ServerPlayer player, String type, String height, String name) { @@ -212,14 +223,13 @@ public static boolean startReplay(ServerPlayer player, Mob actor, String fileNam } private static boolean startReplayInternal(ServerPlayer player, String fileName, EntityType entityType, int speed, Mob existingActor) { - Path dir = buildRootDir(); String actual = fileName.endsWith(".json.gz") ? fileName : fileName + ".json.gz"; - Path file = dir.resolve(actual); + Path file = resolveBuildFile(actual); LOGGER.info("[BuildRec] replay file={} entity={} speed={} player={}", actual, entityType != null ? BuiltInRegistries.ENTITY_TYPE.getKey(entityType).toString() : "pig", speed, player.getGameProfile().getName()); - if (!Files.exists(file)) { - LOGGER.info("[BuildRec] replay missing file={}", file); + if (file == null || !Files.exists(file)) { + LOGGER.info("[BuildRec] replay missing file={}", actual); return false; } try { @@ -282,15 +292,15 @@ private static boolean startReplayInternal(ServerPlayer player, String fileName, return false; } - private static ReplayBounds loadReplayBounds(String fileName) { - Path file = buildRootDir().resolve(fileName); - if (!Files.exists(file)) { - LOGGER.info("[BuildRec] bounds missing file={}", file); + private static ReplayBounds loadReplayBounds(String fileName, boolean includeMovement) { + Path file = resolveBuildFile(fileName); + if (file == null || !Files.exists(file)) { + LOGGER.info("[BuildRec] bounds missing file={}", fileName); return null; } try { BuildRecordIO.Loaded loaded = BuildRecordIO.read(file); - ReplayBounds bounds = ReplayBounds.fromActions(loaded.actions); + ReplayBounds bounds = ReplayBounds.fromActions(loaded.actions, includeMovement); if (bounds == null) { LOGGER.info("[BuildRec] bounds empty file={}", fileName); } @@ -640,6 +650,16 @@ private static Path buildRootDir() { return dir; } + private static Path resolveBuildFile(String fileName) { + Path local = buildRootDir().resolve(fileName); + if (Files.exists(local)) { + return local; + } + return FabricLoader.getInstance().getModContainer("creaturechat") + .flatMap(m -> m.findPath("assets/creaturechat/builds/" + fileName)) + .orElse(null); + } + private static Path buildDir(String type, String height) { Path dir = buildRootDir().resolve(type); if (height != null && !height.equalsIgnoreCase("any")) { @@ -844,6 +864,7 @@ Summary save() { } LOGGER.info("[BuildRec] save file={} actions={} additions={} destroys={}", fileName, actions.size(), additions, destroys); String rel = buildRootDir().relativize(file).toString().replace('\\', '/'); + rebuildBuildIndex(); int finalCount = finalBlocks.size(); return new Summary(rel, actions.size(), additions, destroys, recipe, unique, sizeX, sizeY, sizeZ, finalCount); } @@ -889,6 +910,136 @@ private static class Replay { } } + private static void rebuildBuildIndex() { + Path indexPath = buildRootDir().resolve(BUILD_INDEX_FILE); + List entries = new ArrayList<>(); + LinkedHashMap byKey = new LinkedHashMap<>(); + collectBuildEntriesFromResources(byKey); + collectBuildEntriesFromLocal(byKey); + entries.addAll(byKey.values()); + entries.sort(Comparator.comparingDouble(e -> e.raw)); + int n = entries.size(); + for (int i = 0; i < n; i++) { + entries.get(i).score = (int) Math.min(5, Math.floor((double) i * 5 / n) + 1); + } + List list = new ArrayList<>(); + JsonArray arr = new JsonArray(); + for (IndexEntry e : entries) { + list.add(new BuildIndex(e.type, e.height, e.file, e.score)); + JsonObject o = new JsonObject(); + o.addProperty("type", e.type); + o.addProperty("height", e.height); + o.addProperty("file", e.file); + JsonObject recipe = new JsonObject(); + for (Map.Entry r : e.recipe.entrySet()) { + recipe.addProperty(r.getKey(), r.getValue()); + } + o.add("recipe", recipe); + o.addProperty("score", e.score); + arr.add(o); + } + JsonObject root = new JsonObject(); + root.add("builds", arr); + try (BufferedWriter w = Files.newBufferedWriter(indexPath, StandardCharsets.UTF_8)) { + BuildRecordIO.GSON.toJson(root, w); + } catch (IOException e) { + LOGGER.error("[BuildRec] failed to write build index {}", indexPath, e); + } + BUILD_INDEX = list; + LOGGER.info("[BuildRec] rebuilt build index entries={} file={}", entries.size(), indexPath); + } + + private static void collectBuildEntriesFromLocal(Map entries) { + Path root = buildRootDir(); + collectBuildEntries(root, entries, true); + } + + private static void collectBuildEntriesFromResources(Map entries) { + FabricLoader.getInstance().getModContainer("creaturechat") + .flatMap(m -> m.findPath("assets/creaturechat/builds")) + .ifPresent(path -> collectBuildEntries(path, entries, false)); + } + + private static void collectBuildEntries(Path root, Map entries, boolean preferLocal) { + try { + Files.walk(root) + .filter(p -> p.toString().endsWith(".json.gz")) + .forEach(p -> addBuildEntry(root, p, entries, preferLocal)); + } catch (IOException ignored) { + } + } + + private static void addBuildEntry(Path root, Path file, Map entries, boolean preferLocal) { + try { + BuildRecordIO.Loaded loaded = BuildRecordIO.read(file); + Path rel = root.relativize(file); + if (rel.getNameCount() < 2) { + return; + } + IndexEntry e = new IndexEntry(); + e.file = rel.getFileName().toString(); + e.type = rel.getName(0).toString(); + e.height = rel.getNameCount() > 2 ? rel.getName(1).toString() : "any"; + e.recipe = loaded.meta.recipe != null ? loaded.meta.recipe : new LinkedHashMap<>(); + e.raw = rawScore(loaded); + String key = e.type + "/" + e.height + "/" + e.file; + if (preferLocal || !entries.containsKey(key)) { + entries.put(key, e); + } + } catch (IOException | RuntimeException ignored) { + } + } + + private static double rawScore(BuildRecordIO.Loaded loaded) { + BuildRecordIO.Meta meta = loaded.meta; + Map recipe = meta.recipe != null ? meta.recipe : Map.of(); + int unique = recipe.size(); + int total = recipe.values().stream().mapToInt(Integer::intValue).sum(); + int steps = loaded.actions.size(); + int duration = loaded.actions.stream().mapToInt(a -> a.dt).sum(); + int rarity = recipe.entrySet().stream().mapToInt(e -> { + String name = e.getKey(); + int base = 1; + if (name.contains("diamond") || name.contains("netherite")) base = 4; + else if (name.contains("gold") || name.contains("emerald")) base = 3; + else if (name.contains("iron") || name.contains("copper")) base = 2; + return base * e.getValue(); + }).sum(); + return unique * 5 + total + steps + (duration / 20.0) + rarity * 3; + } + + private static List readBuildIndex(Path path) { + try (var reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + JsonObject root = BuildRecordIO.GSON.fromJson(reader, JsonObject.class); + JsonArray arr = root != null ? root.getAsJsonArray("builds") : null; + if (arr == null) { + return null; + } + List list = new ArrayList<>(); + for (var el : arr) { + JsonObject o = el.getAsJsonObject(); + if (!o.has("type") || !o.has("height") || !o.has("file") || !o.has("score")) { + continue; + } + list.add(new BuildIndex(o.get("type").getAsString(), o.get("height").getAsString(), + o.get("file").getAsString(), o.get("score").getAsInt())); + } + return list; + } catch (IOException | JsonParseException e) { + LOGGER.error("[BuildRec] failed to read build index {}", path, e); + return null; + } + } + + private static class IndexEntry { + String type; + String height; + String file; + Map recipe = new LinkedHashMap<>(); + double raw; + int score; + } + public static class ReplayBounds { public final int minX; public final int minY; @@ -913,37 +1064,46 @@ public ReplayBounds(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) this.sizeZ = maxZ - minZ + 1; } - private static ReplayBounds fromActions(List actions) { + private static ReplayBounds fromActions(List actions, boolean includeMovement) { boolean hasBounds = false; int minX = 0, minY = 0, minZ = 0; int maxX = 0, maxY = 0, maxZ = 0; for (Action a : actions) { - int px = Mth.floor(a.px); - int py = Mth.floor(a.py); - int pz = Mth.floor(a.pz); - if (!hasBounds) { - minX = maxX = px; - minY = maxY = py; - minZ = maxZ = pz; - hasBounds = true; - } else { - if (px < minX) minX = px; - if (px > maxX) maxX = px; - if (py < minY) minY = py; - if (py > maxY) maxY = py; - if (pz < minZ) minZ = pz; - if (pz > maxZ) maxZ = pz; + if (includeMovement) { + int px = Mth.floor(a.px); + int py = Mth.floor(a.py); + int pz = Mth.floor(a.pz); + if (!hasBounds) { + minX = maxX = px; + minY = maxY = py; + minZ = maxZ = pz; + hasBounds = true; + } else { + if (px < minX) minX = px; + if (px > maxX) maxX = px; + if (py < minY) minY = py; + if (py > maxY) maxY = py; + if (pz < minZ) minZ = pz; + if (pz > maxZ) maxZ = pz; + } } if ("place".equals(a.action) || "break".equals(a.action) || "interact".equals(a.action)) { int bx = a.bx; int by = a.by; int bz = a.bz; - if (bx < minX) minX = bx; - if (bx > maxX) maxX = bx; - if (by < minY) minY = by; - if (by > maxY) maxY = by; - if (bz < minZ) minZ = bz; - if (bz > maxZ) maxZ = bz; + if (!hasBounds) { + minX = maxX = bx; + minY = maxY = by; + minZ = maxZ = bz; + hasBounds = true; + } else { + if (bx < minX) minX = bx; + if (bx > maxX) maxX = bx; + if (by < minY) minY = by; + if (by > maxY) maxY = by; + if (bz < minZ) minZ = bz; + if (bz > maxZ) maxZ = bz; + } } } return hasBounds ? new ReplayBounds(minX, minY, minZ, maxX, maxY, maxZ) : null; diff --git a/src/main/java/com/owlmaddie/commands/BuildCommands.java b/src/main/java/com/owlmaddie/commands/BuildCommands.java index 1799b5b4..caedcbe2 100644 --- a/src/main/java/com/owlmaddie/commands/BuildCommands.java +++ b/src/main/java/com/owlmaddie/commands/BuildCommands.java @@ -12,7 +12,6 @@ import com.owlmaddie.buildrec.BuildRecorder; import com.owlmaddie.buildrec.BuildRecorder.Summary; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; -import net.fabricmc.loader.api.FabricLoader; import net.minecraft.ChatFormatting; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; @@ -28,13 +27,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * Commands to record and replay builds. @@ -109,6 +105,8 @@ private static int replay(CommandContext context, ResourceLo int spd = speed; ServerPlayer player = context.getSource().getPlayerOrException(); String id = StringArgumentType.getString(context, "id").replace('\\', '/'); + BuildRecorder.ReplayBounds moveBounds = BuildRecorder.getReplayBounds(id, true); + BuildRecorder.ReplayBounds staticBounds = BuildRecorder.getReplayBounds(id, false); EntityType type = null; if (entityId != null && !"player".equals(entityId.getPath())) { EntityType raw = BuiltInRegistries.ENTITY_TYPE.getOptional(entityId).orElse(null); @@ -123,6 +121,20 @@ private static int replay(CommandContext context, ResourceLo : entityId.toString()); LOGGER.info("[BuildRec] command replay player={} file={} entity={} speed={}", player.getGameProfile().getName(), id, entityStr, spd); + if (moveBounds != null) { + LOGGER.info("[BuildRec] replay bounds file={} includeMovement=true min=({}, {}, {}) max=({}, {}, {}) size=({}, {}, {})", + id, moveBounds.minX, moveBounds.minY, moveBounds.minZ, moveBounds.maxX, moveBounds.maxY, moveBounds.maxZ, + moveBounds.sizeX, moveBounds.sizeY, moveBounds.sizeZ); + } else { + LOGGER.info("[BuildRec] replay bounds file={} includeMovement=true not found", id); + } + if (staticBounds != null) { + LOGGER.info("[BuildRec] replay bounds file={} includeMovement=false min=({}, {}, {}) max=({}, {}, {}) size=({}, {}, {})", + id, staticBounds.minX, staticBounds.minY, staticBounds.minZ, staticBounds.maxX, staticBounds.maxY, staticBounds.maxZ, + staticBounds.sizeX, staticBounds.sizeY, staticBounds.sizeZ); + } else { + LOGGER.info("[BuildRec] replay bounds file={} includeMovement=false not found", id); + } if (BuildRecorder.startReplay(player, id, type, spd)) { final int fs = spd; final String fid = id; @@ -134,15 +146,8 @@ private static int replay(CommandContext context, ResourceLo } private static CompletableFuture suggest(CommandContext context, SuggestionsBuilder builder) { - Path dir = FabricLoader.getInstance().getConfigDir().resolve("creaturechat").resolve("builds"); - try (Stream stream = Files.walk(dir)) { - stream.filter(Files::isRegularFile) - .map(p -> dir.relativize(p) - .toString() - .replaceFirst("\\.json\\.gz$", "") - .replace('\\', '/')) - .forEach(id -> builder.suggest("\"" + id + "\"")); - } catch (Exception ignored) { + for (String id : BuildRecorder.getIndexedBuildIds()) { + builder.suggest("\"" + id + "\""); } return builder.buildFuture(); } @@ -169,4 +174,3 @@ private static boolean isIncludedEntity(EntityType entityType) { || entityType == EntityType.SNOW_GOLEM; } } - diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index 793243fc..387172a5 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -191,7 +191,7 @@ public void tick() { int tier = this.entity.getBbHeight() < 1 ? 1 : (this.entity.getBbHeight() < 2 ? 2 : 3); if (buildFile == null) { buildFile = BuildRecorder.randomBuildFile(this.entity.getBbHeight(), buildType, data.buildLevel); - buildBounds = buildFile != null ? BuildRecorder.getReplayBounds(buildFile) : null; + buildBounds = buildFile != null ? BuildRecorder.getReplayBounds(buildFile, false) : null; LOGGER.info("[BuildGoal] select build skill={} type={} heightTier={} file={}", data.buildLevel, buildType, tier, buildFile); effectiveBounds = buildBounds != null ? expandBoundsForEntity(buildBounds) : null; if (effectiveBounds != null) { diff --git a/src/main/resources/assets/creaturechat/builds/campfire/test2026.json.gz b/src/main/resources/assets/creaturechat/builds/campfire/test2026.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..3d12f0e29337c67758a7c220822fcec56f9c9a31 GIT binary patch literal 10668 zcmV;dDO1)TiwFP!00000|IK~db{t2NsJTeIee?bI|Krn-U;h2g`!vPrbDFD3_0sss z<*$}D|Kpk^w3@zs^XtbSKK=6X&HMj+^Ye$l{@drD|MKR&zn%a5+vgA8zj<$`|MB_b zhyVBSw>R%q-oE+p`SYj0|84%%4+uuI@<);sy|BYXI`sklOeE#j@+yD6R<>RmT0e|`U zWB1`L2CnFLUw;4P;V*yx^Or9_|Lu8yyIb1+;}2W0`DN$b`tHY1zkT`e(+>|nuDt!_ zpNm5qJIZKn`O=`wxexE;sKm7A_|8v#WG-_2b!TtaJ z!+*Sa|E{*Fd9E-PzkK@g15CxcG__RKEv^TqX#}zgv*h8nK$A&YDJ9iAuh|Bf$mEuq zy+&X)CkwkTFiA40IZgcoFilfUZYAFtm<0cNYYQ-bAWFG6t};oqRBnMwt<>VFNh&#M z(G4mGq#QUk-Xhl~{WezoZCOzHBvw@ipeof;D#bm8dyr6EpfWEJS@^HwSl35XO{Z!t zI#4!wV7W)ERkh+Jd(EOPlKBp*+^i&UuIl(l&ajGMcU$N!*6Mmx)+Wj7rC+S-wWJXi zqDjjo<9lHz8-CZFgOzxwO5yvGG|s4S+LIT;9KMIJJjJjCtwxS!wmt6Elh;;Ma;sR` z7ea-TT$5ESS*rDP(=TA*gVA6kgTVZSP*onNW>}G=X?qEq7mI3E8r~IHx0gZHa>i0< z!o5$D=l89LB{=UmamjO`4S_{{gyryUa!tAPx3IwG`SFKGaj?Lh492_QgyE`fUG!@hgtq7T58VMhy|5U2fV`SNICO_E0vQ>?1Pe00waHw{c? zywTDLcm(Vu3SSp`pqG2tOtk@Y7IX9S(Gt}O<|Nz|Y(+m4tvWsPOoCp*4RJRh~ozzu7%e&Fn97ci3@v zJmV1F5mhY{{-TwBsY*&9Jog2a4cyCbfC@*$_+t$}8P}=q9QK0ONcbr;THFp*@>a!e zg9=7LoctI(yl#!vQ>Yr5tkrUZQ*{Vb)eI|kgBv&$)i7&y02LlcxIP_a@X2xkgS;mw zB_FIEC@`C1HQ#gpGJFZglkPpL!nn;nFQDTBHqqs=$!W?UF>=of=$UUe9G?Xh=|kX~ z4L}8B4*r{^s%zLDs+MZOr|ALZg)W?qdhS5MlTIbIk9`&vdWFlrmV2_iGge9~l8Fz%3i-r-0Fc^X)8_z~zSV-W@IQjQ3vVr$7lwr*$v$I8-(zdY?jf5ND@c?qH$=#LP@Cboec6kE(#i z`?q0z{_w*du2|uECl3+aM_`&7oVvmgLW4uNjWpn{X=uqfSnz;&?Uu-zH#Hf#UJES8 ziouIWAW+#acX1AogaA@a2@Ydf94{#oNKoo*mQ5yD^5ZrOsNijR;9%j$v|UskD!2Za3`3P8ey0iL7 zSm$l@h?Ma_7st2U;SL!LJryZ6G|ztN)2N0KWrn0J&$LFsn&L8V0{b)BKb9h%*kFph-cR*F8V+4+6joRgn+K6dW!a3Kr(X>;(&7_;-Gbp?=hY^ywF~{WA0N)83cMj zn6G3K5G^2>;N%iJ6Kfq(mXiET6S#lcPT|TEEC)+45S$UwM>tskWUXP7+;yiQ ztYi=>5jueq?M$gi3Jg2CynJpf))Ps8c06w>JiOvXVZvtd0e69!C|h10t^&+1gQsCeM-imHI9x8|?QE;O6q z&bDT+>k0~tJFndG5aPKmH4O;U9zmt_j{EdHhzvj#z3VNM)TR_H+gd^~!*byVab;)3 zYr!?DWc$!Af>VGCu9EcWzK}BWcXOB7k{PTym;0J5q1#oZcCiJ+7lUoz_q;pYA5UFR z2Vj9emJz4D!pbz7M8gqDvZbTqk&cib9I?tmFj#sQRB3JI$eZKg%ph~+BsmE{^1PR^4iSWOnl z<*GY@5dh0s@d{O~O_jY9%Z-f50km5#o3NY%_@GV^#`X@&Ed4K=JTzcAcza^olO$zc z=L`$96z^B(S3i+Jxe69mtF)S;x~ILv6177yeRSnMk-RaZye zf+3u{!ZNzjrF22lJmKlr0sWyzV9neM|L$ENoHQLkpz)3H6SEd;>@mrkVp1OY>~sI9 z1%3rx+T~~=B8xAF%L41CZAkM_ZMDEIC3bpkTCHfea)T{mjY?yndC8Fnh^f`L5e9Vi!D)w`zOHdrPRU((gjrm%}t%VILydD2NpJ0SPz0NgE{DUYH`@S)Ws;r z(THQXg7NPz%`=Rq9)LBkmghCBK~Lf#SUQzc?&lSqt(iYq^ywaUSA${P71iu#Xzpy5 zFfsw3_r3?-e5!1BUG}hn0-5Wb4TP|7$m(mu#a^&jdy9k03Xblgd)K!LYih6;ciuz8 z;;@RbXUv1D&FA<_k_z|fTSIO|l`gH!NNY}Gq32QbZRZ#StTU!T6TJAb52EIK2hX*Q2Y7QHfeY6!<&F!E>^VGu>lBFa z)lTk~mp-wRy1SD)0$E|bd`EE(W;I?TrV%@s9frMj$pba2Rt%_GJ_d^k&%PZ}!3B$@ z%NOXW4v??LDqjTIOPh$CS=Zb-KuVE;?02^H8k2ag@^+v_J>#m8t2LbA>s*UFaG1WuJ%7{61(Rjr!OIVj3fb@WCX{IZsBllQFreIvJ` zT1wtgSE<4vbBe}5SMi=XD|>rxz)@jq@K08p8#WM_?6iR>1M`eE<7b*KT~7hG477T+ zSX7NVzJts@V*~L|W?#ilcM+Jp3){TgoUrW(DvUv@H%Z0fdQp|ASZks6@~~4%$f_XZP0b6;L)Sr**XSRiMH# z!gHXvSu2oTXZJS#lY9$`8eWvBqG}dfcKYYpHFv#X00sZrhDe5d9aUmk9UN`EMPNVm7#UDNH(Q1x$_Ii9;gs7D2f~kdpAa z(ZP@jU>a(iNFdz<hprZd+ZYoN~y<-?|K6<+nS5$aLY%Uyq6fj;T{aG!h;OJ#Yi%RiYZOgL62h z?>cM~BLa7|>r}zc8NU63s!UFq(7Rs2r1aZwP%n8src%Lfq;JfW+ZS zr0lb(?GmvneI$DvD4dMQE^3~H!ydNYR*FUqW6lf6)+M^;3ivLzH!fZ26dX*By>Z<9 zl(&7`4WB8QZPJt@%v~`zj6k}HF5GiKrBHaKY-^HUG%_k>IP5mELD!>dQ^M-4WP<{* z@^Mpj1QqkA*3XC;m8(1es(@6L=*6h=MBNO%>#-_^v)=je<20s^fMOy-x}M+Hf{NO| zw*dva=>>$hkkf5TrK`jKMB(70$jM#^6!yTHrbNxY*vp#2t>b_lmbGc_Qf{|Il~bcy zMPs(1ZF0Hq(o~diIP#>HGSAXDSN?+Ptp)KxNo7E6{TZn6{$Ysm z6RHCbdp!z@k}fW>s`ee!ZhrujPDxuockeZz5*7%+os}Ndpc)-`fsF#Pa83n3doxRp z4WYqi(cDT)YH*z&c?=5#)Vq$3p{T0M+>zH<8QXS7+)wM~}Y}F@o&|@k% zv*~z80yud4OXCzgFur!?d>{y^)-J=vlvg@3glvJ-yas2CIqTsz^08PBktQh{j@L;- z>;V$cS~^{ivm8zV%^qRZoqDwyClvHpeVg4%zZ@fFc^<-fzJ7^Pnq9fj zC_P(nGOyIwr;IM;x-QH0@Rle{G_w$@b66wJN4-oTr@_@<*RXiv?US|hzNUEKX|Te| zdced0-;Qu|2DbxOfECK8CbvHb>za0{#!=$hg0=K`}R50DIXWU5*{6- zrAf}gN)kNs4J>Whz6Decif*UG!OKg{ECWE=M})u~B*~`p^eRYNYIEiplBA{Wr-Q^Q zJh;hZ#mUb#FiL%M?rP7e3I|i%6I7+deC{TWIc0D>;`l+o9u?kY;bXlVbk zBqngBADjvg8JG92nAj%I@&$iYo5yNdNnNQPb`!U>#E3E7@Ik^_Wj_`Smb5J@Wp4)O zW`TEg)3aDuOySK9*BijpS+&8IEH>{r9zxqOo*-S6zt4D(i{?ebX%`n=#1UIq1ooQE zZLrTZFO^jtET*-Dam{Mh(z$PoH88aHTh);fyLbRs;2L&1I}eIBM^>xeXtfS*;$w%+ zAzWb2pf+VyObEX$4d7Nsa4}6!{lT5dPzh($08|GH0L;xt+!ke~UJYL+)fJ=-(&K11y2Z zA-c+)iCk=js=~AlVS?X8#Rvx;tg4<#td~utHfUPOsz3A!jv!vw!vZ7CHTSD^>XaBT ziv0jA_L|{2>ZOWK<>LdKj3cmEyPUNX7qw>t_X55v*Y%fWi-#o{o)USOcW zJd`$|HRu4W!nr5?=)}hW#E7^1YEX@}O?|t|Lx1{RZNof&vuJ%?wm~?#COofT%`Inl zwxl+(>BhdfT9?Kl_Kd^3b++jUu({2wALo7iaLH{@F|;+HY?}>HiCOr}^tWy&3C!$) zx+zSeh9+C`y&Phy$@_t?<6JbItfslYq&P=z#g58uM1w!ujQ>{cG%Tc0JAADiJBF-g zjvtHKO5%jov;(R!jSqqdsyg?DFC96Y_gkvo0#s!to(>IOHtL^T!V>1VLaCcl;7qi0 zK>l6Bj)iYJss`*JSX{QI;OEIxWs%Abs$6g>GLVCto^wy{n!_2>dLvMUgNyN#?WCst zJV=GDXVz8TKXzX~kIt;qCh8n;I`Au zeBuRs;$^-#=WBg8V4+^B&soD53fblDEkIzs{_x3^Z@md1F$&XC#$a1Sm)mByDpYy zMrM2^qDEz~GNMn`!Ajh5$6|M6FOD=^Q43J8+PV<4vLLD?lbe77`E(9|a$~*hUfF13 z(?qGv<2jk%IiCrZ4&xeqpTa{|@rWtjp)4JRcP_2jX0^CjAjP!^j1hC5?F9HGi)p>sC? z$7@-41qZ&*S7K6*{0fOx=p(W1e~4L-!e5wdoRzj4P#Lcs)91lD+jdqZzbz=OpeR_Q zq@3Qi1ywoONCIaHo{gWIik}ba31+<3YRMN*vj!N0HDQxr+tCZGUK{RR;j}xp6+fb4 z+=C(nBbQ2^4T^1VB|G>DkKQAuGMTE}X!ovyG8h;D#4t zz@RcTbV;WetScf8$EGxX$>X&X2C^ls#E6E)L3Of(#VBMYRCC;fs+1wv%&K?1vjRGJ zE?KjnuT^gm235A#u|zz>4%N_0pN60JS$9Qh!jD|4MyWBE*UEP+g&Z(D7(Wx_u5c4e z3VS{#V{b%np^iIQMxYbmypC!3|*p2zKE50kzSra#_Oc`c+_wwIxXe3OfJHK=*k zN$1Dh|BOFq*jVR=4ojC_k7*VRmGxYgK@Q&mP@GEA*ZDrGH4U+v13^(;{Ky!@MH}~O zRQQ^rwI9~~AS*CJhxSTT%_;8F^$yQTN_ZY)-ePT4ng_iG-HgR1XrE^eP>LweZJ-aR zz@6ZccBp2#pE^JTEKd|-a&{!-R!YvhdTB<#Sjk{!UGyhVJq}D6!tqimv#5tr*kgyb zc;i=qmQo;U37A!^@~RkYK=Ir{g@v`v!I(wyjlMsJXDNq88V4rCBEiSDGLKb7`>sX5 ze2>f9o%rx&co;Xv;f~b#P;JJC6H`zCECB(C`l40{-Vzs`W{1h};(~IpJKXi+Y}MT! z^d~{-JYTG95`3;_T=wEp`64cLdstVUsw+{kC)Q-8O+9zz-4PNVmO}pIy`_Rvk%tVj zxOVs2WLf%wQxBp#cf=HBg1g+Mj1-$^uv7OuP_tC1s;yqXS-@?Cs$-Qx-Ddcp%tl@o zwZpmOSaYSiRH3HT`AH72bg6NJ*oMy~B8>N@xpC{^m62to;H+18sWFdJ&t<^`pB*3S zI)~?8=F*;N(qqp|EuG&?aZ*TsuF%*)mD!S4nKQEJDDdOVlI%8%yJN zPW%t|mp8gNj5^ zwQHDyT^uV)$uZw2$^w&m0gEmHxK!e}q?FpCHHqt2IRRwgN}Ugt3)~Wl1heq_u@s<# z#PmN2v$uu|xXc0CU@dpwlZ8}&j09f+Q(!Rn2aLdf^>_s^aP@InPoT*>EOl% zr4&WFZc~m%;q1PjbBa;w^2iaSpJ)rK)kBv*`WE?UP>NAib@IJ&U3>H`(Ztu^^l?E-KdvoHW97N;v|xLB!nwjWFZ0Vv zVv4ID;yJ2dmTujw>;bnO_P}-&K4>WT1$FMq>N;#d0li~3$5Vl4OdP$Q<~D-hg?m1x zS!6u*apRLf%Lq*H$vMIe=h;qr#m~aTy)%#*>Gy88@a$RM;5)k@fd^o*TzoYT=-~5{ z4`YJ4(422kIOuc@%;B5PH%Z_c zaIPJUH1w<{?5cfN|SzXYypR*S+pDnx1oF!wK{BPt>Dxqd<7VL%cY*pyOxUrhH>>@nVB?-D6{;8TGW?e5ukE| z8;wWF0_>?S1%+*vPVc0pSc&O=IfLi&4%W9jwc!_cFjE|};1vX!sSh!#=b@T8d+szK zf;!u9%n#o{V9Lx!hVg89+a$c61~q%mUHu*tn7lRvwy$K+6H|)pG`M@u$K))jwLWf( zv!8W9QOp5Qo85!$5(@8pORcl)9;#TqH%D;6u!B5zH4G=Uj3Z(?avT*nzepL1YQP@W z^>D+dKwWNO+ucIn+`%hh&90Z$JaVyKC#u6+_)bbp>;ULCagH)*J5G@g!D7(cc9dAY zWgaB$53fP>! zI3ApNZLmfh`wgsT9{aYtjdsIrd?+_OKq5wLqcP^Yei!GE$z6AG*0(R6mfKUi_`x<~`qzZ?DUj>&(mXt$Dq>9@Rg zqg(SM)1HolN-=@b^n8W^Hww<*5mjrrfYMe|OJ?cNJ7UVXyOar90!F92p^Ln zV5q9BvZ-^K!yQ4r<9LvH0K|gop*!!M8#qrk%e{isqdezbQQ^ML6A!n_uR~xNwKD*# zP#9uU3dN{xjwEr|@H4ebxzCEIRTsw{@4zGB{lZKUarZt=pz&5@P)q}v!0H#IB z!WxzfW9)fT;{fl}IZcRVQI^1L&iZLpCYaE!-x(H-InN=%$*P=R@2p^B<pYy&ag?{MXq0YZsv=6u9Nd`C1E$}}Ji58u2o`g? zR#6BY&3aG8CZ zOt~IZJq`+QBa8G`onOKJm3uCgGiDl{w1x&Aq0j*?D~~(hAXbYyT=TuEtl)?^4)WmO zwk41Yht+GxZLm@e1Z+r(PAd+(ID57t?6S#YE_JAQfvERE3AI>;pTRlgNZcD-L55yw zg&OW3d%$*38M{~u#}ZUNI*(dAP7Wb`iuECTKLC@l|4xkGzMM?YB_=jurCjO$%i={d z+|YUv888UrP}dQdU>u_;$HcnT9a^)GiHYcXxlSxK8sLIm4eDJ?)Q?HMa@vDJ!w67? zC9Ryn1ES?Sc}%>;8N9}MuD~c3sx{$NEw#1Weu=I9a~V;3+oT`_hR^bCQuDSP~CUH&^VV#Shxyq6hg?O?(i z(qjGjXLVFP{v^^6nzq3IgU{hr+`iM?P&|w5GK?r`F<`-oM}1`Zd1;=9N{q5X^{8Jh zkFa?1@xW&6g=N`rc$PNI(iRjjlbq7ohYcQxlvYwobcPS-2AG^Q3dqB|2nt>7mAZN< ztZTt3OHmdI&rU3m?2bP?bn|SKQ=UksT&&i5AZUNA@U^fR)Nz8MldLF@& zwgDaF2SCxCrT^Vqtyh36bEvu1PG0-Cdw2#lw_?EIt0wr<@V7D`*nkKbktA}wIizuN~I935^hP~V|quGr9 z2Pj|V!?R4}%}n04ykdfbUcyW-lQYvAhOJhf zPJ#r~T8D|>o%I|n@q|&LgWt+Ib6CLyPx;UDL7!#KMu3{dDJ7f6ESfv3@$`ZmH7Tl{6p_LR5*e5hVeVy6$Y?6R_UpNwRhc%|~6c18Y@}qk>&Xk+pB! zc@?V#OD8NB^O3kjj{~FkT4fa{9TruFb;P?dcVKIjt;oGlZi=}L5$dDcGA!h#sMs&f zDNvd$?T)CY;=bdFr0lNP($jZSR51_bs4#)CGoK!ZMJir!HhfdosJ_ew2CJcAu&6ig zJArOE63KI7bRJ|~Uk4_ZHNneKCUz{pZcvreSUe4Vo$CNp@FJ71>e!Asgd+yn1RkoINLf{%hbwtuqMZ;un*MXbs3nbEkn5zyjw7`PT^Aud%EGu9>z7h zFO2RXir9PR}3OtD-BWH@-|lCcZT zsIqn?;oLOKJuHJizc^la80VP42v}HlGhCm-i9AdozY17tjpByEbVVsCZ;ciXy>TXm z!jk~9@*=RPC!MIT=&-0eCo8jtHW`fW0AUw8RN0iHu&8t$y4^qZ8akKw=DELrfraN7mag-grOrUdSa_36sn$x( z*0OO0`Rq_yGiR?A1gxDywauoMYDY$aisx)V{|QmKg4ZRO>seUy`KV%gkOzHTpm@4{ S2i3p)>Hh;-cN24#5di?cj>1F$ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/castle/1/castle_1.json.gz b/src/main/resources/assets/creaturechat/builds/castle/1/castle_1.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..e7cd8f3b50b6c66131559ef0d257a12cf473ec55 GIT binary patch literal 10718 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 Qzy10D0Z%dodP)KT0C>i$$p8QV literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/castle/2/castle_2.json.gz b/src/main/resources/assets/creaturechat/builds/castle/2/castle_2.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..8866032e8060404d0c1b8d5f645b624934bbad5d GIT binary patch literal 14357 zcmV+wIO@kAiwFP!00000|IK~bavVpp<@@~%J`-sX?%}cM@jlIb!7M#(ZIpzTXw4Qi zB4rx&wf^@xC$cItaw$}yG5}D$hy+05AQq3~+ws5r$A7*1@Wa=SpMQDx-MfE2e*N(7 z{kz9MAOG9q$De-w`tCbFL~-?>kYd*43R9DE|7-AO7*TUq5~LVLia&c^CyN4|Xh?E_cx{U0B`KK}OU^G`oL{y2W-?;k#WdHm~hzT212zrWOX}E-<+FZTqItEYCC+bRWj}rXqwB(6 z`S$#Fz4!IUkH39ge4x3|?T3H*?ZcNZfBT--`Y&I8`TWP9i}n2Lzu&h;?bpv=cr)u) z|MwqZweaizH~#C-@n8R{|N0R&E5HQsbAzQA)nafVsrj{`~OAyYC(f%%0`w_$NMo{Q*`o4JjoJ_k+rZL3vk(;EPKWs0Uw$Br$pamW$=R z8zkdTH@MQi&(DWZC)*`Ux^w+d`noC26a)q&aiOioi|Hw2`k_x zmg00a5sE*R9PUB~STUHhsGzS=g6S4jaxrCmJ7*zc&n8r15Pp1tO0C2$1|la(SScJ` zTq>W-1{QdDC?RGpS#-w7X2ca0C&^sSGcMsrq{m|6(5eP8EJVUvvI8rd1QjVoRZH;S zHmXeD#=?BwRbfmuAAfl~4oC2YGwN~HVFms6VC4dfDbdti$T8;mNi2hz%%BXm6=&hN zB_%Pz!&(H;N(gR!+8=;QVu8hUKI4mW(g~D#T+rgn3@d^@w!x%dJ{Gb6`tfC?B z;EFH`SShTi=9KabEXJXIceD&HnD+-PX+Fckaq&`c{Wah(bh3qo&y^%*9{1iAenpE4 zXKlh2+>o+}@3CCPV$KqAwvsyaYg78bZ>1%A^*H%KqRzOI4S1Bp%pZb))|6C^U&PyR z<>>MH8rDtc^}-*(u7!r3hKNHiZoP}2KK!r;%P(L(=!i>R57#qRE4~D(G=zZbdHm@) z0?P_ay3DBXdnY(?J?xgGRB%pxfCY;i`v7wrK`Zd~!b;!`p3H*}IPW>R*~CZugdQ)& zai-;Y`{AY}HGs;b25VFrT{Rss;k~vLPN=Y5u%xB2@rKus?%6A$3iMCCF(T*myjH-F z1L)4|@fbKf7Tk`Lf2#GW3lvhRElpN4JHKF5|%76=)jywV#P=W6hX}ph-hxtwq=6$FF89g1d6%X@W?w}-r^Whja z@y>-2l??EJR1JjPd+{OX2_zqCct&uaG$ff0e;nYPv@ekRdErUn zz+vZM$*^owXkgdq!;_fF!DejUz%?SaZ$jljBVi(D zR0W1C5H1vi6s5w}e z0^W8zm#c) zL5esoU`as?|Jhh{A=8W=P(6r3NDU&-@UNB^0F>}@!1&E)jVYT3f(9~m8YWC( z07-75&9DvdvqM~;sSDc|p4dM4B4m?Ui(^(E56K1=Ku1^IEYRg>&qnq9?E@&L$zbjx z%t9>NrMiU4@F!Djg3SdE24HsBJ&6V@08Frm=5Jr78p8FBb%Gxzn_8r&w{DlRyb&bb zgQUkHEjR8IcRcX2_@j_Od1LmCB`AX>CY3uJCgW3QsnC}^Wk*(^_@sF!OkLzs8c_JX zEl3?-;Nu4zx7jgxO>Z`&=(f+D(fce(5;#pNK` AQ(Pf3pt+7lRaUb!a!SWcuV12 z1Vx*OdVd%|MAIHp%zrefGAmvaA|W8Gj8VOS#JS&*%JV2bYp$4=n-Y4CF?h^r5Y z3wQ2ae8LpO!MPk<6c(UF340y_98%V{iG%q8!Y5#jp{N56`h?17rB973kH;Z}EhzX= za4uoUQ<0`>##LS^GbZ>k_@PR^YpwhW70xQmW>muw5#8<}^J_R#RzQQD%^sdt){&N6 z)<){cQJ+y6xF*Z7k}))RJ1X;oG1k3~M;$kvM?k@zr&^&k(Bw;dqClDfiKAt zT1hUfGcioK6kk$Jd^qo$I!KmKo6VSF8j?#LnoeMrP4FaV5MeMGw~o1b@T8*5PQh^y zQO&OdiWO&Gb5_B7wq7WYg#xKzCO0v5)hrZF`7##+&K}&YNsqV(%HIqMOp8uv0aK|2 zYOZ2+3-URdsRLF@Z-&G&bLPG2sn$lEGo}RB3~XyfUG#WkPGo3~MM*v~!B^JQ)8QNQ z*nm?yWgPzu$}?gfv|T6}l$F8vY~I6~<%R5-4Axqiv`Ke3_D?BnLIP~q$r7K7>K?nf zi^&hnr+@($EiC`isU*RI;lRT(c(~HVN&`_Dn

0*Ti?scn!lQ1i{7+ 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

+mQg5gDmS#i zGOjl@883pXgUR5?0GHbtTS{3oxKurAfK^xpnNkwTCVLUZTD}?aP#2u_9LwY?jS}gh zRsBBIjrOQl%dGm40(db-FY9cGW2Xs+Q?M@pUQuv!yL#0iiEU!?)QZ+)x=+b0>!VB~ zYm_%-wyc#iF$%jm?&Vx&Yl#(VO&{S&jr>|9>TD~*(-)G(J4vasot^mgJ`z?=fm0o# zH`W18o_KVFNU~OM=Xf6XHDO({VEYT^zRd*-Jz!(pGbc-4Lwe4tRQP zTgfe~&o|~K*f%y6MLtm8n_-y6$!AObwJAhrV76GW za^0sJhJzc_Q)N5{+a9kFtAYkUOJUntt)UjJ#U}Gz#U|2P@aCfVvZwqH%2O5&uQ5E2 zF#B%1iy<1T0DS^%)!Evg$`2dm_Osx|ar1QmRtiJa>uOQCIy~dX+qiq^^)Rj&2Zr7k zLjtQ3NRKoB;;1eKU2WHN*>PN~09&-{?4SBO{Ej22>gf2581g1=c*UOXdmXQU>s1$v zv#i)m*`neIyj$ZY`*!R2V&yvap&`%X`1o|7%e~9VvgBkD`rS>++{L+G1K54i$oHb6 z_$kQ#VjM>1(4SkQIbrv7by%-kk-hGMWlB7SwkI?4i;MKocDNrEgZ0-c1a3X@-L~*H zRClRzP%;)AlT=XZ0L3*VdiCr;aWUAK*FEKi2*OOCecsFNTCI|4VeqVw<}5q7&|22H zQIO_Zl4TK&v{w%DoQ0n_EviwUIV*G=&!q*%9s_64c$P`~b;SZvIcn{)6Om$rtsK>j zsBS!4t7@IsKz5h-8eJPIh~yYXP3;vbHO}@FrO2h1*tBfJY(=|f9d2ZSJ4$D8YN+;B zQVBb*HO@SULVyQYs7o`;6-dNe9b-F*%&IGOWVYx8%Y5PxK-{@ zv(Mowuv;D3Re=Xb#b*3h8G6|(hykb+@PG8CadjJ!6PEjmwUsA_>wo=mP#j*YaUl;? zl{$JDiu+MRL+)b zsBR_CtHQavk%5U(!nH}YAHRq_Fi~#Su3@^BNyy65zcj8_JXzFxXV0*HOE!zzXvTR5 zO?t6cST}I=4ExH9XV>*!SGhNQ9uHxmniSaLG^N7DAyPwo5d`Px-X4xdNGVJ7h2jpS z>{MZbIrswSh=cEAcGE<1t3nvRU}OdB>F!j!2DWtkW{L zSE`B)7Q=lLe#tBZSg6Y6T0x;+pgbp=Gfj@2VAt^;rktg4e23r`*8ZRu$K>lQg^;q3 z945Sj2{$u(&G2~O{7ln?aiXxUw!lW4PhXnpq`l6)eNsbPHG?9>5F zmAAv~ccs02N0a)w2QDg!?2OboZa&UMjxJpwEb>Rn?}y&Fm-k?g~2LS|v0IKL;p z6ez(*9l}^ZT5VP>%{32J(#ya~oVYG2IS#VZy>{a#aIIDgi*Il&`izWWXi))*;sl$x zcseaItWW^}?vjd< literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/farm/farm_1.json.gz b/src/main/resources/assets/creaturechat/builds/farm/farm_1.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..3698ed106af771375ae66b3dfb79159359535f3d GIT binary patch literal 5680 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 WDAwv^?exiCzWYD!ffS-Ob^rirb?ck} literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/farm/farm_2.json.gz b/src/main/resources/assets/creaturechat/builds/farm/farm_2.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1b6c17614eed07cb5d9ea2931af937b0de299cee GIT binary patch literal 3820 zcmVavMhyy!$h77ALyi-8$}R_Y3x+b!^ZQZ8I!OT8R#? z?eM>E)Uv~W)$qkvzwNGH-rc@^vwOb#@#gM&_iT6b%gtYJ zZom8fZugu9=Q94&@G3$6Z1>GKf4_Zo_xEH-{gC|`0(cK&%gZpr&8qAx7V*f z+WkQAMSp7f3v@`99}izdvX2U{Os4iKkIGv?(K&gykh*s zzrBJn@c4iGAHVE>{5t-4g>R_AY!FX&k= zY%rsN@ixB2L5kI4;VmLuYL$nuicu|18UI>Tq`@zM3xRDVKTumVW{V?OS9oO3flm{X zLQ$F@`j=S`E2^I7T)gaDd=HCM%}PN!XDM|Nmy@^?PP2MzYWyTzPfA`Bs9=XF1lAyY z=J6Rm3Q7iRS+?#m@qnW)?i(B-!CM-)VvR$U>r-Iy5Zt)46>=Hj@-1+AF?c+@OEYQ5 zlDP=g+TnYZ*ipM@=ckQB)hxuv(HP9QNvy z8mbrRa48B}Az9&D6E3755NkNJqS97g@OeV%bl3^mQX1U9c0P~a=s}t=rWL&FBPtnC zuDLcUfn!8rdWQVZmjpajXleXUH*8!72 z=t-9%TtJ_7gBr9B7gf|}FErxCE%AM@49_+ck>?q>#@;dOnwAcz96`ZjFzMT(uyoVH4#k@aFK$R znL%Yb)MMtX?QCX`+wAjWyTD{R1f(=PJ8+~R{0~_eRq!(huhkl$^eF@G3#Ebz?O!q# zN`>Ncz0_p)slR+Va2MFjK=Vu0QXt#FdhDYx8g<$z#r6^4wODNiEzk@ArwD)w9bD`% z$s$k?M;F_T4@wgNf*?j1LW~z;&$b?r%qyhGgp8qa4XTYi zaH_U|>?)7ds^Rclq9J*-8JQ1))YF~na>hMBvn`pff&PJDd#11x2q6y`1X?7m%o9RN zN+zn&+fHiR#B^|Q77pS9vNiT17Jx-|mI%)q>@+qL_Q0$Rk|)09p-wp@4W`-OhsG!r z%g7p$g772%N#VI|M`QyOvwX78`N)|bcMJGHmLrYedqU$wK!r5Y6Zk6B9vS?ynAlXY zAZ>;es$IOU?+?p8G9)&^S&E1`-OHy#WHb;p4aQY1qDI_XuEhX_9Ml^QzKDu@w&9N3 zc!!2V%|a|7l_D5$z}1&BUE;G6g42Znr|iq#5FG!9-A0Yc>^Gkv&fULuFv38l({fZQOUW8+s+KO6 ztw(_28P&evuWTF7eX30M6@CT@f_il7AgdvJ6lB7JaG03LS+E}Qvht{xarXbs4F1nB z9i2K$6j7)c6nnUvO+_KwLWQmM*fN&ZSnZ>e;f-h<;nWJgi{!ajM+xZwB~7Uq#DWLg zj10xKcmL&T^#sEmb`^3)io8}4WJqnuyzPivrqx`dDSSI>LHfP#@_9wQqt=0QlwF9N z745VM6$l$u6ayg%D9aO81F_8JdLco+<*DngusY7T6AEqvXy|dJI^SxChg#rB_~uAw zoF5N|UzqE_8^NByK{86k?!$AWL0MXRYz?Y@g@&I-nd~Tg_})#zax8Ex;;(Xkr#Z50 zhEkT)^WM|0pc9Y@`=+fsWIa;R22(?ZI$;C>@)hW;J(y-D*M)`_r2cTvB0s@G-odNX zdoKf1XttiPmDH24Ma^~w+)S6aWHvrL*D<^y!z31(8XwjMi2rWGz(DMge&YP_q(w)Y zP86<{Q&eao07NQS1PGdos+mXLU_73P$Cc|~pd)?$0LjF`Z!w{U%)~jM6oZJv)0a5g zC`^mFp$ zhh!CbKNHU(BLb(am+HMkSNk&PE;5^1_GO@`Q06napFmcTN(aoqzTKZ4aojVF zrjM{S0UaU;yzJc}&YCXgG;0lxCb^2x+;IEj?{QhRPiTaNaqPeIl! zHE}Np_JdhjPu11|gZD$I?0XNFct1bFx>uTv22i$cmaQP&&eT>ozia|52B^$_dA1?} z)h$*_)&aW$yUraD=@Q0P1$`|tQ5;^S0~UnWwJjD*)*&k?^tB$?tdSkBCY%ZYTY?t% z&{}f^nMa+oTvc5rXsL%QL@9WhsP}s&xejezCk>v4S^?R!S`jOh;yCQer9q0t<_nN< zwhtV+I7hf{pB33OE3jPWg3y)X;pGI~&gMp|WyD^1o=g?VqwZ&C+6MKg8!V&&+BnpP zbjyYxEmw2ZYFdSfA)rANXR85;%fXbkFl2L$cB6FnX@LfApL6}D6%?Zi-u zod6vC5osoj=OHpUS<>qRb+Q&BHvdyc1D&x?2=BN?t21Qt|`=l zV~3&#zPPgZvj0Hjis_=u7JPsor~h1klJj6&T<{nN#z-33 z&C;xA^9}$3MbxNWO2a$m8(qS>9}Kf$0Mr>St&oord~rgMr!Bdm!qA@6_InPqq2c?K z2ES!nku@gP4YDD%Nb(6_)5XZlHXtVfuxN0zGPU8s@dl6(?F7Mo$BEWAMn}$&`w0QZ z&LzJSV{8~olq-Hm!fg%!Kr1zRI<%kv>lPbpVxAlU^cHMQ7pvzuiVD;yL<-6x9v@jv zK54WQo<@(Q4Ibdc#Cd-nJ|{OIm$BtsM=IrrLm0<8VMBzVuJ$ErS$tk{qeb40g~Rd^ zIltf&(|^sBD(>UFuABb1bVE5g;WV2@*1et;I||vY?d>!tOHdWR4;d9e{fsx z)XI2YL#%IbAn=%Be0&%ox!Gc`DuDrd?r}IWZjOdN0t-;7!yAnpk|4HFcsb2v&T*n} zZTptM`M|^nsP#?{L>WN#9?W7-1VC8~fa!*@T&05dE4K>D`ZSY46Xymje+tgbQMy6O6P=SpB1JUNV?y?=iT)S417-5(rDWeb>Dx^Y=-BV?Y1PRFwhDhK=% z-4;|$L{E1cl%uGalAf7pkNoWTR$Sy(q|au#Dhl>^YU&?>0+1}taD05|Xx!a?5#Z+F zc^k|+1)?L#pyzNm;so12;BsdB@v#yxPV@`JK;90S zJm-{1iH71Tx%MI~bN2-)xItHSJ=>PKdxQQXkWrxqb=1W3hWF*&wDB^yP{nnSckYw6 z6B|_W$qlj1_c24P!G)j{y*O&Z3E7z|%a8&b_*@8@N!(ekC1{I-wS;lQe}!D1f;8`C zZkFP$QBgku4jD!f0X2vIWIB!RKrdR}DQJBHL12K4ZeX|=i<33j1ypiz(<*xj`8O^Q zHMug8jmK#tA$2C)wez?9iy48-Ho%q}2GDnAnjHGXY#q6UKrRwH!e!38SCw;TxsxmI zkSlq+;Uz5cI846~woBr4B{bA4>syfd8oETQmOa0T)5gsBoOLjSgtG+KJ~N2PPOFb8H$~U_ofBe(K`^TSmx2enf=U9qTF|YCD_@iF?FJqz1 zMfqm;<^J8n=lk95zjvSR{`P+#KK+E3m7CrBhcEa6Jov}Y_jmE=_nY0XA0Phy>-~Ry z`1I~?Uw5~9v-|q+ulxVm-RAwH|J~j4{?VUzw|2ApeD~r0@e#lJ?e3R{kN5As-2L_O z_UTRD-u?68>tB9NN#6hV13usQg8q%$zxeCjmtQ{IeSH7+mroC0AAkLFzyGmx{9`@L zw|75{&;IcHO}mhvKYhK&Py86@|M(s&g~$KgfBS9!?O*-3_mBNY`y9`8q%~V{Eo7zC zg2ntCFYfTV&++n3DxTm{a+Snb^PAmocmLSkzNy@)Cy8J5@c0gf5$V{p^7Wv?&MQ$7 z)vmfq8gW_bDw(pr=4z1`{I<}5Dto8o#ajh_?~?GiH=Gb!D9JVEeM5U#>BV5Z>{?}PplV*mKvFvM_+1;o z5+(9XnlhP5@(HXrsY70lt?NaNYU0`elTH&+1FUukyY(s~^2Gnu3Kqe!9Vy{mrt1Y@ zR%?!c3IwhmRy*KQxO~Y+Ax?;E-e6lA5)JT_T(YwB991P3ycL!U>yu4KRJp_7)3_0x zL3(N`8(7g?NV8XbbOd;xSrLyAFr2h-cDyE+ldvjCR^7t8a^c>HYU;>*3%CqyPY`aa zN)d}~@r294EmZB?YF*}BGKmo1AJxhq(CU$GZu4-0Qk;1epYjEI?3 z=dfxn#%lNjX;6v`xL6SpvL4}LtFw^$%zE+SdJVuVXJ*SV6&5AMW;+JP2&#DV_M!ZnciUp$sE=L3~0|Ui7n(8ciaj*-ArHQbj#sjSCpxA)irXawS z^@$O?2h=b=Aj5w((;SmzcsdbdsYfnNnYFrDK849M zT<@~A(#5sn1F8y^3GPzi>V~LkOR$ndI)X@mx)yC}XQowa+uil=b*G zDs&FT!(jtD;sriQ6G2l~K$I_J3=<2|WUZ75xddCwB1g}XARb|y-#}9YDvS6G zQ$X~{f-Zz^bckt#sliC)z!w0jB!k!;K_N+GsK_lqF(FKWY{GyxCkxP@g^`UQ&A=)O zyvDv2)kS_a-GWMtw<;BC6yax9a)3XB@)}nLvG;tS?F0vcvzc$WQgk7t$l9T}vcQFE zvgaXX1W+)cEsS>>61ba!CTN9f0S)B?CP74ia;|i9oc^+G;^yZ-A$yQPlsUtF4)H)? z(-PvaH63K`nt0txPUY>`d{d3iwo#-#}y_T0*BI)k$!xD$~$UQ5gqkMw-F4fO_>)pg<_) zLg6Y)wo5Ms>f0A8nhzSn)T5$MW<_u6OguqHn=80igg@OW) z;S543b5t5V;8;Yc?K0F#qr;h^b8rsTHD#laGEfc>(?RrRT^@ikk9UV|FrXXC4&MXR zQ1~-wztF@rgIlFEmH3ciNiq;ASU!{%4e=o3^U#s?qHb}a5Tg^;i%LiEH;_H>yfUk= z0TMz*AMR_(GS>V6NkPTVV!Ecv4O)PdPhfe7%(H#~AmtpL!WwKwOFBe@8oeex?otq5utL}mwQY?p1y+abk_yl2 z$j@Uc9qgUU_8tyQSRiaUDRA>5vf$y%m_TwmSi4y$<1Q9%Mbs<@P?XqGwJ1Je>&?o2 z6m$;32@Z}VtCa>-Xb?QS&yr7|1bZ02YKu-`00M7`Sb#|6)0RoJI4vcE?r>u zkfmLk?l@3I{d}s5%RVn6zbq=Y zKEUaQZnXg_LMrgS)IF}1lxmxdU0e;TN(g^z{0ia=gknu#AYKp4I<2ma33GIIXp3>4 z>p|tH4WuTnOOSb^*R>$DOgbvrLOCD<()yawI#eJt&MhbO^cEJaL2 zUFPhv1S`E9tgEU*aQB>}!UAjy223F|$Wl!0=ul*QQ$u28$kWz<5F8s&cIhg|5MhSI zixOIz7KH*8HUivTA!W3g+kub)4VA<*w^}c{04wLBfa4Cuo2T;h1S$xL#HE?fe`X%+ z8Mo3C;c5jcsvRPzanqcT*p#%s46_DR$eaDp+K19UQgJ; zkKpDp3x*AkU^>q>DH;liqL&(B*6RsaaW2et=Ij^w)nud`z%mk@2=){kEi4x>N%Y%_ z#VM62lAJ)6I*PcrB04mIelhcZ2+>6nu)MVk7f)tDobH8Z2M0WR=$WQN*QW=e-lzKP>sk4oT9 zxrq^mgvfSwxM-2TPsYPm;bM4KwDSjGS~^M9*UY19?j6ysA%Pm@`Lw3MJ;2HpQQo~20(VlGO}mE&MH3k7 z=@}HK$_NXZr!+KPU{x^h3rQd?dRKZmTvx4DOoo$6j(IxKDdXxd^9+XHC!`dz6C4>hp(CQ(d2C$=i5SA(J5 z*{j0p8)D#!&1rZKP3#*KbJyYGgv^@dsPG+=66;USt;3};?Vt)~Dz-0;ZH~*J*3E)iQ8dh#aK*8w1iFZTsZh~}`kR+7#M_bFC~?+igYsL+ z6ht%`Fx5OSDWzml*nu>QLD#5Pj-ZmUH$99EQ6*NX5{lL!Z09l?dIDsju*DoPcvrJh z6*(}^3gGJv7+0~x1Tf5)IEM4*g)UCD2C;@)SXIeMt)y1(#B?Ix~ znB?Z!k%8wy<=VmUegrB`uf8TGC?6nzMcZ_k<7%8^R1Y5?@4w*xT4cOL=#N#|3n2TJ z^|IBdC{CWxQNy)A>N}-1C)R7RJ;tk%y~+KYLpq8MiOJWL!Oy@#4&D>>GsrCZiEZ(w zFOp0z)H$(ci=6`*VizF*#pqB>_$6AzR-8(lz?B+Pr^D$mkMzt9so$4M*3gJy`j6sh zbRkA@fzKda4RzK20BJdo?W&4> z5hGZuf=qD#TIv_#9{Z$ zoTKzxiN<-iEFHi#CrP!$O(--i-Cu0ZNkQj@;9WNv&vB_&kpzFH!#L#x8DgD9iNAOp z;2+Drxr@K@7MCQL-LDL3vnuNvaB*dXHD^N9JNy#cqR6@emcFSScIfawdA;? zDDV#)h(iu&9iy3W+$A%;h2>(-I4c6b0Jy6bt$0pIoJa%gnPf3>QWW1HIL#!c(?@I0 zfo*3^h(wQBT#ox|EH5U_)xf3&eL1ov9osU0H)swGu;#Kd{%*)9Jmb}^SvyGOG0KW} zCFYCHHUH!?Y9n?TzK+2&k})F8llO|3w-L1vxT(u`*-6ql3Bxu%vJCG*LuZG$cD z_Tb7#eFt-7mGv5BEi#bU>PI9ffxd51vR;Gh4aFs-5QInSNhHsvYM-&De!o5NbpN6s zX%vo2yIy$Lt5UKv>BSg_nA&KM@uxOV88^t7d) zT@LM~7tOr`toBaEtxjS}$2qE(NPYe6wk+aes68x9@;C}3*+y82+Y1|>+T!QV3oO#J zX*S2Fj}sE%vYe|g)~G_c0DEz-7MDE@z-lyda&32Iv`n_Bone{cNO+uSX4mB!P$D9Y zE3)Efc^#%p5&SE%V-v^v+2A$zH4Jr7?QdDi#Y@%7vIr}?R4 zj^U#;i(waOrek5k9c#>!V=>}RNM9})6b|vLq;YNEP@aw3KdhbJ+(*{+7a(%f@ifXe zs#VSMGpMX$A>n^<^B4~|e%81`hg~+rHMYLAzsn`=;feb<7WbkQ*eYh`S>hO>KL^Wd z{M!&~xY&63is8$WO2T?bf63wWYN~|D1{M=Sy9y8IqAA4*@@5bu$92nPe);Y-sJQQd z#7zjw)s+@n;&FnUOk5H+p`nR z5ZB!1aKT|VxO8Go1A0R%Vj*Bd1oC+AXh+Ur#q_sVby10%cAHC-HH3Imy9U8l1o};` ze?O1QkP`dESh1Ws%(}+O6xF>$9+j>)`4<~5fmJPTOsv?i!2+9eSc&_)T*vBgLLk}T xn#Q%bVzgyCgX>$I`dtUB#NmUs%!4?%!TGSwVMP#B{rwL={6CvDS2DF{004h0>lpw5 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/farm/farm_4.json.gz b/src/main/resources/assets/creaturechat/builds/farm/farm_4.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..f2fb46ee2970d30a5235d3818dbb289848160754 GIT binary patch literal 12342 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/house/2/ghast_test_2.json.gz b/src/main/resources/assets/creaturechat/builds/house/2/ghast_test_2.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..2ec822f692ccb8f0ad89176711ad929f171a96b5 GIT binary patch literal 134090 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

4(&5y2GWfge-i(JBz?}mVntoeep#ES^=<;Fu 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

Y`^*{TI^EH3S%e~44D4aTbC?P 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/house/2/testing2026.json.gz b/src/main/resources/assets/creaturechat/builds/house/2/testing2026.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..7ea383545309902602e26493ff60c644a43fff3d GIT binary patch literal 54080 zcmV(}K+wM*iwFP!00000|Kz>fk|f8urFXB#z%@F|&j(!UN_qkvsIhGz*(6~b8-ReO zarTVOyK8+OnGqftkyY6lUR5m#Btf9Ns&kbee*cea{po-D@8A9SmoGp4`pb7eeD@!p zzx?>!_uqZ~&(HtM=b!%mk1yZ-V5j6v`zz#Be75QP@BaMf|Lv#0e)-3DKbR@azvp}j zw!~cd&GlPzZGT1QZE_{J@4x%~^Iv}Y?elj({P*vE{qf)b?T;@%{`C7F-~ABvhw|^g z{ruxE|E_-zasKx|e*W>#-~Hg{fBEaLzyAK+4{rXKpMU-PcR%RU{{Hiif9M;me)-F< zzyC#;?DyaO>&Kse{|2F^YpMU-G z<=6lC{*QnB_}l0CSN{0&`;V;Z_y79w%je(q$$$U+*ZFt;_T$fgeBOUHOZTTQ|9SoX z%b)nW${`GmYhHw7~BX;T4qj37i z*zP2M+@s&hg?kCw&%gfb^Y7pPL%)6b!+&qce*Nw9FWsN}{cn8P$&dQyZ@<0QN4@$} zefj;L@AN}G^htmJT^8`){){>M(;vV5`mg`I`<1gryhYh8CKK1jHaC+N+{`|LhI?GRe()z~jOw@Qt!;buG zt9%UPzV}mp`}L2{`tWw@|JT3D5z#;Y*ZKecbN;{o*8cCWazpgM|Hki4u9P4* z#F{SeRt7U{`3XE!l%iG zTI%z-+UK7K7N=BWbt|x_KhOFwV8ODnE|_Ywcg80hcd(pwP6(`(YZx3LuBg&fqOV~^ z6{cEkaq%=PJDFUyT;AEF#i-fsV3}Hd%+Vy1ovk+c6<3)`(K7eAtp2E+2H>*R_)v3> z!D&hC0j%JoR>8ZV-#2kV6?C|K&fRM1i?clL5v%nyD(`gcEJy8v%GD`UYpnl3RH05f zlKlI1sf1E@Cg+a?q6J z9+oe9p8H*tt?(rm^TD`eM{6~Cy@?@B+SgD<7Tv#lmT?s$_`|6>&^&Q4stAVaXuQoGI8}AvXkwUPtn8D zt*!&yEIUP`HP9|;@x7UylN($$X+2GF`XYT*{dRB_S*|GC>5HsF=ar_aQ(j{1kCz-= z{Tw53IXTSH)+A3(KIZMQ|I|E0W2~wAN^fgfVaff|ujW@_{rux!j&Ruxu1|gj<`!VG z<+|HieH(No^B!0*cu+aYJCM&3^9#uGq*HJF2|Kai(nslG(cv#9YsGFKA>oQK#8{)L zzTl?!u%a{lb(9aN!x+kdeM~Ou>?*bhR`zgr9l`BFmWMARCzpOjl@)o!Y}_}7N;_6V z_cCe=vp4b9iGTTQ+3nf#=V29H2cLa^=1V$ptn9Z3X~5=%Nxos+Y@Ut!&j=S@hEyht z&$FS}avQJ;maqDx4 z;_5sIL-5|e0Vp}?`cyJ2QOCrFJ*rfao}4Pn9*pAC`~s@u`}a&Hr0VU}p^4t=b7S0D z(3TytT177e!q*P>6_=B7@^5jn%%l&^!N{S>DLdnDgC61X5vR_`;bGZwOt-FS=P~OJ zgjmvfD=J`K(XY`fXp3IMd;~1`kPS4OA~~5VH&M=r9OIhmfp;p$HF}-CnDvp_X7>Ux zH|bLQ^Ui$B@hMnN@{8qh)#P^8OJ8EF(>ChvT7}lxbt|T=3^s3GYPmf&jYvMIKzY zUVf@6l_3izXCmoOm*1)l$oU|bD(BhCRBB-q0<_2dKCHwSJrnlZO>|9_tXT59RnDI& zeqWB#YnNgQh9FzPM_!^1uW#1dUCQ?ZU9c6Gv$ZIsP^cBH-=({Rt3;QI%q>fr*Cd>) z_?6Xmp5d^p5-zJ^Fs+r`u;Oyr=jpXjLW7T{Hz9`d0<2M&QE`VJSVtZ{%jo#DU#v8@ zvXMfmCNHs&%kltIY~_XvD6M>PHr}q)Lt+2rY?=3i_0(edqPS2le$vqi{$W%)@4*pE z_NWM3M!Y`?(UQW!!Y#}5UB3a9b9AQY0?N^jt6QJt3Q`$bU&TBXMZG6V`>R*uOf*5! ze+pGgQTN6nVo2p>1Sj9VtQoSYS>#X18(KaWZfN0D6-8 z91d_PMhr%f{$eZ2N`*W)Oxli;t4Z#)3x9Jb#ZS-?NX9uhqc6rxyh=j75{5s{_ zH{1xA_j=8S?4;4F=z`61L5*I&gKC})8Q4_|o0ARh?Qv1ZNmULZ3Db~_KZrFfRa?E= zR=X@qQqsG?D)QLnK9!`Hs`y>&+y7T^jkEVBfK~Zse$(kPFfDNkSWqzHLwFNlUiUv# z875=sfY_I@2^=$}cfz+2{icu6PE~Jf_jczgVh2a0;)>!g8FjtdDIMg;6*C&yH>ama z)+D+YKn*-#)v^Tgm1$-%=_oyzbHKyVC%RZn**X+q6J%2)EF+@X6R=u;SKM^q3`^mR zgf*~=HW1aIisMO8u2!?2_W_CeG60rtox<7J!>TsA5vJi3D)|)E4@5O$trR1M6gsDV zU?+*ZEf<`uyxdeeZ}zaf-2R$EELO)R*tNoL6kEjn2It*QJSagcPZyj#)B^|A%Vg6% zRa142$Yia|y2;kQ@kX0Sk$1#u$whK>K~!y}?$E(I{Q|C5G45jjK%ak+Upb;{P`gMo zprV|dcNrW?|EAKqpw&qf>DiAOnveF&gr_di{T}!=BJWf@hMRPEjShjxV)o?8uXPq_v@5G(I)!p+`!ec zS5(@DOvScDJ)<#w1t_nR8~_S$+9hRCf+meCc56Pm;q>Wl~c$vHAm8A+!+6GP&W> z+FrGXC0{M+MxQVac0m=JQP4ISUxPhB<>j^o=RLP|ttDqgZrOC9NRgjc+yE2Lq_Qz` z=E=(ye`FbS)@UEq%VYHgVF^U^M(ZarwtEfJq$@cfr*i_8*Gn4m_E?#m2Kb66K;b6P zWwe5lT84A?+izhqOthYGI-Y+=NakqazL0%pv?a}#n4woRt`UDx2&>ae@w_6KVKC`JRL zENCIQRnvi8X9$yENSGAd+OCSAC^Gbo_NmHJ1b5zsM!oT=7VSf=3Svt=fJFttWX~h6 zFZ63Sua!Hc{q2mXn@kwck0VraYH3Tdit`mAehsQNncCO?Q`RqxNKl-BML%z_rJE0m z@*wFcJnvtff#r%Emryo|LQ>-&yHywR~ zx7<-$B*O48Ajx|Mm90~(wma*328%BvJiezvRR;w}>fXRSS(ugvTt6=hXvt~^0_THB zzGOR3d{u=$x!z5C2$PJ8eXylCZR5c0B(Y4DB>`XLB;JS=p21 z`q<79t&@JZ^J!9nA|0yzF-8ZZ9)y84q?QSEgr?bUKowcv&RZ}Ooe5=#Syv=%oR*{L zlkRC)S;FS3;Z7=Q5W7=F*)-=de$}|8a(bGRHzT5(O2J<1E=y&TJ+SNaTgoeU z-V|TUS`&8UvtsCj8^_PMn_;<=OuI3nc4M#7WSmvNRdZ03Q(f5O@>-@G+w`+wt*d3G zNX@^4<)@@5_r$^S3n&G|B~;3$a_kP?8w<@w9kezKBd=BP6O{? zuxU-wl1>oR-0z^*0e!HjnT~L|D=x<#>I13NgnjOqfmEF<+3{#7r7!1-x~iF}U0wwB z^@C)>@nGxo$$0uT&%6)dPSu}R6F{Ziz9RX83a`{(Zi`BCuMK1=$NP4 zMK6hs$wuc^RMyz)y4G}B)fFNQsCJ#e1+$@Z9XF-!pf+_trQ`&tNDJ(R%V^VSFyazE zkBVlpT+%lMD%Xi-n~ftQmmz;BsKUus4AKRtMe(lN#S-s}iuRS<1rjmB=v2cuaH>)l zN?dX==N*|7sNgY_eYN2&Q0W;Aim0-;rY^I%3o2UU;gtz`g-r_=mt0z!0-5mAgQ7#E zjyPAwMmb5DIjFLK1exTds;y5`Kvne0Em_AY$UE(3?Jl9jfJzrLL#~>t`(1+B)hTIg zbESA4;$pQ|;rcL_sST@liI~}_4~TA_g(UC1hET&Uz3_2}bj0C`{FW9ePE0>^8SL33 zjvu&6fr;Q!za>bDNG;7*-wq@$TyXv2{%Az_b+WJD&>C6o+Ll#Lkoe8rn`M-D6GUaYhKz$JziSqp>6N?Qc?H1% z>eJ_v!R{s*tZfl6R7py<`{ET$ODWhy&z52u2pqo}*fZ&1UR0lK^|YG0gOJlzIRPaG zSCLX#nU3#Fd5S}R?FzSth87;^YfREE-tuU)lPi)EuX zL$2YouzZ~$XzO+`Pfo$~h^4YsmrGG~OOWpq`cQ%>;)SOX-uRTgU3~M5(dg7$Vwx%? zo)HEs>DH&p-M7Wv7C&HG3zc3%0fK4ux_tyyP~@zTD#e}jXgYt}biL*8dV5e$mDG4o zh+3#+(4oZ1xwdAT-fwqX1AS-=YU`W}Qw*VT6*A|L z_Ujawxr|UuCGS_qr4)p~{H_n^3|-NRDa4{jmUYN_MsFRLcLlXf&oDaEL}5sm9PFLk z6gQKG$&O!-Q=ur^7xd;_#!~~G+nTzcl4W`ggUF=V?OO*TEsaY!QNic6ZpQd->DoFk z?eQm)zRNI{(`FSdIA=H?o^P)^sl9_0Ca-OQj)~%$20~i4&9-DbTc-=Z>#M$x32M>Y zQH@?)y7MmzkO*sO*4KOD>#axd0;U8nYuBcD@dpNkRg6RWD3_?vO-ldW{-F~X1kvb) zFlp&Kcw~R|4PPmDIR@r`}S$qfw!%>QWP;%4DuD_>s zf01JFrR!?EsA>%0GGp+uk-lkl^f$BXT@tEH0t-JuYKCB|;8F480UgJCu1mM)6E@Z1csW*)&; z8|ead_GmsaFe-?clQXjr+=?pb)^0jq5Re)2HaXcC@8KYh3q$c{J~6h&k%%dlL0du$=Or~T!)k=@L!A4k_fL;D(MV^lW}VmpeR?Ng z&0{RxBo=W#V&7}KR|^adsQj6ZV2egagQ6uy;CkOfDu42u4r1ZB$Jo}u_FsN9G?yG++Cd|w6>njE9v#TaX??BBthFwbz5a~EoE$`p%S zM(Zus`tBqaM}UG`uPTxVOtI~%gso6-EotaXo>DIHHFkzi)X7+FvVt(ljxYfKfMTlOudCc{B-H}O}r(s!>WgDuAo9L?s_)0$( zn1}qfcFpSpL!A?_Vwjjh-%XDUXt+3st4^|bU3=ev3~w2LE7@ri8qWPG+Q+X-(qDzi zZ&@eb0oXh5V+s?33%9f1c?cKrF)qi6_8zqzOP60M55df~uMM|hm$$^IS6t7c%lHwl zrFdV17}2urX+;}1!>(KTv6E*XI(cshbL3hEJ4@uj22}GC+kN4N%x?$M-!Lir2S6t3 zAa#S%Cz-%ETuHe<8IDMkQkh$CUISSt^i)E>-Cho6e!F+gJz{q<3S*{I@z$0rzB2VK z&*5XZQ z7=e#h!N$#@ugu@bS!-1A#(g!erwGv)>w`fkq&&|;-J;Sj%ZTU&(#1&Tg)OAul?RNQ zMj4u>8HDD7u}7vfAnpRaUT)o^z5`WV-?%W$FBB-b&m??l^RwpQ4$3asfIx&oEE%ok zJ&dUK){eO9+_}1?h_bvnS`X{0UfzSnq7C0t@35}%9WNya?O;(P=X=|rs~k>j;W8BD zn*s!4)Y&YYgd6VxO;x=*GV$51y_yiUp<&&>Jcb+VAMD!RuGZE#qzRe86p}ASZ?!pq zC3mOB#I%xpGMld%P!z?H^6R4Y3z7JFla-JHQ#>xyKzo?GwOGh68!rQ|moGx)xKQTn zIilCKQ^`ILY8iA1bkrsJo{gjN)-TnbB;1Nf!Z^6Ptdx~Y3&q!T2#GlF9k|dK)%3V6EbASnlB$;%9+}j0 zZc+S6$i?=}$nzXW8bSe)7z405da8D}@17h1gLsIML8z8Q-4#-qnzwVyMt@tJdB`E& z&*8l(rl;JNgj1=Hv}w^j=@ID=6^jaQqUCc16p~`X-6Om!6+IMY*&A0Fxiq>TpjT>g zlp9cTLJa!tMvJAVIxd+7S#8o(Bje=74|bVWEmX_pBxSdUgnVX*t&(h4>cO1!ou$@+ zkk+X;Elsx7Seb(0Zx5421D^0D(kX6CUZ&m@O2rL*&&;0ubyIJL2mH-3J(EpHCD;sg zWJ%5+>|9`AL0);n%UVRYE47x`vFKfrv*$emkIWdO8?L5AfB9{wU=jCr&m*^^Y$iVih?p%Q#nF zWKMca&eIxi8X1FI#+Kgz)7nhp6266_U#$28ObSerQ}9<60YQR$(aW1qv&;52-V`WP z9C=O!qy_c31=O6G+3+ebLX$@Hkf45;GqHb&*5&JJN~6nEtY=mHYzLP?+ROj>3N7>NoLB34kXdw+!J5gA?7E%d-cmAp^-G^Q>J zI91Of49=^fuMUwsn4Vgp^(NUfqU{STPE}@RObb%m(DbcW*9c)PBX~$lb^{n`DyHup zNp?!ed7gJz1Smkt2fPNG# z3r^3GUpLXa+wdjmY`Fo=l)Cvm9)(!QFj8@VO24IeE=6s$+$!6nYJ#CXD(bf*M9Ag4 zQQn5(LH^5aLLX4JW}Ryq9^^iCpnMBqc7}aG_;~_VbQ2AoJ5I#&&&|`IIO{2fo6&hVG=xz&;(Sum_fdf(nt;(N+w{=;n7V_18+-RnC zKfit0ZCQfmFN8uJXzwpry^t9nCUJI8b= z&{o*L%K99rhZ1JfHfQ1rNmjn4&)0RLa(doX;JWLeFyvg}?o9inMlg(S(~B;Dp{`Ck zl=8kGej2!WuYy9}p+Fr)4|-i_y<|Bv6`Cz~&X``cN0zE64D(itkW=d9!$X>a{07_K zJZApS`&7?^ss*L1PGA;#cf$sLEGWgE-Mhz_xeIkbMv*7P24<2K+&W)&6-P?OS^$Jk zay6lL8cfDayicoB9AwY4%>zO$87la(;1Vm12JI*j##+VU0Q5xeXA6-XXH4at# zgAi%es|!t3ImmIye^*FM)mmQYQbzlP=AYcwHbt}I@=4}+2rL=&7UjkS9n|BnJQQJd zJ8U~}wVs6)4dTURH4X3(>A5*yX*h+d!a$Zwe-F{mInzyB&cl)$6+qYPRpcG-VdaS; zZWl?k36S%%6{HtRYFVHH1^?b_SEL1-U7=#B-HiY5!ZgmuI)e%&JktmAm*{jzApbO~ z%Td1-6!>^pF~v^v=o>gz4&kBD%R`+%R#$$9Gq50M{s9 zpon|sr0X3<@o_~p7bHgb$IpVQ%*ofTIennB;A-`BzC%N3jw+^#Lz|Q(9jI$#dYJTG zZ{xlgIWDD9OWP6-(nBVhR$R04jA_d5sf-BEIW=TW>HSgp#9 z2VvzVgw1GyRxtJx<#dY=sVu0q(38@6#2rxc_1n^B>E$s4MggH= ztc1lx*%(OAZ;cHR3j>oxWn z_6UXjh?W!P7>Mgaf$hc2s;pa3`W?DvlTJ%$!&|HUG+yi0(U%N-k~t@+;h-#C&F$@T zQ|l|tOiIa&me^%O=e*;Fcd(#-IU?UdGlF~t8qi9xyLK2y+amg`x~6bRtXY^j?U=e= z)S<9on{G8qhN+KWp>^(zXMogpx=4)Mo7A1Fp!ZTP=!j(Kv!Ha{bPv*7Jj>66q76Ls zp8Cv^I)D?trS3}g2&mhL2=@+?_ z@+EW!s65Mq37c{fz1aCJP+8jC(X^5`Ad|E=R&-dA)X*hFd8o5<8A2h)k(UfbkGR0L^)(_7*k5|-|uuehX`YYoA; zfP`xY3ylvwi<{y6arHM?5jA|}%@%AwmuQ*aUoRUeq%!R&TIO zI{4Y1;x8Wo>SmqZ;qDzPGknFuy*WmIEY0vW_wpUq@mQGQYd|(`r#H3!U%S_FA_dkX zq|E)wu!)-y+8Lqyz8^MmukAFgZ3+dGsq$GLTEzzte*F~qz@vDJPXVeu-v@DjkK^l* zT!^`jTeB6C)v7>0RQQ0hLzL083^a{pH%C#MoWN$3ko>|BZ#GB7N93}`LPtT`p`zCu z^*mclkW;-$2_WOpd5u}Co_C6MPx;u^!$yn1G2J5e1{LzTwNhH3H{bN>lQ{X@Q&nI3 zUC$S!09B0jGG5xenA7c%f~hRK!dzaW-qa_>Gv)qnfNz$GbOT8LPr<5_4La>z&vNU| zw`oYK_i0=qOdqk;%b=xwg z3AXx>DQ=*67G3y$XX68dCpKGK3% zqB2za9Zl;DZ7*H0#WDS0YQKdjRgXAV4}daS0Jd3QWaDxTbDj`P=?cZN7K|;wMxuQf z0+|i(#u->?3Npc6`_}ozvfm`Cx7fl~l^tQ-pT<-yym)5Q|6{TbL#)-)pxOo8Op`a+ z>JP=lh?wmv#fi8mjnE=EfvW0zyPEnu#F}3-35U+8*-&u2Ii@F<$s^*NaQ#BK4eI6O zWa~?;@UAOZ&OIdhAvQ4%v@b(yF5V@}$S-b;e7pTZkx0{rIYsNt8LUx#4d}o2#m~m& zkO#CMz*!^5Bp8^Xops*i9WL!$P63HrA8PbHEULBs)+P*~a}X?g^r28J&_|nkyIh|d zr)hSM0Yq7BBrzQXz)VOEO##6y6u8xnR10LGa1MV(%oI0J-#7( z_&TUsObaZs|3z*Uf`rMXm(+&n_Q!-eda{uZ*L|GZkSi(lhAK*Mod=hl%ENEob8M zbX&v1j7-Zl)H<7q6*;(h0Lw~4WnyW9FwlYT?qTaAj-Et_socBeUDVQ~dONV zVA1oyJmoHd>l5M?8}+q0uRMftjLvdxjzg#>f9ME) zC@3`GEwfeM6sUsmS=xpMr#5=>kw#0XRvQxPH*z%OYE97cb?#I)a%x{Y!r8wE^04drWsC`#yfx6J5g^$Cy}S28 zn%7`C*!q0z2eO;^xqG|Y@c=3>t6?{ET&?tPKXj>xwqx-famz)l=&7d9>t(R-hwB+P z=*$)x-!n6#HBkrD9UGq-q=|I)3)TUoHg<|ot1hQov*U_9P`6JTYlf=mh+`FWjcl?( z7My0(J*d*E#zya@E2TZ;mjqau9BdLBCS3|bBm9k|hYndA1Q6sslv`NH5~KB<3|ey~ ztPY2lFXC-iK+^^mUxwtGpwTyoCqlI0@&PJ~hMA>_xLCg`Qp#F+y63y5{<)L*G$;fn z6lghgh26n9_o!^sy&>-=TDMZSWC;IVKgT-|5$9ZP7G=vt=9L`~q&vI8b+;5uNCKH#hBBjXV$gXEM& z+LQT#YhdKi4n(4(hs8L?2q|6 z6kFd<>M_YQ`CGcVT|V%|GoIcF)z_@lNlZ?0xK|jv8Jf`-4&dc``j9vOw@Wz;eOF#a zWLl@GWt;}Z_^YcK!^9Y6Kc_j$dwLC%9L*SHo8>r_N_)%IAx}lV?k3%Ullk`B)*^(A zQmvzVq%JKO#b`rqmz2DSF2DWDOC*kV6wYy28VX9LS!5HQN4ig6dF9f8SMClVrCRqYlK#8NK9Q|Mz@@{I~hX$9*T zwk}i;LzEtU%Wu|%fQ37VGNTY1TJe)lH7t-I2-dQ0m>p=@I3Hu8-LB9B!D(hZk9ICb z!kBE4g12j{%;o-fn5+U9_~J)^@=OatYzGCmeHavlks8A$7%z+@4!~5hGc@f$$dRw) zycHcOWR_Q?=xkN}ArxaB$9}YgtajAVXN%uYA&O`Ov3$ zntJF6GF&Z1QV^bMj=6?#U%kT&_uja5ptf4$VYJ=vs5x%|uw&wOMouNnV}q?bc_?H?c{lJkU9@ zETknP1Tm*!|6O-2N@FwgHkyv0@6tWC-qqr#tM1@QSqDh zKdeXxYUWuvjS1fzeoV*NFU#;ikIA^KTj=4133EAY!4xuc-){iQ=@#nRBu1VVz26m- zZSAmn-_aBdo%epNmiOj%*s=1MA5`s={-)sSIwaQP;vLo70LJ+L+zy?GD1!MX;j#z?6)iB^^OhTt@f=rMulFlP{tpOoVn4S3sxkACgWVI{i!|2dvAGCJNtGM(opybcH*xFg- z_}tR`Hc-J;h9nCXuj6`XVtPH*{Y58aK##%mu&NHLDP3dkCEtGDik-mJbP{XdgWkAn zy*Yn3&un1A!7Q5rT)i9v-2OA5W?HTIO@VR-ExVp>WuC_>1Ek^3!OE`7xm{>YUh5OS zfJ!qcyNx~U8!Xm^G3U%cmLCC2;bq?_d>+-Zg^{OPpXqIo4|=Ph04gNfikvA3q4JVB z`BWA7B-_hI&OAN?SdR!z5t1O}_paJqOPk(6uXkPVjn;}f(kkrGI2<>_noM;MXi>yb zD*8-4h5UD?dJU+ihmCxrC%3XJ)kW{DSD|UsYxY$litT~FNul~Hu#Bn-1CPaqvXh*6 z30Me|Hntm59$+2p3M*z~VSk8lWPA;*#y{3=<4narf`9A`EGG|Mu}9Y|JS(LR5EzS7 z2{781T8i8{cW@EWHbNg1W~;hFbjg~WPxdBSwiTheHafkRGx;<#mwJQv zyh7@{&bAhr^witZYp8MFpGh=Q=nr?K=;SdSEf$JBjsGsowO*S`y-{cpOyAYeH2%T> z3n5s}mY8IlBAPR|T>LM0w0xIIE zTi4~b7)gv|8b_e2q*g08O~K58iFc9yt41M|^r6dp7^bSYtIPSiI^}W@Q#@o$w_|k) z`jwCsvdG$ad-O_M2knm*RIpsvhaNaaCU$8EZ7MJ38CSW3C9hjIFbBHOcL&}~wT4XE z%fh>`TtZKq1u^6bN*nQ2Yir6aLyaAfuqRS6k;GOak-ZkCHu?HeR*V5A z3I;T4gy&~z5=$gcx^&>&xwd<$Z%@5FI`w8VTKDc4$1US=wq6KbgzW*{D4F7VWk2zL zDte>3SE-uGJhOt=VXaq|@GbBSJcKJA$eVb;YYweL_}Uv`r!Vcar#%G%A#h z^#Btk&(_21rXh3km;0X1o_jYZNST7yaB>r}zfz#FU5bN0*>%O_QhUWM?7C84M+F z(#yB_}UJhI>CjO1BNn%7RxN~-0#*gS9#ZT zGY2p#fGX&9mumnoz1j8MV8oqXTJ3Mfnzv-h6_pIE4R3_Xh0ODdZx9Yp$!iPf)G=8E zOZ^K=HE#c2!IU77YHEsEQP`q?3*D=2Z8ld-`cgRBhGdKef~rk1=L11Oz%IYC%Ql>M zRz`rbNS%LEM~cqE+669hHJzN555**>&X!WXsS_no1@4~s+Dbj-lV8Ko`fjkkzVvkh z6jH^xn~XcPRBjL|daLyy_sXf~FQ7e-ifOn$SRkYc{<1gc1RDfHwsmLf_Wpv^$+ofWbuY@x&a z+P=0y70ClR5{%ZjjM{|{D#&N{9b5f9RF5TO-OV04k4T_a#24d_a-KBA>$1VNZ^~M^ zd>q#^Pa&=Ej;2I8k+$qc9q7SVAe{?r)P-_Rhsu~f8I%+V(M&mD3w3S+s*9zvm%Bj~ z7>1I8fJvLIKyhgZwo%93&qeel9mF`>h&Y1Sj{zD|J7qt9VW&1-U`+BYj$dRKulA}d z#Odl-x;IATtOdu=x}nf{p8UQ+RhmSYsWJk-__$h8uBYkZct;l7517c}P-)vdT*1gV z9?bMOlN5p?Q5k$(T8n6e3~=VIhss8U(7Mhe%#}sYs>ouIhw%rfFlO@aiV!%r^jTC= zK(-5*tz6THX4eQzrf6r_o{*a8{BF`nrqjqZaLqU@{Pa54)oe5@8aurmpt+;Ni8?IP zGN#6*kh&c-B5&;oJJEUbD=6dJ*vFQBV8lAvWZHO4b~&4ay_5UP5RdFDS?w#JGLhk0 z@w5)s0UYb}9iSVKz05(jZbyvLrV?YK$YmkaUlLV@-hghD_A;I`dS9ieCU~9TdNn*s?Q=Fh( z`WXH`)D3BUT6cH70h#&0z$C1fIfq;GU4sKfI9moxxkgk1^{|ENd54QX7;%y-YK!bhjMP?_MxN*=Dk7Z_ zarRTFR5;%UR?BLKgf2s;?v!*RA$D9*&1`h_IVKetW8JO-0$nU^k}1 zyc?|d-N1zZ-kqv6QFQvqLv_intuASClP!LLyxKiX@vfmiM!5Kbw=d;x{^ZVUbU9+- z@HM)R(goR}Y#2!o^F+tXDD9-jwb#A52lMM+rTZWaEhs_1#5i+_4=&|;0E0ZZIDqH) z1%$61z(}WZ*9kI9*7*V9BfZ)sH;!DCF47UC8lZv6O>l2%FufFfY?s2}SE3uB=gb5l ze93K=4@Tajm9oK;B+y%FV_XD5_G*eGq`kjOI$lSDbQ6^ zm$r$eGNAQ5Ox4p69}0EVnD>W@9xz3PNf%YSRr6AuzVI*DH$la`xXMD*a>~12nk4}H@9@!V@?Yv!@O$;8X#@l#2WNjh0(Ee zeG)bd1C@+wg0RQCPCm{uMA5VI4Lfx7;>xerJq<9T90X6=W18>^9gW5NZU8UKdviUJ z8cndeRYRuc70qQCRhZJQbUYh=hyWpvm;YeeMO{*_Ws=zrU6#6Xtw#8ipsL=wCYYr= zdazbRybPToq-EDON?i{~WIE-E3&qobS;YrX$>!=6(~}+|&%}bt>Zo7ReM?A?U^jZj zwY@Ny`NQbRy#cH_dOFY4`E7wZzkx79*wRr%Tw?O`F7+Ky@qQZb7$UNXXk~VRi5%mY zcjd@Cd`kW>eDbe=yvL_>4`gO2FLd2ZrBJUNFz$I5;@yUUEKE1-Jm=2_%%l3L2{<}E(vGRNdJw3%$Or}l*a}vb-dSWGGA zVp)ci=&-DI=4`r+2_khJc_W^qXKlAu3BA+)wd4U95Y<8Ff{+*STq`>7Q4v^G20Dp; zQz!Qw5h0Hq$&*|Qz3FMl!BQ-eGnG(9#3&?NMfHs)i}wE>E^E+ZNvalvy-TFDZ5^xnR8$_BztT4>-T*x(T(AE zLvSe;jvFQUY#^r};9i`7r7+LuegJeHqW{P}tPldl++D&sVY8ZHP`Hx z?S!jy$Zi@75nVvY`_ck>)4)@q$TBHcM*U0NNRY$Q(KQ|F7mOQ_SM($YDieN@u%3K>CZ2Xk*v zwqdoDU2963Aa^t)IJ!{f@z88|`Br=J7gPb|lzH#va*+>Omy7n*+>f5h%vQwcN6>Ud z_*>*qHOcRzHmpqEuxa9C+06JNs#JBYMr zvSiG4eJ7pyF7mPTp?X(F6jW`5ebO2vmco@>W`n9Uqs5$~R%>A<;k+v%TJ5{76CA1o z_{}3l1t!_DRv|zGbbzYzmg{;|naM^Je@#XgdZ4<@gh!)KFW3k+B?a|5=n7aMU^z1I zG5}T|RcJjVtG>d5;>Guq0rRxpknRu2ZYqpPvflQ{ltPzWV)aIEOe`I&B%`HQ^p;*G z4v-V2tIvgUAMv4tCMH zU9%rYi}`)s!D!5!qPXM=>DP%}rQ2n_(bUREu*>*Hwhe_MbF&90J|x<=^-_jLN_2%) zAy?aYG=6Ffup?S+J)M{|mFs-XtxLC{GVU^Bb~es~FV_ORT#a5UdMGMG8A0#8N#W=c zvOuArNLvtzUs`P$nb4NMdaQQpJ}*uUtu6TS9CE21_Md#vZYy9qP6eq?`B`C2*E|gD zxp6ew=(Jo(du7dDCs7AJ@VqdG&Cy?qE{CvUjb#Aw2((DL@)5DbnvFepcXX^E>+=a( zc)Ls(ldW;FClB)!N%a=Q$9C~}oT+Qwsh-9T{h%rDon^%q1y=P)92r8e%X?_)d+|L* zq@8>^Cn2}=>gY(*vOulYrK4dOIZlw>6g`+GERq5WF$gpBE{Qax7ZQSN_@(IKu=Zf7 zio(|nuZ*k~FVJG&Bhx)~7qZ3?VTB%5SE1sEGjsnKY6C+P5NKVRp<~lz=tS;k?zArF zp_37y{H!40AQm_x|KYSD^ljihOC@~EpcEiY$v5JimG2|SmpE_S&w$eN97^4!ypg|o zkb~sKk@w4GKoZv9t|A;B52qJVSO(MzengE8s^QEDsZO zMrqo~V>wLxp-CHB5KmK8*Qr4*v>V-HRA%23@eC;AsrZJBq{nO&Z=XRuI0~(uB2~qX zv0Et|2tOW^M`TcOGql+eA+>{7kZ}~7Y?5P9d>4D!*6P=Rau7`pmmXZBUJtW7 z+&#sE`X6E@x(>^bNn^ICc2jere{V<3hiv2Nf!-Qst9xi-;kVwtCR`KARBXGiD0=c(KZ>5EhQBg#9s1WQ* zJGu3T*fzHBGFmu{%gR%ywQ?6W8&u*e(?Ed)h%fGIL4AD);ik-Vqu~j0&g~NnMqzb~ zn4cJtgR!gaoHAxA_9q?gz7hIv#3l(cT1J}pdfg1%&>U{tW90k12W@#)*S4z|&P?E8 zK=&O#GJ_hj*Va_KGwP&1vtkUQnuv_?D&iLMq$SL~1s(PPvWa9qij0FxWoNAzy4EGI zflhJ+I+wJl>_F5_upNnuJQ)4bfWjC3ugqIRDPmUI^bS=GbNgf><8OW6JVQluLzhB> zB3T=&b&jaxAux`JG|B{!BLraxSFdXyRJ>&C>zr$JfLzZRIWhNP*F!K`=zq1 zhskuO*Az=$l=qk|CdtutKha^I2b2I#fQplq^|fz;DUxc}Ij20a>zSu2q5sIoGq?*& zLfgYE80}{7=tLjVBqo=bHY}v4^Nx1FPC@=%%~XJOHy7?eD_4c&Y3LWJRV;>dF3YuJ z_9E0|_Qgv2!x~P1tf-JxV7dK`FN+X(WoUXbkL-l!*-SEcNlQMJ?aS* zwV4Hk8~fICxreDScE-+*55}8I z@4)kz6P9Usy~{VXVp-g3yDv|eC>K98{m&!TdbaK1ZK+(j7ghe%yMi^`*0 zXnT@HZJ-bFOfkMJ?~7@2$4$7`?}{S#PrBX{+go#=VXWOzj6VYj=CjZy{%H2yc`PrS zbVN>lH&S;V)ar01hc={*3bgi-=!=-Nw6)e7&*ppLc6l>ioVdFe;_8=1dsRCr93mF zPOnl1`VFc}D#6{P5(c7z&x;J)kYvKssG>aRTOGX-(od&ADYoea*eo|vZtPIJ?q~DDyB$cr-?8t+zy#3ABU;QtfyVV<<6mUZ(|j9sG6kt0N&$o3e)OSA@YMz zvoRzlE>V^{?y+||+p>>}&Wnl0t|Xa_V;Uc3Kdra`* zK}=>BhRm60zaKr8DuL1T&LFX7_IsD%b0{V! z66mY8rq=}o5}7*S2>nV1 zQR&vV2rrgJ`KrgnL@3iadRS_4%dLxTV5H;gq!(rYVZ$ScXo{7Yf`_17^VR5sSGxRG zKy5JlH6HDYMc;OqY~6&nhV<0%Pb3Io!bX;<>@Qq#3g}S73WiGLiije%e*-|&7Zi>< z-#9*24aN=;B*H<*iSE6nCi7;HNaiS1w@pgG#jrO3$Z8r7p({PBORn<`5g{0mQfR$- zwjP`#4?BvhRz`rw?;Q}qr=$nHF?rX6bL45|&lDLTrVd|+=+0#eFI={_hV;}hRE(ke zER)Mw34287U7LVsr5B~+1f>KTdO#%%A?hW2iU=~gyk4JsaJKaxqw$uk{@zQY_|z8~ zGX&MA=5W`B)67)1Zkn&l#8?j~%6qf4$?9!e8gt(gkVAeJdQ!NZ@)08Pailx=Zv^SUC}?d`7`!wk41vPOF3W3mT`XpwI*2@d&PM8oV+ zYDSx;wni=H4oTCGbp=@k*4oYTQ=tC?mZoRzSuD_eX3hd=Y2hVx;hcD|AiK{vID-tHhNG*nnb4+iZHt9HPg zI5BS!v^{`{;&qUR;N%c3ET@zW3>tEMGDpTXy~{GC8gkKn!*Vao+t3qoA-Y8BOz(2O z=mR8Td&8tz)b8v($$OY?2aV^Rx-_5?^*pFhCy1B2nSexk$_%-7PlK9gLA&f=rNMmH zeR#)w=Ogp&2Y?E$=i<+X{_-=^jdlPg>+J?oGHyWQcAi4xVsxglfBJ9$DJNaa;Phm~ zIY7FD$xgP|5zk82A;{G_!}NNQ9X|k7J?VDXRmfv3CB$GShB;PLO}N7jpz)>qnq^>^ z-i7S;kDVe;OSfbS*s`=Pl$-2ksF8Vb{V@+ZDce)N6;RG_RMIV)XD{T)Zr%Z9r9scG;+y^OAkT{27-sq$V| z=rYIgD#88Xi833zN&mis^-g}TIr4ff-C$l;Lsntc3IS3?!D_+e_29_qJ8_=uf(QbH zHfIW}@qU>M6CAD8mkLjZDNHoj*&dS~JTstUv@|)-K>xa-E^%uOtW1~;XK~5I>ijya z6nxVti#;Y1u>*=bO>o>mAm!+t-IY=7){d%IId&j{U+)S-50IG?7053M%yg^$$ar)%TarxD+~6O&Hh> za5+ImYEYiA%=*lMxj_Yynl4$sW4BiLa9j3yIERwdHt&OaX=SAfz3;PQX~YC*LJW}3_Q!oI$q`6@o2=! zGyAeZHOxbJeB1tF%Ja@8l^c5N8f9Ec|>kdwI0xZPtoHs_uM2EW?%ES`o2W zS#;Po0)&Ulv2vEkbE8&FHczMX6^53_j{BSAdiT{buJ7KSnHb7|=KB+tRn|V2KhL3_8QNVV2*umb@-fGCXxeLg04aFNBQ)I@ z|I9|z@s8XJO7AF~9bdvV-pam>7(_+GzTSp_03%}r;i$tPE%6rAg~oO~c(c5EI3pRp z_@QuxlBAF5i^Ky^a+#ex(26hP*6PRg4wn94(R@3At_)Pvr5o>kjPADJfc%f%N6wz! z8}j+SLsb}2@tz5qmx$9zR zscV5c&ckiY42c>=NhHe(zVf1`qp-2H^>v)xQCuYWeU@= z`hqptXd)6}{fM*dC{xKJinPV(cI9YGgnD65&kVI21Zsi4sf5V!{ppTzxOAcE4)s+o zX-xKjLRTLoPX^N_6Jgd8n{0y%n{x%l#t3;$JsrAzK2?;QuU%1F&Ondw5Dc|mq1T&3 z3Qf%fmcpWq%SUvudT$h7CE7S(E?$l_nPgzv>Yv6EYLd z&NfM;rCkH>V8iyETYs_{F_?o^NUPS;mQfE*d317(VJ1m7Msr)#?wVRdm|M-y+fR$Y zhJNC#Xi1NbyIQIZEOyc`T?>(IcO%o8g3;+dfOsb(Z0o!G&m=S+Eo)P-Z zdTQ-TDlcco+i3U^Pykx|2OTO8i{*;45#JA>c!MY=2Uz&w_|?fhcoC~S1evtrydw8b z#VvWB@Q%i5=C|=eu8C@B;baLWY9Zp(4N;3 zG7$$wN9iCsq|&5~mrrVKbt^0@@7R}quyCg78^Aei^r4lEY^6@=%C4^i#ZfpZXNIo6D$U$R%Xm}ZWgM}G zdRicc4Lmf_lyb0K#y1`#J;qs1#KM*SiLrmGExQJk+p6Wg36&c>q!4J&dN4Sd7GX zR6rpnUFPlf2k_Fa^-iD^NGTrBmXby3P-*vK?K@d!#vJ!{_u>&)S)rt}U2JLdlpNKp z)3B(y<`(DJ9O%QyR}GsUSVnG@i9lei!H96-K6OQoN>Z(q0bQIVlo&oF|KQ0kUnQJ- zTr#_aZF2i%eRZZ6U`5es?C5+xwI*UcTIUlNP1!xj`=RBJf2s$#VuIHXR>$njBA=G! zw~|X-gCw0U%cS8ePr7@+ro9KQ=T=L95g>&ob>a_>*N4Co5}CzBP7o#C9V{=C9}y9E zt#llgd<9gJHP_Cd25xTL3sXLI9jO^GLyqu-jrkT95^EM!KF zto#rg5nG-IJ}eY6!T~BAeNqD`S%g)yHj>GhGx`gx0G+{r><(Ql!TM|=94s+5G2~lh z2zF3iV6wQe8eEYD=KMoPv@i}B{q>1{X+3<=){w(3Dg=vSO%aJ=1>&ogp-0&**lxXM zS+F>O3dz!5!xS2Fb7)T4 z{KbiS+v@crW>jqF^|z3?h9TS(kb@hAtBGWA+%1++>FKis88o5hRrGWs?(KAIIjAs| z*b(V4)X;NPxQ`YdYT7$T;x4dEDK*;M1FMWDnE@?JjxKpRd@ux+3ibgmm~spS%627} z(?ym#P%p2H;1FOqD8=EJ&VakS6@QKVCw*~$uQ+s zk#Vi>%$S`Y$a$AtP;9OMEu|&;J^+=WvDI>p7+G(kZF;TSu>7HQI0mIq40`1r6tWw2 zKm!2G&9bGL#GBUaN~iNWssqzt<=neCg4ilfjdR7)r{Z<0cdk}!u#+4GPmBKg?Bodt z&2Le0(g(B+3RN!N!odWetUo$dIw_PM9Z6P~R#cUVtd3X}SnxQR5%uF|VL3w~ba-T-*8Mn|$WT>_Gv_4t- zI{b*{k~n#y2l>IldD#8;sDdLJL-7>`4EsK)6zIdtAQw}ygWK5$J-Y?153OX-OSfj< zT;d(=X|v(r^0FcWNFFiq@UK}hEXhMjRDojh%Miq;WSW+m?{SqW8@#g9b9N7)(i>h0yF)_4 zXpLNH_0T=cLt%x-VNrH8E$*gueK}kK&7`@5=L(C=DSUk+U@=yshr}`yl6TIsa8(BV zNIlqUJF|t5kc(X-HfLU5WZjX#J_Ac99rry)`V$V~d?ZDwaAxFV(^@*Fk2rS|8d!Z} z^?Hx+N@GY=;}q)Kbl}s>c?p_w0#XedpGHw@|L$a@Vb929u9&pq^aAO}R-Ba%g4U!c z2+bJ zRLtH67q9p=H+fU{Hqct6ugK{96VFR(E%BeagZ*XH^kX`7L%f}VpHkb zixWw$5js2qmEj~oLK{`411CN(0}ew*rFrO@q;V5ZcIieBXzR{H#H=mhgq}ObG#at- zJ`*2`NA1PKD;Bolj_IzRq8-E!hoCC*!y)mLBc=cD7Aip8t^Y(G=#InZle1G(xL(hc zL+Ocr`&T^W3#@!-sTM`@(B$#j^euh8dt5rb)n(t>`E#|M^QTRp2jn)xxo7P9BHte2 zpOO_UMgFDV7cCgUE&6y1L8EPK&`TcP&>7l|QO?8F0}>)S4YrNy)1b!|9H1hNYCW2( zZ1>vNRcBTcyW7RD+Qj3+=6m3JZnY}0yUq`cSqDe{E~?ps@5<&Dc`XG5=bsyene=u< z>O0Iu94wYkO`bApL772YvfTkr57#9owN%+EusHwXFxdc8G)!l;lzp~aRx8@fn)rxX z`f?9xn^4Hr52V*A%Fv_{s`9eg!4wLuR&UFJd56G~`fyppuNFJC(aCw)4PZU%QoWn%XZeg*60bF55_vQ>+DBcvJRmyL%wh8!KOr`O#P%%rj zslzo)o^~2-_qusys*IhDs0zG}iMmQ7(hG7b>fLoJlaKTPj7YBq)O~fTbY$Z$Ql%x{ zg@OoFQ?h;!L=3hSTEcRbt*S?ERi;hf>$xHK8Qvb0-aj<8Vbq!xw)dDUZTyzL9x8Wh z-#p`9p>Kp5cJJWCtg_S|-mzH2fjv(Vp+SU+8QNd6SB=H7cA$pcyNt@SR-Ddx7hE_n z<_Zar1aO`Ksj#@j7Af_v6~t3wiUVlJbO>_GlIf92Y2TjWr^e&#aafkytGC_4Qp&uWvmM&lfq;RA;Ow@wE9{Z*8!OrSvp2! z6fhNisH?m5F~hi|9Ptor;C`;Cxz z^P>zZ$GDmd;w1+1E|?O*pU!?4{ME@BIp|*(H*A;6ua>ISuySP%-`~E*hQ_dxo2g0= zU@j|#-|O9WLLU|RA!Jc(K{C4cMsM=&=@Tf_wxLT`bg$b`*X`p`>MYfzHF=kNc__r% z(l&xB@^OZq6SwQ!0ac_Uo(gvxG`*rCt0?=q2}c3!h7jG!9w zru~IwH`U#m&$(yW>DocBwXiGX*kbgz0Tfyx2}ygoQ@4j4(hdTg9QC01CD<#Cy)Bu| z8hP*P0LL6i0Vxu)P<|ev=pi}93bFx;LYd1`#p?|}(lsHLJT#k#cf4HW#gbhVt)#I*7ffx<*s zU3bi74=Pk2Ba$pSOz5Qqs5s)THwU|ELVyN8$!p3>7mStd*3Ik2wjCZC%M+A5}&Gb9@} zi`9rZN1!TgNCe-67U5m8Mf+r#)u2>l;RRoL0Cciq3f-WRC36FJ$B02CSP@t&>x&*= zcN<+H%cXchkC2NqH~z{MQi#0z*_)BLO4VVuFoE#g?21_O-5MJ{JjgZ4~mjnD^) z7h78zk92hiuA2#hdy|pz?tJNL!$dIvm#3CNN4PGfy|CTi0IM;E}!3{rqD{9x>R~s!kRon_ zaq(W)zCBxEoQ|R$go`Lu-XwzXw6N@f(E_PuAVCy!3zjdyg51N{cYU=94m+SU@(GQq z>T$L_gZv9aZ;#8+)Ci-op)9^Q@sLeK^ru%`qzjq5e~OOAZSpZ7^K#L(Q8MLy%)K^glh-}Fth@ttTbgp4o*21BXw_h-M5-9t6 z7+K{;t|7Ni3;=YUhz-QD^Z+$I%u0r$J(s&Nk5I^jS#wQK;tdbLHl4}fdpY!{4p0EiZFz` z5yRsWsVTVqBx}sEfRPYZ>iWy)G@JLs(!1 zi}1ssNSVsM$v~DQk*lsIhaCGm`F5z9kWz*SQw{?SABm{n>?$lxEX zJssv`;NiZVd)&R>mjadhe$zL-Rg8WoY|pKlvw8w8qGvL*VLw@6TE~UOj8(=mbE&KG zm-Fguml!CJg_IF#C)213C`>z4pP-FCK-M&~TzklE^=o%NiW5feQa`iP+FeH+e+B{M zB2?OJp=mpC6rCRTHux*oI?+DyF2ErCM};)XJ*_3|5*7wV@Cuhz)mGEfyk_c(ENbj6 zzhzuE)Df(mfdnwcY8k5F_qa%;WGG=^D2#sX&#zF4c3FW-cm9Az#fL!Y=rxiVoC{yS zZBS{dF?RB{-v%nr_*I|Ar_YS?)whM}lEib5)ABnVw=`08+FBr!+gTFSNj#91yC1bNV{?QIs?_E2o5Y=yfc_aY?fMS*p=^fT65 zW`_r#>}|>rNmTha`l+=^O#dGkQ+cGURzR6k@l#KA4`b8jF1Duc^FraYVbQh;V+bw4 zd$uF8lSptx`La+@+SQuQnapko--e0quK`XpwNd#Gx$(2*eUD%hl#F`Nq3n&6-xpPeS_8iO3-m>UtFU(^z*UFB4}gw9A{vXgR6<+3Oig*dsf4)yUoi7~vNzE78hg?4ymZ0XD9)>NGHjwtWvw>lKFZXAcGD zpnofQfEeIOU@#}TjyGpkvKk@icoG;ce_!eL0l8Bh|JPhFvw6I?(EK21$y#4~Xb4o_ z?&S{Ip<7`#iGcS(_VsSXH-~GWA?qSFM26Aq{9s&F-!2$kcl|t}HegN>Ilp2PjX1ef z#vhK}%8(+h$r-(?Dx5c-eq4((i05YN6(d?8DNL16$Y#c)?j}PRK)$(=eAw8nSVY^Q z8;Bp*{4ZQ6Fhhu5gO;QmL?AB!3lsV}Z}V230(QG%j?{jP12Xxifk8}?zdc|{QQKw^ z$4-zS?8-%tWd-a_R^VNreZ`uk5x`QFf6?*STsb{%Uh$uP`SSU9{o7{M+zSu6`Dk$d zk+=LEz`gq|Xj)ArmL6DO#9>5unH$r-F~}b8y1H8cX&^nct}6ERt9D>Q-Nbb4(ekfj z(hqn(xoM{ovT^W%M&>Jagh|BWnfBMp+;fPgQ=iCub01pLnWuREeB#2fR3fY>`k4`cFsk~vFmLe_k_Etaj%q8NsY~8xsPFL-Frv<1`Cv^LlC~7@yn*YqYaBfQHE1#h zpFBcWT4=l3lQ*FYDHqhWj`tAc)vQY;5L(5~k*1)j+cJW^Glg0*-YRXjyB;-)TA8sv zOJ1jT1EE3NWqPH8P@6i^07c}`SS%cRue#6sshAf)Ic#fJMvDfz<}*!AWjLmL^XgyI z^J`9{vZz#asR`|;OpC&*kK8^>i(kn-s92e#_&`uz#F;>)ch<_DhDGhgbUBhcu%2OCeHxR4-6_X`m`-n> zMU}IQr(>C5076n91qGv4_7Hj80uw* zK2;(%^ffhNh%2V7Q?147G;*6nd$Q*(0+vPFgVz|0yz$+TD()wasUBNF6Zmx))S0QN zz^+CiPK`k6(FZWRXw(G_O4@33f{=bk%=a>~RAbj;97E&9^Cw`A z%Ax_F7IWI<7xl8}1LzPxuDV&(ARnPx2A{$ekG5eNe4{7&|tJgtqU1 zIxvt5jf>eAovfYE^Ls)HXCYaWWE4uL4y=Qo^|eDp5}roo_1rk$wY!C?Sfbe1l;Q52 z^j$Rn*1#S->sMUq-Ee(<@#_SrY$vGxyG&6=IyDTS4tpAv&3=Lzqyv>ES6m&yV02=s z&^numV%MY^BP9dcw!8uZUS^YgqSfMHGFE|LJt#?Bn1Z^7#c>;m>RW~qHpURE!qvx7 zSwCet5o`1u+U%L#^+i2}%4OL;xwZmk59)6(fP!wOJ61CG#*g4Mdm0jaH@*`&lacT~ z#1Vn&)0mRt?p|#ATfkHq>*)wjg(ldPUD;qNgwSW%yYL>(xByODF2A z*{o@jWrm}K=G zP1&V^=}c%f08{CvqtW>lo&gce^N7mC$YfX2a#`;kb5I`$%F%P*)!r{HIlJDo+t;PO zU<=%NP~L5LqR;Q%X-rnZi%(tkNQc}-KSJW|%tBRTUD&k_e&o&L8CHbfgHoU#DBFD` zD0GZ`jISQ__g|^*_PIJ`W+Fe(39gk|^LDD_qjyiHGgX_r2j&~FobA!;x?V2L#IL3~ zYW8z_+bB^>fR(fdL>)V?Z2@kV|iT+<~7w&M-44?HFw$PhyXSM2$Cn@Bu}Q6?#+9O zpGJrKuD4k_kWzhW%Danu&A?fhat#dN6xU12`TOToSP9IgIPEh$s?}fTy-BwsA0Oes z@-_K?P@Ar@+W2_gwpiappX}>C8IdwLd)x7fiE7JWYb^2uh5iVL6w`HL2kOk{l1)XM_VX^xbJ$Y65pfc(>hR+}=56NYMZpA!ADO?Dq0(r+U#nQa=cNQQk;|mOzq*p+i78C#H6ya;Y<(B zaJl=UfX8KPA6iu4Gi8mSaY!!smKofvC$O-Nl%o{qwy{`i$AFp{3%t{7O^NVaO?z8; zzg-GLV7XS9oN^w`js@k*$)I$L=zALnuJE@m`csRl3hn;B9zSq2Q zb^IhQ7FJoK9fAQ+Mc>U>7~0-3_uA3$TjH26KI+hwG=0IDR}Fa|*(PDFNDDqd>JW5N zVK|%RO#cvL!A+G$>#DEIu--n0O8lckM*7CpP2RvYnBJ2aOndZs=fq~E>38gmade;0 zaHXs;sNq9PG-Un74feZTEf+2EzeS~lNS7d4P@}*+h0x=DR4j`WJLA+*JDS1s^FAz4 zbxYC|kz#{Sc}B_RT~H+s3NBN!xeq}9rJ;lp*D-X`D0L_w68F|BA&k&NS@R^ z{V$<keUraYFF2B^05j<<)}>J2%}8BYRHE+uA4 zGh09+;_-2PK@K7)303lG503Gy6czTQQA>Z6S}5DK5Vnsy_MmlL{?V5ID+~7ur4DKc z^dan4#(v2IWJbhZI|lC+L^t^HT;mj%jfDrqv&i;{Fx}7+nX(q`3L4!k zv@6msE5HqsJ}iSnN|_OhUoj=>otES!-#QO!LNbMflkxyW7<_n423bsLNOA^N>SJuH zOlgJvw3{_GVUkIo5N0FKr>EuRA6Q)V;pCuXI06sD$NaTHSn&HbiCVM0+) z?nl=-DS2cUM|o#_1|_eV6c09;u2AT6t=&EnrE2RL_ifq<1#G;cY`uJ@ZdG2yGkzAAo>GfY3w+v84Rw!ltPNa7 zrF5cE$M@rx?DZk3Y)xrmP!*%57)QClN-OQNU@7!DW+xM1wNeN?^`;z-<=_k5t$BQN zfFl5Tc2gW~5K@_*sUqvHgkr)zB)H)F4){a23S-At`H4`yJr|VwdR;icGd!SQ&l_ z;qW@XcanQPm_L9y@mA(9VHN0@S}!KTpGO9gs_fm+QM|Wdiw>@$tyD{|n;3JIo6&C- zEkv}5zZ&y7y>;&C()~8bcMejYw)s)9P@)&SKe)4;it+}i0ee=$vTEJQ^(Q@_VvUM2 ziwPpuy1JcyLu^sb3;~~~D@R*|T(~;AE9-ru$So&mX3onfmh`WtgHJ!^vjzEKOmDqR zg-6P?6|BOfAL9X%-Wp2CmEi+lHhqg9MWst$3hGONetJG06k?mOC&`(k6A?Z^r9aK< zNz8~Q711L|P)9kc_sy9;U=@X$Jwp@NiqWcWS#d}RJ5DzTt?UF$tV2W@1Ac8+kB}7b zM}0e2gs3&Qg_6GB96}B@D<}G!0*XIa;h%z-+?Pg(MHOy}KHx9SO|~tXPOwKkyO_20 zJ8ks@h4w3`>a7&(vGx66=DJ=)z5ps`{Vm%xPwmE(*M$0$ZhDWDO$Ng=V;ETRR|B%BER6{_ zzKZ{J5GTqS(lS%rE7=reuOr5)Nf0ak-c-L74= z(IuZ@L@%Lo_kktLD>WnUm5-+t+A50-GSttnkPM?S(HJOlM6`nJtd_zI!^51EL#8}o zhB@98CWN5e!SzU|>lu|Nr7n#ogiL8NnD~VhmnI8}YVn+Hf6w%)&Vpzmr!BhLP2bfO zWEw^NJ~|_eo^6j(zFEg=>?-%8Pf;^sM6%Jj@(kr3%07&~%FOFmX!^UM#dXZnmzyrZ zVTicSnA!?l>7XmrVg5S<<>h7+^>_rUV|w_-u7t52WS`P?NO)VSGRmx52_g08S|ZI; zM;}<SDs5JB#@}cYss;ZyWco%fk9;T}+1!6jW0~K$SWhVrs=#J3A zS|%O(*_ArS)W|lJA{kK2a`b_F<9pxFKc5wiO&wK^`aaR0oK;LcBI@NBuJdcORrT*8 zLAD#S)@x9ne|cpzL#Z7`(N)eY9&&n`eLX}5F7)IJz555MF;i{2qCSoG-}U?})AZE) z8db|xwZuYN1{%EbLZ|R%%?c~4(8;c5`pB{Ckxl@mz@7KUY3Zt-RLzH4UL4^e;|d$7 z0wcwSNhtL^`;3EXxWY!0R>m+Lu9UkK!r|nISosGY6&Wg#I;hR0%bF@qKJ3ta>!!Qi zc9=lvmuD_tW~aGua!`2?pHrP=v7kKa_+^ZW2GfdZ=(5BsjoKAdXX=Du7q$vcdJCjC z!4V~oiJbm}nC@S%+?;|KHU%QuJ1rbR>GsPc7TNoti$27O7gVFmXc~MILFo>=Hii-8 zl+&@sr>l(|b{VUrVRQ->Ohb>=c)u5zzICPcE06-E@4{?`r|KC`+IwE{rlTO#&GP}_+New)B5!YqFT~XI8yfG zIkB4D$6f&w9({d!qnmDOL`p2Egj}~#g@}E~cIv6O7LGkov^6{X$-(0xSO3DMxJ^$X zwNI_hFy%!rZ-kuk?WG)!X~gIHVsdAliMn+Vt&WZCVz|{jVRb1}7zw%zbWVjDUfD96 z{UBO;Ak6r{6cl|GhUp%srVL+!Vl`C5_d$s%)7!CP6XgF~->S9ge8fwKSbX-%YTuR1>Mgb?HaTAQ^-Q_y^iRqQ?}}d4@+{TQ_C6Vw=J9SZb*q)dUQPGgc|oZASHd) zO#K^uvA&IqD!|gaFeLIz)o4PA?Io!5rZ%5aUkDQ%1t4vt6Nm{@2H7y`9CcYI6x?+4 z^{ISPLRxiOeEhr->!~oH-s7_Z)zDX8sXKV{F@@bC{7C$#Yy*}AHI(O0t;>M6eku>I zw5CMH*1X?JHxMz!WBqgjqyQB`qL-tTIUWj?yn!Ykpcp%ibbqY~wyuc8Zl2#~3TG5Z z7*v%V!1<8oonVS3vlyjwa;a@i^Z>O&FbZfxZ{1JP2zUXC_?}0gn7Z`ZzJfT8En9c4 z=T)XG$TMT^JY9VK;Mg9ehF3Ap%G0xDGn|qtjqEP}km@>V{==|jY?+vb_GK%WKRK^0FQ*_xRgAg6 z+&+aE=MZC_BFryGh&n_OI^x7`POt_!D;hLup1iB#`)>}6?ceRwnA2}#eD4$!6PvUk zF(yyXmRf%Z&@3rqrQnxV@7nznt@ins{oc8SXKPZ3!jzZ?y~uR1Ce-ot9+0?Ya$%#L z!j~y>wquId&*~qbN_=WOL-Xvf#Giy^KB4@ez750iE}o!rG`{>;w}`c`6GN@ubaq>g zP@B@DD|;#%B1K^}kl!gS3HbmCmQG#&Fj`;AciQ#=@8ccYU1y}3hw{$7a=Y>{3rB>x zin&oYd{n4jdjnnksxU<6Hzc$A|6PdxyizQLxyg(=d2r>VHLV%yX_E_PkkP%TW}_6j zr}BdsxAtIC%nLt(JZcMJb*d{>PH8kX9s4xO0Jh<)mTp7)a|1=wD^A#O{068>M~;4= z;*i}uQB7_%l*N{`EppS>KN@^(UC zg^{(#wTm-Q%Jg{CUCN_lIDx|_>tcI$GJ<25EF4;?e$m|R%sw#Avw3hI(1t?`ZmY;F zJbx41Qn_WBFnXnm6n*Z`m3Kdgn)UaEPDZwsjX6@$jyrJ9Uw@Bq8p`-5Mhe5BHOOGC z!hS{tD1gVZ)i#pyk+Ffd^-&>ny3!e3AqCoNi4Kjz`(rVgA>;%BXIExiiys%v6t9Y_ zZZ|VPCOd)BxnjFgi%bP63l1lJ6EaR#eYk!;wUrJnDj8rnWJ9%-9w#i<3l+?8%0Qe` z`jMXNd}9(z(O2}1sN3&oic!35KLCo-FFSt7C@FLFCsPB?wWd_PjTOndF8l{S49xQa zQkdbT;fB`ZYI#ikro;1FR_F`*)9@R$yjm;xmdc)KbI~@LkXB|g1UkGmFo${EF!fN@ zjpe?O*yE`Nly_!mG*UteZNlzC8U)t5>J_TkT24;t6+uvj0NkV~Yd zsn2)z@k)0s3y)$vWs7MW&vfWQ#kyfCqZKfRx|NYHue%H%S2?tqfEC!7Q1+v1ReXPq zDlu!5`V`JT+C_@`2Ib;dh&bG-1~G5c-HUxlHa}3w(@?M4F)7LZyDwQ~+kz|q=WLoN zY`_>JCpK^H5ovu3SGy^0Hme->aiS|rP1ix8P?M}`&N5DPg;PCU1l0z+sp)bpLvvHK zJ3enIVL!X#1h!BV#|5{Qyv8C<>N`|K(o@KRzC@)ozp=qt89}To=g3E4_TMOq{30la zQ&X6iyb@HULmQ}ytEu*=#(;xD{3)n>mFtUPFdCf23Xb6!{kSsd%eNX*yve5K9|%Fe z1xg_VUFhDCtK2F`_12Y(7((8lK+7rH_^L8;ZmahC#20|k)@7H+jEVL^1UHHC|>HYH4fdzFbFc zNd7P$NsTS85VOozq_nxkIaLSHKV*1bP4ZXZ`kVkgDz`j%{?Jy1co ztcz$@cAy_@euDAOES*wA2wmp1`sel)>-Ui}flOTIIEoDmlg$lY25&V!U}dipm~3=b z6zLU*Do9Rk8;^WZMO9)3S#*zZz>W-HAY?d%ipmaQVi$PE1hwuDCBDkR<#-%f(ajEH z3nN6j(_7ZTERgGFT9P^d1+p1Y52Q`95wRuy*U2D_{vHdGtYF1d4l%10s-Kvm70>)Y zlPk~z$nIF1-b$aH*Lt76QpLOQY7fm^>RArwU#8JB4`FW0$%|4_>N~O@C z0Aq7bk?+4)=(}?&x>?~k40xb2t7qQa?b?pNf$Oc)Dmj#th8jgr&w1J>sFanVIJkjY zgy$xVDP91jlaZ~hHXvJ884g3|-wMc{wXfdCHEPX-kq#e1ajJd7WrKts=}iT57xf z$UbFFrRW%aW~;EJepuTaGZSDcr8E9QALF)E3mv!Ll_btOG?O0%lLyhae^1yHtMRTUdl9KRk5TG=QiP7kO z=Wy$KGuK;Yqv@P0rKV2#{drQ-LDeI35;N)&eeAP7HhMY=9>rpEbp}D6Fe!jx7@~A) zUC-e?vqAc9O}@Bgnlg`bYC_w*(Ysh^xh7OlUO>v{)&j1Aim}s~^+8PZ064C=8AaDA zI;BWok+$vwo&8{>`xAr{kMPu*v8EAuFpk*YZ&SliAO>21W^8z2ZhGqw-S-ahr!Y=@ z9AASPn;LtjrdgOGJrJ2FoschPQs;pw7siYfZeZ88QF%e?yOjf3x>oYn#A>JC@@WHB z(K;h&0scNGqrRE8^UPQInO;d{#4oYmxb12TP|k#rWqjW&3nYHU*dnQ7cQWT;qe-e~ zQxzySdHiZ~hsbeiIDpd}HA-&NoUyn|q zZuG%2^bXpcFo8&32HJODI6%fB`T^`je)fJt`H_0`Fv?W6Z&dX-u65aZ($ zP_YIV7>U#`?~WZf*flE5^SY6vAhW8>u{*54VfqsH@tql7UcbE?6-Ef$&bSyN40#1I zDLD%7s<~G(5~c$_@2o1t8~uKT83s2twhpKY95+e;oFeMd>eFqvpaL!6uO^l6cBAal z%}1{?ehTXqwdE%uN1r~J56W(K=vNl1{1+Hcz5QnxA;z;*_f?-)Da1ELtDg+;zUliC z8C+tvNc zaA?fIq@*%s=0o$A(r=j1uMnohoLOn>c2H-Gt5`=h2w%S(GdO>`{_u2}X;ZS9sm1qa zRIf>$_^B6+v(R=_{id(-=ZNU2s#y8Q(Fi>+l)awU+9Wu zD5@bKc=88ioU8-8s(7xI?pI7hY1j;S&x8ae2tt&Zn$Gh$2J}yiVd&+>s%T^-yl7+R z)0nJ+iaP<@Z(!=^#irC1KrZL_xKBPTJ5w7dqTU&;HlMo+ULU%2iz3gY?trPn+2Fn~9%d)wIwJ3Nx*Ecn;I zqB1$PU;fEd6e=v#{218W$Z^7pHR%@^b!N$#;q*m-;!3q|wXf`NrJ+|6x2+!}T~GHz z&++xh(72)8%i}MkLwD?3NBHc}6+z3Sc~)4Hcbj7>=L@3e5K$$CQ@7jC%tGvy3I7cu z>x~>>%8($fUskkw5%!NtjwPePpd{~&+z6U8H)MbobLL?>5V19iY6QtR4bw*uqkXyCqWc{E| z7Z*@<0rgOCdJcl#J*md_Pc*3djTM3GtY7a#sy9{#gS}9(_%MP0+@+X98j>&1!y(^$Z~NF3Or7sW*}R1G{M~}iqUp;*q7(AS5AT>7O4(-ME+Ys6 z@5N=q1SUm|W3V)>vIho|{$9yMeR(8H+wBgw%EX1mwF!b zjuz`KXxFv79;5(BQ9By1t@!rX=`o0kL#vBn2Dy&!2!8%Tc?v`!4#q22dd}ITEKyG)**l&)JC(lxN~u+qHI3}_ zD)CC@q_T3t9fv>bSQ9k$z6PZq%rK`iOH_5jx;W|^^vl)0y>M#|V!dBL6&My~-3|)u zYC2R+A*H3eUC6=@pYL0*&;6s<$BOiWeWle}V)&<^=D$AA5_M$&IKsldZ8rP_rf+^c zHv&FgXqLBSq_+6!FELR`*59FJ6X2NrH(-KmvyJjMx`)o569UzF$|ZjyLHc=AKXFnh zA~gyo$U(%A;@^XU3t;T3QEnzG--Jf~7L{TsI3tMO^#x-!flFvRy}92+1wUgQdNOn= zR{m5z6tk9(=T1*N4z!lX(Q)M_?7Xh0q$_C#@ z)zNKjj&0h`?b;d=jTNjFl#ghy1$TXz{7X=4O4{KJHH7+aG`j*+rS zvS?GL1}X)7hjL=9Z6m|kPzAcJ`f1&lxU5kvCqvL>((U z@NI$*yo73$VRv$A5jxqgChzVg#`K?02F5Pog4NC&`h+yhMVml&c?DK%x7f3dWX7yY zdM0cBpa1Ls{Ez>w|I0#4pBfGr~|WrZG>4V>$R*-NONUiil*V&MZl@TXrH z5SsvNq0O@LZOne4uKB01BIUMfLCJkxfYc^HTBz94zwM+38QXDcbjB9kh1;G89tY?A z7q|{P*{3#ZUj_LcWWRd7+Mpc>X5nXEY5M}Ks$f5*kiWe2-};Ph)>r&CtY1B>MS3ge zd1hPHAa}==2GcOaP0jJ`HyGvS~?qU8-cJgz^}jt`oZOd8B@|qu@AFr~{-i zt{Id%)=n?(k+9Z<|IoBf3sUa2yBX)C=i<7;afFYDP~sD)=6tP1{{yb>yb>Ihg{jWW z|5C(h?Jzd_t!w^ltbQ8QZywZGBsRP1Wylc43GzAm;l1WiTtkK|JcQgVrYvvg*^8_Q z=RC?Xi~}V7mG}dolq)w^d_g^mng8?z%HKlCEqB(I_4c=GPPZ**V+-z+(o^`dWS;cS z9jxG~<^!bcU4-2k9h&G?g^RH+?^-b*ue0GeVc}XSNv)n*Ukh?#)g3|A&Tlox|Mi^Q**-v-Igoa zSLfZc6$nb2VrssOm6+n!Lg=qUdO=(cKxEONje{s`EotXbEVdL~aC4(c`9lEh;K?~8 zR!XuRM6F}KVuFLA+{=R>@rt`onLiB;4IO40;0jUPwp+VwX4;*lWsz!z7CSXz-Du=O z+15CMf`OC~cuZ(pMJgEgO7g{9mVD=hm=K)DTP%r%abF>~as4>(C0j3hNR1dFr;Uwk z(ZDntgE1>bb#v4fbe&>Ir9k19k2gy9N&ji=atOnwE#?`7D}y@n0Sb-d94AmFLnnmj zdSl?U<#_V>6pRwyvgKu1fKb$HOsK7(LPN(TPk4I~+C=3EJ+YPzgXr1vo4{Tc#T+ z(qhofYC#zJUztFd0MBQD<(CCZUFhRcb%ha_s^AEooG}WzRB}dT{-sJc*m@G%b*+TK z(eV8J5@e%+xoh@OxA#|<8h^E*rDx z;$&b}zr5~rLH|f^bi3zu=^B}~x0Cq~69g>*6&YRBVW4q!61O}t18?`qS*j9`lk9#1 z$<~{YK!gW3yv<8II)#$&O-O~saYJTxA6JMoG=g)GQGu-maiGIi$ZJfA%>f{ol>bGL6l>O<5X}%bpCf@i3sOPeCrsFQcpp+lh0GLy z$yODs{^f@oJDD~~xWS_tXB0GMK^nEPGNAx`LpF3(4K0osjr@KxiDPLr&P^delF8}^Q86!YVV`! zlogEwW`CTK=j*%K|0+xuzuw5{TC>ZdkH`0ScJl*c-pMA6F-I~YOkl#k1S+;vZA>?u zUW#8_^iRbFE?)HS!}=#@l`n%DGFT@@;VWW`PgRvJn$PL5?zXD`7OXeMCoh4b2v%o; zy8Z^UqVbyYzYEl&ZeYFg$YKsWgl|=)5!iOL#zKv)zYj`xEI`^ir&5j_9^4w;olr{r zmtm^Tqz(TLNYD2PcD^=&C-(vpQ3a++k_Aj_kwI~_xW>F^JeD#-RAlizlJH&g77pju~K{H|5w>nnsru2cF zLynN`y->cLGgL*sHQUj@TX*fi?)Dxeh1dn5YVTdH->=+{nzR|49)03AVJzcqNV(ra zvZ^!?n?jm8MQHIoOwe3;E6sGbn0ZFiGb3IIp*L?RPW?2??M`lJhRH={Y~#KPpJjJ zhe^%_?%ia&i(^0g?=I8_?Uxv%_g_GIb)U|&VfRa@e)XW1d7l0+x=>*1*~}d2)G{+U zf9Qo1Ix6;t*Be{sVM>Rw#M~4(H6Oe$k6CvMDkfh$dkk#q8v3$)Yd%_*d9Ra$|8dH= zZ9fGKR+wSX!+}9>W9Pj_;h}cB>dh4$0K) z!Waxrb;eOSU1H_7y?6-6x4E%OQ=@j6>}w7yVSA>rQCv?$J$NJPT6n$99h)~SITQwE zrd>nR%pK-R{mnDh=iuk;`mWv{Tuj*rn%#*PCk&QCjiMxyfl=KrWtVM*b*df)XI|)0 zClXkN%Z4pFu}ZgN7lf}eT(5UPN`khPK1@$#6dLa@fi^$8MTyi^-@c55X26U(AAQK$ z5P;2nRcmJvS(eb0v*S_}nLvXGP#uY0TEyfEPOlH3ZUvSa+XRtG>zcwgX6=#lbj2{N z%$v<)`Fe=O=x!kw@Sr$yW)HGKJ|Hv041iy^)&?$lZnLrC8N%eTO@K89{n+~8Fx+DT z)xZjDw7F1*@<^_$1m;Kgj12~}O*d<-icBWtiX~~fM)BmZf}BN;RauXG)S^GNplXn8 z+OhN({pL4eEdm~JFu8hiR(bGffTnEM*GRvG`4&9uOW zr&8l5Onk}v1&8i$*3UzEu+)CdecU1kRM{5Y7KIAz&t0Oa3Frdwb$68mtuM1H2tYj@&Wa;pum116C zVdB?PLVY9;`_h!stt@1%si4atlX(IQKO}b=+t3$TL+THRRh%xjIj*E2jSZF=wRikY z>m|kpOWpCl$MODTt9ntqvFgo!^fY!$?P1li4?kr*CKQD(2{Hh7fLB!a&;mAqn(Q-O zqcTuLHeL-bmgZ&gr;IbCSaL#gQIWgfzTz&n7&>lW;S9ss+x6)Wc+m)qXG~%h5+H6j z0sixC`xF<+wnCxwGq{jpjG^(^mwfu$xFWu@nk|iwjP|Qc?v!@4hdok8fmTiaCkF<# zxxxvfN{ZPShH=J?``jOW3Jk%bC3Sdw$W8~T&S|l{jjN33=)8TR0r{{rrO~2fH)FI| z1FDbSkAD)_jO}Zj6^f&tKs+yLZv8IL`$v{|Y7w+R8+$T&ZJ` z=}~j>?|x|6a3$H#UE}YK0PT7IfhJE6TXq&M7k>oVTO?|TnRh`Cd=*@FL$vMvNP4w^4_{Lh&$z)$n~>p6Rk`-=lD) zl8@}Q-)jU;yZHe=&)(BMPD`@W*BiN4U?~lD@@#P?tm`-b!~2^KD->&3Uqor&>%cv; zP5lbA-?jPqMhC%MFduJyl%xL=7A^3^M*N1$$vMf|<{6xQ@8g0uEZ72&W4h5P-2PKs z4Q8sI&F!j8q|G1!xPXdsXN_jWaU{e}ruI({s|O`>X6GJMT&(c7ht+No(zt&aEH8Ti zEBw}%`-uAYeEr)C??b%78E20Ry&A-$=j4|Os(Tu7;h_reO~I^$4wpmK6AR9zt&54Y zSM;{*8EIFwI%U4FCa<&+Ne+6^k9_>8Ul<-J6$V1+Rb8yGa~PC5D!l-Ul6JrT)hX4C zti-gJ+^bJ#wOPnX`XKgmI=xM>2G%d-dFH11CM(v;1RY6;y+aS)yMn6oNePtpZ}ZBs z(XjG$JuBDfvs-iF75+f^e?3{#9frmW-gtPZ9-89jq6NB0I!7OnAZu&W7fuWpt=op< zF{KHNsPQyQDEE9jxdjaahcC2Dg{e8&~umeXrTc zN|f(5aVl#QJ7XqLjuyg%y>y(2%M2bV`GS1+h>Au@t*OPvk>*F+8TMwMeg+k7(!yAj z3k=g!=|8~g#pz|p`nS;ZNH%^iC%aJmt`k=m-n1zL1x^>L!c7`^naFQGL8Fm1h&LH@ zXd$ly1X&hK?;u)liT`vn^jUOd%ro`E1+K+E%CRdfWJE%sCsuPTCMjz1mR+)eSh%4Y zJY#N!krjlZvJ9+D=d;1rjcG#!miafo>XUt7?&+lOPfEXCpE2;G=bRz;%O%SRy5}CR zUVJLB?-e=5n&Nh|;Lj@2>~`F1Ix|GK@cgikk5(7~T0O!`DzUKA~6SD$o7qTLhCr5DoBkIaFRGA=2jj6_G ztCcA3)}osQt6tpN^b8y2(w)6ol>ufyVFx?1rUgw~V-g@8ZkAA0T|BhHB5W{g(8cVS zcV_BtDyZsUGS_i57kYfQr2l20^bBgOwhFxrG|UKQ-o*sZM-2OjnIX6ShX+-&K&58m zEBLwn^9JZEq4Rt7`L0{I->G;XRJqAB+d}F&s5dIl|0+xuKi&t_jS7IO9dSBOcV2zI z^aG#ozQOCvIO&Xp=i&z(guDu;tNnsY^)!F)tn%4eT}GA0hHS8Bdp;4f5?E`LE< zoD6C1P(>xTQ#g6wMWw(Xx#HRm6@x_2qqVO-VLAJR?$==b>R~}wysHFyR5KLte??4m zKdbs9XZ2lFzk5^+1MVzCQ((-T_1`(>+|Xr?<=^xImj#O)zLCwpx>ex^Zq)=>{s(M3 znGIyM_Ogdtg4_G=OuJ`3qoiHPa2?dudo`pm{muzaEIuYCS6iq4&m`z`A!cxlH<+D$ z?TZB9Jz$4-az7L8dYO_f7nYY4k!D+kyN#8(q=dRzYHUPto3)^_sZqK91adbvf?Q$;Zpg(PRyZQ<0D(~& zQFatHB8x`$&3ETMZwGa$FaM}%>`R;8RS#_W7C8!yc7>P8ylKCkgKDttkw z5L^imuRoU;fL;T6dqZ4`r7r#acc3z`j&f&Jy$Sz~pk99C4+l zR?4=3>{u3yW&xSDX6~isDHYN8aWTc3cO|v^4Bl6=fMMqj<5~W75m}Dy`5P9hnIy(L)7Zh8{8IzwwIyNd;Y8mQ#gHTK_0oWKO@VN{-|m9BU5cFD-g+gy zgW4vnSGgN^0o^3TwxX}kp;x#u@1gq@lYDw`CU?!Uso5qO_nndndT)+l3Wb~ctpgPf zf4uUqC_`Mi6>CvLzcEBI#a|CXdOdV1=Q2%4yY&(j=bTI3!D2T3(8PSQ@8cTb zO|>nQ`*_ra&cFm%^ithEs;^_=E?iGG9AkY&?<@(GlE&w(OtA8pIY>$jbIQ(i?iG`U zwwpbga9*o@$VwJ{D2$!tdoa8QR{XFYuoGAnGeTu_&&*4NKSK%S4Ehk|cv(q~eqtwe zIM`CXc>3cqS;^InjDWdiT%rM0(U&a~rW|A`tNfr$DCq_x14mTy)$+$FDdmbQN5Wmz z-p;DrcWPz~L@`AKlS&S(^!KBEuUuf}GkS>2n$~@IjNVDJ>v>R2^msU`XX{Wtsco(G znxI@Td8n#c>^;;*0z-aCR*;)EIIAtV?odT)AyuE(RrDu{qn-IGIXc^#GP?$-T% z)aDU5u)n2@Y>M14TIh^06lKlrxeO6T8Hte)wCKtzy4Vpp3)WcWa$`g8%N+4^QR|s= zFzO5%KbSRu!+^s}P!YOkBZv+tB(ARv3U8RI%bnd$DlLaH(ywAl*D!exUxT;3om_97 zwadT5v}p9$zPRg-Lq9&Fm25JcWt`3?_5Ic< z*-t=~5Ttc-C2}M_JeyBa)PPxS1ztUGhUs7y3Oi~Np{fv*O`(<60iY?^6*mi7)rULn|Q5W>5Cgd8$l7!y{pc3FP-Yk3;s^%@K zrgX4R;3!2eCFMZ^*J|u1sNmwMdU3Fa`Cz5emE-ijdxwf9N}WKb1P2(Dc}a;Na_mRH zlS}Rp?$#dUmDdz?;ZzTG_x+S=OeitP#zZBX`jz?7`9KDwO9e|_hFVa!ePf=&GBAw( zb%y#5C^`Z1z%&I29*zmB(tHmAfP$HY#_^su3skcaa*EJ~=0ofE*;?Iop~@%ujbd%* zN3N$J#G_|_K&9iMz!9KXBTrpnDWh(?VqXdbA?Zqwb%W(y^z2flMm&5(rIfn0%2IBHZ9Q#P+*fpCJLSg!DP^fAmmLANO!SW7_uF=k$LHzk9!Z=g6x?B$tO;7A zTuM98LPDpUW%J4Q84GbH9%VbRHvgabp?kiK^%v#5@@I;9k7xCwfZQ6hDkh)5^U-}( zPHQ{6aN1n@aZGO=(-5jcrXJ6HtPj0_RQrwfG@CllE2fnjG}yh@ph!lJ|5zYGU<` z%2@gP!v2#_hS3o@1IIjTj0TkcqD7_PnecQ1D8)<|b7qK?xx@oKEv+@YG0I|!b1W)| zaMErk=T>V$if{%j=Hh}^)UD(2hv-vh2_IHTrM3`ie0;zTI|BNVyxQT9`u6Le;ez_|Nc za8~AJzp(7`2f*{+dZN$j*Y5C)7Xc;KZ50XE9t@E0LOJa=F##JCEHS&CHl} z3F){!o}zNW5|U-vnD`X+^CK%Yl~TL%y~Ev#H>yLujcRBPajo(FB&>deoeRZjg%C9y zzo7z5y+bqtI>S^OjULi6(OfJjw|tv0o2Ceh?FpSy(EBfk1QkewbgV(an}?dua!!d2 zs9nvf-4gWI6AEdHC_OAN4Yq}F+&2*_*AjVkrr5n=8lC21531jy;=>8m_3uDJ!^A7s zkB;We9h4I06kD(v*FBGz5sbbGYAlG?v`+&&(%h@LN)3zx@-2Ld&D%Z z2gqbjnL--#dneJA%v6V&p5ai%_93hl<9yd;u}yi!__5{*1gZKCvQluT6}m}>L|#8C zA#p75jIC{iKv~JC&(-8O7-BurHX}sVpQR6&B0FgPt_J?(0uctFfikAvi@8`baVB6G zlcGBK35$g8mZ)`fg?4mC51E(TN0;?Pt*0fg|Ez&~sW)T-tg_r)L3{#<88gS8L1@UN ze2<5qPe(AOa&iogG24|LPev3iI&V<5%%;b68%jPH6vgz$RPm9*4ANR|lcU2R2iVpY zm%MU}sT*0&yA;%fFQ}7fTyi}^g(oZ9rwQ9L+z)TYp{Oxuu{9pf|xP!hiZ5D-dUxyvx-+y zEoREh7{ja9;}zj<|1ZRJ`RfT2qhIA;g-L&_#AZ^4Ol;aFj65@^N8rYPylaO_KYq_K zr=UEh;rtCIg*c|mE#ItQbHY3TTNZUx$`#%s-hGS?6wRUR+q;64&B2^OVrg=U)DR8s zoC=Fu$@%9DhKL0*#85K>U41hK%nlhwK0lJuFhQv=kNO>>VwL-H64V|H_2Q^bql3#wHP|q{$zS}ayj)W6ynIwY0qXKiFI4uN z(M{C%ORc8~a=-6e1f~drQQuH(*X4p{=)AlMsX%KTZMaDhFy;x-=G&MuypG;5K9E$! zK7qgOeMq$FgH7`?MB{&W!wJcA=%zS!Plw+})UG0uf7{75wi_(j6ZWOwhm>#f!08J_ zud}Bn4gVD@<=x4c>>8ZuGRhhUIA?FmNO|7IA)UWGaEcUcN3@N9c5M>j3Z@E(#d{8tf43Pr=BsTK{3R7L8&>nb@IU1 zeya;{+|$6~jC3lE(r%_GK=16iZI+3V*nfvcwRN*kexLTiGV&e6Z-CP0@w7QoAwomW=76e}6f!I@&AFD!@1QEBI>phR;z>TxES1|J zXiF&ig`MWX=z7-OKpC@0agJyogw3pZWXH#>&KsiH8d>)+A^E*O+`bpI zd{&nJX-x0kD7I?#KcZug^|^v5BPKwlq|xy6+XCwV#T@8QU}{P@Xjr%4LkveF1%Z~# zWbiq6Z>cz*APu6Q33XN`#!K|VFjpqVx}xRc)8 z*fcz0yePUx&12BZvRl3}mbh+uo_-7_9hefn&}u#n?Y@DD2_z@aky&bZE{$!V!VqE1 zR|ewaH!(9&jF11# zdA%N~=oL5&jgBz(ZT<7x+gjFfVNjf55a`!?*4SCLKKNn%edu`L>|yH0Z%mApSk=x@ zFOqpOQ!{LN9fQw3jg6^;DVN1;2pAUV70t$RE$prDbiW`YD^r-Ust?RO7SE!^feobG zWDMQl2~G-;t>IK?9=l~GUY1cN42v+XlVyggK3=KyaL?<_BYThn%8JBM78KM9&*{Ph zL?LBH5_O?saQ^u|RakUWSoj*`kqsi{P`=q!ut>QslnF!mN}HJ1mJ#L?<#Zrmtc-2z z2$jI-&bcw`_ba$O!-mR{D%9x9MD5cDbPU$?#MF0g=C~9*D9NyEJ9o{D38(^E3(Sx+ zx_X!zDddBSAcQ*jXalKyYcz-rrh0>7tIiN43o@BWQ6~$7{jdzC+$w}dGZx85zwON} z37_THU4%=g2q8J8<3Y!i^SC$9pD)pyb_6dy-3X{j8kIx39%t~fLSxEEn?92w z^rMs`>(PXA%3^wP8@Gi%mI4*KF}_&O340#M8l$lpy`oBg7`~;y-#Xj%t-!OsP1=<{ zHV6)`Z$d{FL25W*(C_HF?Csd#4=_4(I11#8)~{F5LdjbRVcPJYgKNqSp!SYzR$F-XAK>Eu+-wZerQo{9C|wpDKot3WL=rR^-wi#HX+GdXJ^#4 zB5eBIPuLh2E+`(wS(-(ZO#9;N8vm&x1( zq++m8SlKS9sTxU^!BJ=ySu2#~`gOk7(%z%olc@Xw%D0Y^p9&*YD-e&i2S~j_b~tt8 zl^Lf|`WztRpzaCmJNPuDk{%$348t-hdN(G_-p?>@aiNLq(6TClc~BPn8#|l1_|Xc{rLQQ``!1BU%(va0_SUST_1N8k-moMn$* zU&>o<6$R}07a+GUUOnbF!%s%hv_3$(4Hm&9j0pNi%34(IU}$C}IxD%$O%dJ%RBc%2 z;7C&B4(?7izJAa;w6V;jx7+fX1h))Do$0~KU}wa=CoD`TJwQE7xs*bEdDa!<(|cYa z=FI$EOeqm_t~RFy1lUckkj6f!pTHcUo96WKI4y|T9xJKI5J#kzafT6*x{b-OIn>w9 z%6c4`Lz_Sw!PE)V42qE&bD`~k3DMX=Fi)-`$M$sLwvU-1+SY5Ip>FBf6xN@m>_oAh zuP3Nry)SUB!PK|K1XY=xt7a`V52Exqfr<-OPPiVSXDASJejgQVn05k5pjd#)GidGK z1?Br~&|90MD*Ry+0+kD(21{2m&QvLlZ1cg4y^vD5xBAnWqk4fVKbf)q6-Gz)Y{e4m z*xSihI{*F)h81q(shO^zBhhYU1%-k^j7Djt49biNd1@Y%p&2$R1>9@tc=z=RYN54}ET+6= z&klNKZ+3hu#j6R;h%)xSOLBT}UJ*=r7}*d#4iElQ)Qi@@vEohHPi28`+d)ncdYUh( zzo3`lu6qFily?baS$8t)gBwV!s;4!FvmSium_?!;{HNK<4-fBk zM+MJ_n=>{;<_!&1g%-W^V*p0WMJceub+6gt%*Fy0Kn9_g>hdcE_$s4YBHe&~EqnUwBgUNaGerYi6l?k%1V`i<=&LKkf zE8CyNr3{WKGHAwhXm-307};!YWHkAd@6Py$`l$>X>YnBv{qtpqQ%EmV;cIu%!$Z5i z!3u3aGoivDc%4AB@(oNhC6wzz;iY_XBj*mIgJ=RQWwc7+rpz1;t!IRS9eg=QOg#<5 z%J@Y|D}5HbuYzeS6Z%J$zfHMQa4r>?U;5alJpomR0%%lr(|XzDMIxHbM&P*8AqsWM zl_1@hUCJc)H z5>WP8=EU#T!S`HiW!jcn$^&7+oTnc^=+W%v3n@*};q(VVO}zEs^Ju`0vbf(MrnK|| zdxPu1g)PZeMw=|eHX*0*GC?s^{XkE%dQ|7{otYJpJV4EC(8I8r*G5NkPYLY3{x-qR zvohjJ4e~-B8t%4s+FpIwz(jk@r!Rno7{)ZnpcvZOf<`B6 z#^#2hS1dQ!!ms=I@dh#hQFNWCFz0P>H!`OhLs!zKC*s={cE{N9oT!B;zBRr33=O_f zQ*ZDy*9r7^1E<~>>d`3H*2As{Ya_Ce&M$&G*$=^&+l)Z+F6;c~24X(UHq2RC?6?VAp^DH0nX@KeHfxS`c zS-b%h4V7@IdPw1pz{u=U88hy3rFk1n%djeinZV2tUtzfvi|WQe)d@3e?uc0B04-}6 z%UyR|5k0ncc#(6wRDs0S_kg4&xeoD2KD8v|!9P^4HkQGa)d%|D2;wm0p zsA^2>7#k(LpMXps(K$@Nmv`^&FcB?W@(iIm1Uq8$n>Z-~*ew{I6U1^kOp_&p>`RP~0u!gNSgfgRevOi-4CAU&Thg}i72 zD^u7CcRz;-&C#Ok8+BiFr;b8#AE@jS?I?24Q|Q}$3J^o;_M37r+osr-8Knapip)AO zR%-{&X2FteZ^K^5Ecm_+B?eFPteQJDR`K$L88LWDVnYm$(s@2G+VTY%9}5)DWz*`` zk{2qd?#vwGSV(Yg%Bpu~_lP!fAVN4fEJqz`Ad;coLel_^W~u%sP!aUjq+{<03^{-B zHtAL)DygjG1-~*9|0t+(EAZ7*KZoS__y#F{Az-Nt*dg)Negjp#B~&!^^gZ6@%+U<` zfK+9$iZ9#rP#{!pB}RiP#gzI49?OP}Nlo6ljz93=YT)jIfj;6M{YGTDQ~@l-{NN~` z(K>EyjL2g3q2O?Gsm6|EE(9@b!&(VmeOZ@HJxqUrUEagm_diF)u2GD{qG5D6PJ{~- zamx0iW}+}*<=0fU?`Z*;nYmek6~ZXG$5HM^N%CQ7_yYp^7@*=$u$8C$8<6zQBO=(; z-1<>U0h!iD+f&!7!V!tYMMb4 zH)GJIqr;>3#yYj)%_GNd4{>ZP1cj1{hd1#Ay?Mo=Isa$A1=nxK)Isd1O@;9M+!79r z(bFvF>b8we#@T7P{e+|Q461ol8y4Bcr?q9SOJuz$u!OoEQlxi0N(qf`ma~hx zO7S`R6(;@eUKbgS!5G&LapK}P^WX+kPRebD`Zkh=%{dleOX$yLDZ`hi{6TrVE>7L_ zN3-rXk=}}lR$tOTG=bVAv#Cl2m=cP7-SZV!?xJ)UwCIMF6jx&jX#m6P8!D!*^$$*9 zZC1G5^+!iK{0#6OuT<8zjhn8Y@r z)IS!vWDRpEa$g*N0fj2gDiI53XCKNQ}<&gJf)k5*y32|MiaGnIE ze|)Vq6pusI$2>lVj98_P`$aANv)xd?!YahRuFLyF5Af{tUc&M>KI;a{-5Leabm5}M z6;n=-7&1o|5MKKxCN%RXZ#)N?&-VO_gL1or3RgkFv}@&Tz3zFX4FCQ?t(b;Z7cxak z!sDo^K4D^c5|+478B|&m7f=-}m_^m}t4**7Jqb*|S9)yLp5 zs4Os#2DCfDMQlq+E<*sim4h)1A@!Y*Ut&jk!rT@OT8L6)so+pT<>DB5BE+Z=21VNx z>(-V@x>~-N0mZ4fst`FiGz;Ue>4+<{nTvEs$%C=tmvG5C6mnyvL7F>AuTi8O6Gy#y zUaRfR7X=&1TU_4LEx#VdfUAr@3i2^b#0e}1KO0jI%6!7s#ZrQ%yw5D3PK1T#vcT|+ zYV>!xseL|LGb+P{Whh^2|0P&HIts%K;PKy%m4uE4TV@+7jt%7DSvHBd4Npr8rB_ z@PoJB(Do2Iz{&!By$w=lD}*7NSl91xcu+TJ~rC+yI@oo`ud+FLrIaC6tb{t%xYa_RKR*8Hzy< zScybZvQb;`XR{Ov>74>v?%=v9{4sWnl3BxG@-H!EI>8I+rAJv!sDwfSfG8RT`qeKo4d(|dDAud@Kk}yC1oW4uSGWHdnOi-YOu)2?eYwX zEB0av_g`rbOPx8M7+4t+2Gx{BvX{Ji@Z4@SE+zV}F4{Z5D*E#b?L(43lz0!g$nFEh z$?k^K-TEmhty)IW$T*iWnHhXpRdFc=6HiDT+@ah15?3O{WN*UYl3NY&+wA)157Yf3 zvR^(i$hw;AdtAB?6F3FDUKP`H`gzom#&XXARw06*s-sJ|gVkcqCN%;UuX@6i(IAJ* zT;EhuO7YON8^;wX2c)nB7b`9}t`*B#Tpbpe35!XQhn+lAS1G$t53HHPfHBjW*g@;L zY(AWw+>mxH1m+YK1kb3}4=zm3iT<-cu<4FLBi$JGQYn#d^`<4ZSe*VHmrMu!+XGGP zw|4g*?{OtoQom(!C$T1AjPSNbr}|e+kNM=UtO)Y2=vAZO|8VrE}jIf ziXzCdbIIE#ini@gAzS# zXH^?x5Mc*Op{4&)Z|#(=?kR-$4P5_x3q?0ex3=kj&@GPPIL(&e|8+Ff@#$^5@az($ zzH5MHmZhm_&v=JQr2Qb5hokExLWO=mB8-XlT;Eo9bh|xEv<4R1Fm!4r7%eE@dgRhK zj0s-;~CupUQLDnNI^uXbGBj4!js#>mG z%iQft>_l&eL7V40Uv_ss?(ckfz@mG`$Pu~)AskLCvVMkPWBW-F-nti48Z@6~y@`bs zBsJWHkGP=<>@C58+gm6HV@kxX&)i&gLsKEG+w+bE{wGvA^9iiHB%)4NUBPJ{Ets<} zs9?FW)X`kbZ9A!+ybS;g`5)ujA-j>IfY2At!be!XiC~##EKdYteG>+@lfk1Mv(S;w3T# zq^u{Fc08)c)F7m!`ZC=H4)unFI^F`eRw{0+ZTEgGhznL_6PbxA zqAb6<9j2z7v#KW7BbvCu3NS-frNO<_!-O!fO7`^DI_qgA7*zW3w)QYF&O!)BxEzE@ zX(eXe#iRuamWI?1S{2*Mj*>7MF!4ulHKuK=B6!x%&LRizd}a77G2Ok=?I>@CN7i+6=W&P&d1C^8gISv`)ASce?tgwTPt z5?^&(dV<--c2=2mj5s2@%i8@q)clbdhps!#xFT5Tpjq|8#(&ykL1HLHrB^Jyz?Xi~ zja8l&cneIOxsAVO$y!^tlg*>&cSSvU)w96&4h3Gt!|mMl$duM3v#I|Q)GdQd09vTS)N%TP((=E~WZAus96I*4h|^Er*2_0_Hj*9|YLP zD2eH+c&Fsr_WGyURM-ep9QtD!Vav0kNRj2D@siPc-0JbZ-55Wy4oFcmQ-~9q{W^H) zm}Rrsa_DEBerC;NR@tEz{&iFdaal%R_C*m~=Y)x%0s$W4+qFBY&ql#kdEdAJ3S{+jiBng$=XJAmLBE5DicdB+@LCq1*Hj$WSK^t zyq#<~Ui!%CK+`KTh9)0f+Qx4L*@XavJt`zdXV7`(jI0r14RhE>b|XRu7ZFKZ)06?Z);meHMX?yP-?=rLR`o)x#g z)uvl{3O6IcQk;V9+U>NJgUJ(Ms6o)00}jh37wD z(MyR(z6suHMJW_!Bfk~AM`*10%gR!B=vY&q5jS*Hv)BqrNaLPyb10ru{-_I&yz$D> zNgXoo6rdpiry%@24_!{5VSM<)0C6|OeoTh7v#td@%OoIjhHmYMRX-&9%G9&+4^Wg# zVg650)w`1tU>-|;o*kUPY~2G@C05-nzBXmkb;1NOm2(E%;P@s-JOQQL(8)JDhhcE* zy(&1KW*aF26&o^oGl*#7(3UOqp3@mS4vXKlFRp1LZM^v)dtnz}=Yc|T2;qaU$WNz} zjKV`Yq4Nw#60K3+eNJn4R4DNTed+J{obp*YB-4}WonC{l$T*+eb-5(`5wx6s6AVP& zCGRf#ZqfFM}<;^)YL+ad~kacRFMfi!+!|GI}{>J^y|nei?;QRx4PDzw=x_l4mk~Tfd!F z$&%ZGH30e#Iy-08TNs(D()qS9(mZ@1%9vyr1{A7V-hF8o?oP0NW*6Emg)HBkbem<8 z%1JDW3Tw%%z72i8hh?v~x;rTztIJoGa{UUaQ~1;&=tI^6%Yq<9<%&+GpL9^>eR^L#)LXUyTN8G(~$K4LOuFGBv}>HCngDk{`Xo=?yQs)v%mb|`>lr3rd+GR zb{62g)eAdbE9Ps2h>k&ChK?R*v{qgLdpEQ~(o~FoN6q$#$(KLdnY@3C0H5GA@85C6x ziQ?S6o=(VT`wVc@C&OwPjSQd^#!gDY44Xew8sGxz51nc4Oeq z&d#v2Tk8xhU(Smi82M*3%<{1+6K+GkK|e6U0-d*xFGBUH&~ zsT@`q#T4|R*2>x#fC{#Y>2BK6Ik$mq9{Zk$`4Pu7*a@qZ5MM{ zk&F}87&_e=WK+2IGFpeL1^ay0O^2mTF=^d|#uQE!IZq>gHW~wU0x>=8W9)bn3_$#M z;t5fbgfWpEXfRB!pcDxjX^T4t@Ac0|7z5D)YbIo=CzoAORpB*ccD2?76-*F~A2z5D zP3U((@#}K}tY!9a5?c{S42~;0m~rFQ@|6YZ9Xm{>Z#Sa@K#dH-FE%gpap^YR=!WT# z6o(FFw#W19L!ovH7_cz9=M!pt6#-G&5s+9ZUpSjEN|3hlIH^E95<$4}(F?Jx>X5ys zDYZ1;u!4FsFm4~|uLY{R;(SZGQX5^=Xq%RrEZ-0udu0L@AzW-&DA@!(_I9KRPouu+ z*#Y*+yOoIotrivv+7EzA3inV#R6>$cDo4}FfMJfzE>v{)*6}dd2=&y`;pt6)H0Z${ z13D$H5-B@KL=$>-tWcHBL>(c)71f`c&99aV@5@XNfr`Bn>5rrO)stdNH56kHD#|A_ z`poZRid2+xx0v9#_22t=`Sjz(i=bGxmr=73!ksPkg4^>y0ZIv3o3M5KHmHDalF2o0 zkEFUOoci7ejHe&)>RCnVl)Dl4J6Bu2zDalm6l6(pDya1X(iW+>K3d%IBt(c!f4M4k zq084--={B_6{1@LIcDLhmCk50Gg7ZVEx-Ys4sHhaQ8Xo?@QFhT{@!q-=55!P9;9|N zt~!gl^2iy|@_It0euw}Eroo6^dNm9=6Mf%%m_9;N@Q{qUNnjn3NuIA3dG}54r{@&N*m!vtQtoi_Il~=9Vye{myEp0!-}fG>62_!|v7j7H-V0g7 z#Y!GUvNaB&Yh`wENEgV)BmAsboc z+55<^+EJq|l85$0Z!iSdu(ydl8aA6Tm`CQy{4=%I;GZQQXb1BL7xzTt(;plJvK`IW>$QJ$6<;N#f{NUg?55O zr{*lgt1Ys2({-!7+o;St&5v#=Kp8yoX@U~0yD_nc%DoTJ37l1}LxX9I^*6ZhH%>T8 zEUh&A4N+!-EN}+N6H<0me`|y{=2+&}BXp@$X3(66kK~td4dE3AvQtM)^`t=DLLCRi zEJNM|h9crpGFh9unI%02D#hWbeAVE6EXTH%8%*9%geHW>LfdcOWV4G(Pu_cXWy!^S zMYc9Lklw4%9V&VdL^6pNVP6i*CfB-rBO@EP9DbUxBm`o*U9zD{AbhMR(tnO^ri>I{ z(LXonFV{7f$dsCCZcUT(K=-!6(Z!IkzQJ+mi$)=76%TTdCi-NTm3p$H6tZ0f!HOB2 z-Pk0A>l!Q`^|_HdJ~M;;O;)~p!6^R4Zg6NgYI9hMC-g^57~O*jOV@qy7l)V*3~*!&@P(VhTo-~$ zj+Vwk{s>=RXMh@8!Vqm%w$%?bH##eLs32#^QPE#tXLLT2UJIirTj~W*EhGUm=mt;_ zX51wvYGKa-gnNofw?THMUlgHPC2iV*E=@2Gk-p2I22KT*;P0avy9jxabXoI3l#i!S zGrS9`--tQNF8h)Pb8$wn{4T07X4NbaiQKk4gbe;4qRNf2MibGXHxAeGA7UyDBiiZb zF<}@dENr}wX)FTm5>-Vvxu(B8saoOCG!`@$R@Afq%PXanUV*)-#$+WG4yLoOYV3Pv zjXkVz8J1&$-jF$^CKt${$UlSScd*=V!Fr?k`0oOB-BgVNmXUFTu+rrTOK%qric$Y! zY(^2UiGxQ>K~Ku`nQ4o?r|}0RCbbuP_Kmvw1$^J z^^}@b!e#ZU9}37XI8V>N;AO*b3M(tLx)2-xD?wfUfKAqkQIri6nW0aAX&8=}AoNF< zeFo1?jL84O8-wigiA1kM`y}O1lB_`f4Ah+07j8o;v^R9#mFphJ7if~odM6ZNvrpmC z`fg=NdG8f;5z|8QDv01U_l%sB|&^D*gE}OGz z;g;AboB}Gt=tiHWrFM`m`~(#8*|BU7ke&1yOKuE7G@;V}FFCpc9TFlHdv3$bVBb2VN$Ba?33U7Lr+zID4SbkBArOC6?0lN0?gil zmu={M1`h-sqY6=J2u&d!Y*NS17>IIoV39R?bn#w)$NNzI1QUUN8>~9DGh8T#cf2`^P<4@~MlK~DNVonFRvsI=kv;B%Fvou%tkwqq zjfrYt^9(ayB1_1*brgF8?B6G`mY!nC9xgY}?)s)Z*_Bx+B=UApn;DQeVW@%eh?te{ zW#$vm;XC3&&`V|>X#o8rZZi>)<^K{7mTQ7?8$_4r>-7e)uzvP2HDqh7z~JEo7ak$zdGK-$-##9p}SG2!PIY;_7YXfNy{ zDxAMeyIdzc{r3Tz^EFZCutlw9zw*kU?F?M4!l-PFShy)~Npp@%DIUA1nc`JjwrrkY z!6^md6~lCCQ;n8s&DontM0aDjjs=t{CO`4M*+*py_L!e}nR;vj+r$VEwO_*3;ShG{ zp0Tff%+Nd}Ku}pIN$ji<{tB*mbNWC9TCJ||?=ZXh--#;}U$@-ztg{KY&#;GwgzNS=1(K~hJ=PPC9P-59N5`4em|F3s9P~l515zY9a^mBkzkoQ35w~kWA zR%QZiHns7<1g1dGsE#v_>>8wx8OwjXR=PRk?=sQ9EN9;>807ts(`>L1s!(P=g$Q59 zKIuoo>wZ!+Vf`Zx25dyFoRPv5VEqf*r5a0hinnLX8#EYiXcR_RKaSaUh9GD#P9$0h zTivUphSaQvuZPh@e{lw^t~i0&WSzZ!?n%o!4{lhb7GM|5-56C!2e&NfBkE1U_Aq6) z*1?fEb_8_kPbQbs5TppMI=Gb0#^DeSYdc&Gj`10!(y3HPe+S_X*ZTXgip(aQYa6OW zDGVxrKg1;;d4s{&!0HOcQ@vnC+f`7#lH;0o`bU8Yz)e`FdmmI815&Q;zF$N3{SQIW z&XbEWUoYaWaj6qfIhrXME+E(3N#j~3pR6pibPNUe#)-Axm3a_|r74r=J`D!^jy}#J zdiI2>qGOn0^s3FTY(a`+@z(KMzq^pNF>RoFHny>fULiXy1g z4zyVMFA4({5Gt9@iv7a6diay59HP8=_?tE)p-xuTT<&iv*Z=xDLGNxh$kE&|;1lYtA z%R@lBwpu6)9;Kl_12(2fg11*}&~se3^Q3+qjT)UkQC>g0{Ic^7&s0$!PA0jCH~617 z2ejS*r2i-YMxBcVdD&ICOVXw;*A&(uhGIhqKUC&t;OdN#<>2uz zSoITJ`hE&r3+Z$%E$^%0hEH~o=Fr=nf(vPJUtlP%z8rPQKF7tlrBSr<4T8cMl)x8} z(GX0=x<2?@p9f1$%OmcS+D)7SZ|Jo+Gm(nr8-?ofysLkP@H;b1zlZRh9^X}PUGFl_ z1~=#GEv&-kRV+P^Jb~Wz8nUkFPElvK_N{cbfd2ZqE~5dKof?v4q&OxJ^eT1OTsJs*C^t literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/mine/2/mine.json.gz b/src/main/resources/assets/creaturechat/builds/mine/2/mine.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..3b67f613d5a911f94fdd30b08101537adeb13a15 GIT binary patch literal 6007 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/pond/pond_1.json.gz b/src/main/resources/assets/creaturechat/builds/pond/pond_1.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..78032f673fc701e3bb4db256479f2dac92371877 GIT binary patch literal 6230 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 AmH+?% literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/statue/2/test.json.gz b/src/main/resources/assets/creaturechat/builds/statue/2/test.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..1fc07f3242b9f50dc150fb604c1943490d107eb9 GIT binary patch literal 7850 zcmV;b9#!EViwFP!00000|J7Yxb6mHPeb3L}vv>>LXfyyl?$hos*bi?iMai_SiY#d@ zy0T6x|9#KF%-oBaJ3~^uAgOf{OCc%psL|*?{fQs`^pC5X_jeyYf4X{m_3Q23&DEQ$ z+dppq_4dQhzuaBDmB}@)q#1w}1Ki@#fvtTYUQHgLgL{e*1d$wj6!9 z{r}H5fBnmsk2jzGicgFG^Uc-opFaH0@3;T*@$-9rD8IS-`r+@l|8e!!cE9`2tG8+Q zyZ>q*`*QQ~_U;bf>xZjfKYY4<|J%(^cW=9I`lkDE|FbWAd=K@9pKjiN`1JFC{~kj8 z@%HBH-J4%-KK``(zwbWZ-F^P`&DURUzTECU^7Zbw8_Ma;-)`=1fBX3P=bvwX+?|yl)pZ4kVZ!h@M{&ssL$h*&9#{49x+z)qu9KH{~?WJ$==YPKG z@5qZzt0a;&0~^8b5K zsyS+hDnj3*g#4PYYME-)?iR*8MSPoJuZc>6OhIZdtW0yU#Ws}fvZ_!c755%hfaxgP zErU)Wq)sck}jy= zT*YfnPdUatuJD4mURYcSXO>FsePY5_gj4!(L0s{<#PwKQFKs0PjA+fd$5lYybb|*P zf~&&Xn|7Dc@Bsm9d&)T;tk-~pd_1!6Kw^W4^CqimQ^=u8=eGkHeyp!6FT@#$Pr#A- z@W)lkE1UZ`)tJj7EzOf;Q{7JL$HH>>aLe-AX2K?`(H5mQhgp^K+IJDs0d<&BB~aCX zV0*+>R98bVAB{vhuR~abxG6if~IKsA@vU5De(R>@BID z0K3NTQb-yTgj#_-WSx;g$r+d1LZY)z64XX$y^99K8VDV5yX!vUK-?7|M5ZYO(UFz1 zz=RcW5PB_+(@inXFcEQ3<}*leAb3hYy#p6qlAd)*vcQDg+@mD?E9wCz!Z4V2#Zg8G zI6ewyfyvErp=PB*{c7G|+2Vq-;BU-SwH1pq$P6S^a|(`AY-j%f$eJfjDIH-%ENbaA z9)FZD!%CLtP(q)K1fO!;rOF-v$+)B_F35s1LIY%V;^QP49&x_78Z}f~^e`8kFD~+k z%c3w5rQoVq3^`1%J8)g0{Qgwn=d5DgVV5S%n^R;RILy?h72d`Ng}*tNVBY5nJEsPp|}L=QStAw zv?9m@xD~|UA=en{3Rbq@#S-oz7yxeT%!dFg!0XSbyiCxAxLsC}Od$#Qu?aYY23>;! z5ew!~Q^9|Np2V1)e+Mcsl(^l!LpMMwAOuim$fKMpJ3y7{1rDI%vue3d_AtXK%lU;V z3RUoN=a=ref-ivT+eMY|jS%ke2M3Xm);G{t z5N?LYJA=xA+Y0O>RO*C?CHz>}DUd}-DU%|ua3$pp^=K)B$jTxt*zrWZu-v~o8U7g{ ztBfBCahY4rV6JPrAT$}U8L$U1`!?742+DW|dT9lX_9U&k)_nnT#)ZHL!72V_4%@xP z1x}0q8j#_n@ihln5RJyrMU?meoMqiWQfE-kl1hN(lbocSUO<3?mMi3`u1>O}F3D+mB6^lvTU~rJ|f5EcZ;TD3xgCUm1FFRu1$XfK15_HG1GH@%28SD~D zPo8oHRt=#R(54(BD_93u%CNjk2)IGd;+8zcj7b3mJOP1qmbCKA*X#f?T!+Nqd>?A= zFyUn3g7hIwkBb}igutwkfN%mK9H~s5w|8B4RBRnxdsanR}C>9!NRrh4d!a* zFfB-^P-SB-Agrz+K?TJ%@C@rOnr2WLfq9V*R6>j$bVODTHj1TA_&=ylSW#nuvW(z| zoV`}WWc85HNw{t$m#!e{^j_e91B%BL@dE;h0D??Ecg|WN+Nv-bEN2NTNPj;LC4 zmuHG}PIW{B2d_jDougcl+ySOm+=R^e$VayU53}oF^5KmA+c?7Jz3JZ$a7AgaJmD-UW@|RF| zTH@^qOyM^Qh?HowgElXB3d?9*y&#N*;{ofL337yY)}tIlORPd*L*!Kvyg0}cn7O4D zLnbhARdxulWeL&j0u1s2tBxH>#z#zS3n3kWE5qjD_I8PAN*5aU%CHK=<+z{=F8sMH z`PG4e?WGuFkITW?2BgTh3M?@t*Oja4M3*t3uDuml47`C0u)@5nZ}*I&aXA|c5sSP! zu+x)lh1yl!@sxJS5t*0cDDg#+y}rcKR2;Pbq0FQlarzUHg%^eP!ZLFnnau~?kh=kUGZf`3BLyd{M>1Q&=`DP8<6Wy-mRRrB_#)z>4d zugAtUJ%{FBLiW;D{~2%%IZ41s(&fmdvcYe_e1}e=?p|5UcW}^rS`h*)cjA@A&!^yB zR={&#Qy&P*cQd1f$vH&yrjpGJ>Re_IxC>s!ucwsz(6^jh1j0&ni{;{#|!C9TheREMJnXJEl288{6bG(f4uV+Y+A zAra2DCbc2-1~g0Ic|cd-TF$_#3St4a#l|Ycu8W07a6w|!4f72oT~9@|t^^$?kfMtvz(PvS`%OfWjL2?^Fw61v%VJ@!jYCpiE(i5wZlM65} zj+|VC$?T5xGk%%Z3s!D>>*HmV$1^7ngJea-JF1v%0{NC_J8zxR!Aq{PO<@Y= zd5hq-qXJ)zn!I#74eG?6*Vjb_gRBO}wipt4DTGhYXh$AK1-cp#C)Dl;!G6HWMl3UU zI;zZ;!_;M-QbK$@qP=3v34+?2yLGbi3Eq7`6Bg&PIHieD+F@CsSkd`s8!10uByN}mN@ORv< z?Qp;N<_Hl>jIQ`)2g*+TQ(YiNaCwfnCXo~t9wbR71e)Deos9F$4-e_vxhU!2p5g;2 z&!>5SQsE8Qteo&mHJuw0;cBM8q0W|6I zfjE5EHti(q0|}{bJt3{;xKP@?q6okpC#tF z;My5p-0lImz@gxyByzNf`MEVN4qL&9AaH}wY#Efd%F)ia1S|**B6Dg(V8O5&r+Wa%G-=x=9V)HvuA=aW%GLMQBmG2Soe4H*by#$L0sP;E}vns#7>I@ zrxOmttag+|my~O}6JScF&xvZ`WtDTk&SF0Yj#u}Pg-Q)Y*bZP!6=@e7m9&@d9v7QU z+1x(>mm#c%I)ie7=3L%C#=kMY~CtW(mZ=^~vf+MDXq3{A4(@NY?&dADZrJ047f%UHOkik

su%>b(nE3XVS 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 I0(GPS0HzrR;Q#;t literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/statue/cat.json.gz b/src/main/resources/assets/creaturechat/builds/statue/cat.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..9341b72ff0ba9053d75dd84901e1a43420ce5519 GIT binary patch literal 4846 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 Us$}xN{`T|#1KD6~dG~Gr03>`P4FCWD literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/statue/chicken.json.gz b/src/main/resources/assets/creaturechat/builds/statue/chicken.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..c39c8046c696c4d18431217972f673a71b4ff9a2 GIT binary patch literal 8579 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 JDZN+5005c2dTamy literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/statue/goose.json.gz b/src/main/resources/assets/creaturechat/builds/statue/goose.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..199b26bb928fe582ad1418c2d6502075429ebb10 GIT binary patch literal 5694 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/statue/person.json.gz b/src/main/resources/assets/creaturechat/builds/statue/person.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..6df54315d8ae25803cb7f775b2dc28dee222e2f4 GIT binary patch literal 5410 zcmV+-72WC|iwFP!00000|J7aFa$CoaeLtU}XK5F@aqHIOJk5N;Tr#PY>?pD8IF`qf z%afcs|2_+wB6n|+5+&}Y=y9cDi`OLp4J@G1=%4=fkBgi44x4<9bBX|UCEnTw>NUgOE>tzO4JnmJi6 zk}og5-oF3%<@VzG9~bw3+}%BV_~riL?lXSI8{gl(d-v&f{P~yfH=jOTT>It4{g+wLE3K7PH&%l>(J@$K`+|M_#`@Fk&HclFe&v#!RJ~Thy-+i0?{O!x?hlj7gRWN5bGb8_aAP)+^!zEfB1S6+Is@jlPLCm zTgar}m(468zUT2nLj0HQ*M{6KzubMl{o3QxM$H}kxM%F))7|%zn8VNiylhwQm%IBg zg?C}^|Mn}ECjR}utH1tS{q@)Q*RK!b68I9&4LNDGh^%*8K|o?UyrAU^|qo)L-kzy$m$VQdNWk75EU;5xX)+%p ztJF(YW`h^0Xslnz0ly(J|)td zB0EN8QUC}PBH?Q}r?dwdf;0$36?RTkHkyP(&?|^v$ukThRLKkCVi#aC80;w>#PuSHUCl$|Qe-eOww@B>4!8;k4P0NDY)p`pFyh8B zuHq{7Lk1%g;YMP@0~as0z_oVKWQK>=##;yype%JcdtL+!o)iRvsL>=s8|awy2;PPq z7Fn$4WG;C-F$1im#WLaR-0(X{loeOFA`i=CZ6Yg!8h*_#z*fb~QphVp_zu?rOGX^> zR10PIh|`a#Dw0-j;&w(#gvIC!$m$_buAJgw(hP}YSz$~=Vu!LN)kwvW^KT5z1~5XN z6xyL?GF1SO^%EGVij%QC#&E;OygT;kUMBW*(3uw9qi4wz{cFw;J6{T8s^Tv*64Q#n#tc_ysw?E83FLXKf6x zm34uz(qdG#4oV226DW)LrE*^PKu9{^(dDvt&bA0tl&GstvdRU)GU(4aVeM!T)eeH02t_04hzM%ELZMddLYjjIVR2D1I5)9!SAdLH!B;^H>Rwm=vKVBdMEE zfp0;5%PWLHMaaq&`YHHVUT>HMU0-R{N`M*bAwL>OWYJjf@D5&F#G*)e0AqoJ@X6qy zY`B%Y6CIx~RLrRaoe5)4F&?%+i&fC5mII{rz@+L}3<>|mk}YW$6S6bpjpkf|JUwG? zcoSTaEvFUuplELbCWOPpRo9?&11hMV5h9YSl5CRMaa$CWby6Po7G!OT)bglLz@X)FmGK&fCL ziCc^geeSS~=kt#|a~kOoN`Jy>ECu=c((uz56;3JRsSu-;!|DcSOvX)TQAtCoNbOFj z?L(-N%AnlrLbwi)iSmeN^4C4*ffR5hWgM2&NMHNN|Ld-L~K@{Bit^~M^kNSIvG<(zTzSv6>a zI8mD33iY8Nby-4YQ(g`W$qAU!R2)z|kin16szJkRQbP_6l*j79{n9)$cAkM+E-FZ- z1aF*Bfm$$(eev${5>N{LNp3*pA-W{bYU?m4MV>P$6s+n;sI?Tk0Hp&qx`?Z8CkAf$fF#C(lgmWnBMM5PJ%dURNB5B{4gDp=H1aXg#H( z4|_EoK(5&uXRCt~mjw&8EJszMPFq}CRIzQ+p=D+PETtir**^VZS#jK?E_qG_jc(qEow;Z-J%&J zwtja&Q4}(YC9&uMuc;|hC9XbEF*kUXOg)+tT|z=o17}N`(t5c@Ry{UBM1eX#YBO5d zHNIJ+g=#FPuZt^XfQdHovw&-WKlbQ=@NAXfo#5OlENsi8#KaDOq7?1=)k!w{(bNWnWYq1iy(i@>mKkY7Hjmm(1UxJiN{|&?oZD;Q9djb}DRg^tC!j+%1l%w@$?lPqYz`Q6g`Li}(Y=%| zeY;O$oKR&f5EUt4XWuDn7BvR=kcgKm`CERj82Gp^z|PPt8DQ$;1o$` zRp6MVQy`8d%0~XWXS3>3CEgF16Vlj|ZY>>8u*G)gk|8%N~v^ z$Fa+^!YW(m1y}S!7I2=jT!A#ijR!#qlA8`2j>}Ln4857;RARTGNAE9qqZn0KtXCFc zOM6hIqB-{WC8o-x29+7N)41a7lnnb9K1FByRYOcUs1`-xt zU{;&VCgZmwDN2OzPV0lEXF?^*QTbN;{c1u?T5{gJ11cThnW!1u0a%ED;1Fkp#e){k z&8Qj#MMhvgRF2WhcC%t`?$a>V)8l%Dw0tot8GE!7O)hIS=Qnq$ite3|)S#*khpl2l zRE1=-TcSQ>cGZ)BsyzO|=XpTe0tJs&QUNR)CpqW50X0s;tEa$}ktT`eI1E^_l+|R; zt!>~qlK|HghiokdOt0HE;uM+^w^kL6`*i4Vob7Y&+uQsSTUHvofCxIP@lV^ZT;e=0 z!?--BO(2YeXdFYVTU;@CD=%PTUX2bYaElNw~)-J{rEiidDQdFyVm_ud>Qv~VmY-I)%!+IW*CHv?=iaZJXDi5v(ZzDxE}&`j7eOgfw9wKIsiUO17a zL0o`7FPXwL_0Pa%G6I^6frKf1w5UjjYiU@Q-x}595rHE^{jX4cEidsHbfZr zoOk0(4F4la(FZaPB;K6eEkjIuY3iTdib9J(g@>P2s`}*aF$h8{mUOL=GSyuJht}W+ zi2+!|cqS)mhI(}@C&b)7G5r3BsgGwG=AfeW&Y6u0YjmM(I-h8WX`Lql6|;biK!@15 z*d7}P=^n3AFpv4xdD9a!&dF&(^-J6}Auk5|AP}I(vuazIqRXzpd10ONI0f^O+9_m6 z(ly+b<){RzN4{Tr!kRL73OfhIjiIk;mz>8P80Imlu6k<5r|dyB-dbX{$K@b3e!CQt z7m>WD^0)vsMlBQ}Fg2LeYAW>z(ly~7VSwPLwpoq=vgYQvb5DpRj-O`Ob~Pj^>(QV( zY}&1?Af<^~2DAy(ljLGEP+I!Anpa9aruV`pLvC;pl41w zIMjIf)rAhaeAi|HDA?B!QrwJ}W#gYzQsp2zO^LDHJcCcPk z{ihDA(8jw5-qf<{e2C)#j7jr=XPWa1V+HqxgU%ii&fC=Zd4#rq#>UeKCC5!VEkp*7 zC0%~$(bEw3&SHB8Vl+IJcFb9?5gSiKd=ck~>55Ia->SX!tfOc|4rkq0AK*HNeyregM=wXRAA?z_lnQ<;Hm*9rNqW0r|vMXS3Opmo7&H z%tRmzs3IYaS&xNolgOy;aE@C!k~q&BXxh!go}ixH--%l$;5j_*xOhdrDmeIku@%1)Cpp$`vS%YY2$9tCU>|%pAoTi8?GSRHmb* zj~0a&BxMtFECgh+I$hNUPNCe)RC0!&>T`fuv}iOGKu`$B`O%^T(PX?Ww8lkfG?Mqf zR(nA{?jFLxJ*p?uRu8Pc7N5-=jNx!SbO|7E)9z7?$m{!PV#1FKLy$4~6#=QNF&gCx zm8KZIkjiuoOp@sNYX^R>4q4j4R8nH9EkgMAHEBTQQJPEH!(~`ghw*A)?qx*Z~FAl5q^G^DsG^2O)0?I#IIYIPckItL1oHaR4RdG{a>% zh{g$VZBN+N!#JZuKc^PwcwuIj85{dyrm?Jq)e1+Cj6<9cqm=Q|C~r_kQ$O`M4fPaE zz`^3ItZ*>l4fA1?Wh>E(X)j(lcbO}(A7!))!<|(SfT~_p4xo&K_Mj&XMj8*^yLXxR zxF2LxY0aUmI1CG<UE(E#>G;MN-t*P=+$Q-8X`9#=8=FXPk}G#ARwy^ zPaaRwcDPJ+Jja5-ADLk0*v?!-^fW@$xHhH8COf10|r?!j~`#;cG4x_D6^{Zrh2QEy~96Xi70s8hPkA)P9XoD^lvBc$P}0>5lesP-L_XO&C`V2px_W zUB%UE(I4e08W2qQkstCnzuzSzry%(9{J5-Raw~WcBA;??%`uf2q}Pl+hxRFHk1Ft0 z+-m}WYH?2rGXKrJCNY%)!UF2pUa7jq7@2IgC_I(AqF!s1Nbf*2CodkC+k)#Zk26=% zV+6$tKCi}U7};DZ5)(F%g&=*3TN2h)3yp&WUa_Xxp!V9>qz^8Qx+ykpO6lNzu8>yb z6obGF1Qdo@Ha7)E-d*t&$PXhUu4|Ao21nv(9AM!$0!fjQ_;~w~aC#mia!Oc*I}9M# zNG@9kWF>EyxsEX5=##9}Z$9Kaa z#D%NO&E+H(UDeGYx@cM`F;u4qND?2%I>%0vkWKf_@))C$;*8gS7PmD{M1e5?06*Sr z8{@24LXw+WB}|jN$2GKJHIiD9 z9aMxDsKN+9JXQ^FZ-FX09|#gt$jo?KRy1CXRXHMFoFvyD1r--X@bFx)UR_?G!N2|d Mf0M+qsV|KH06@q>)Bpeg literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/statue_fixed/person.json.gz b/src/main/resources/assets/creaturechat/builds/statue_fixed/person.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..961bfe5f8cd6f3ab6c9bfbb31ddf1e4371b04edd GIT binary patch literal 7998 zcmV-EAHm=siwFP!00000|IJ;=avV95z4vGEOohQ0I2=08Y32)N>1k_|RIDmlt4bP) zvYPI({`YwvK}H}lGMQusNJ@t)GMURuz~TOU#h?H5e^>85++RKB8jREUPm8$}vg@n&@BjVdPq)8by-kBne`9ea&!xs6mzS10{>v<6 z?8?_yUvEBq{Bm>k_W!Ove18A_)6KWr&%fXN^^bR-K3%=Vi<<|p-um^`x8FX0zWw#D zcMsqR-@bhP{^91Y@4w&P24G&j1kZc-@#{A{{LkyF@4tWipYJ#S^6B%3zu^%gy!PWi zZvNxytxiAv=kx*i>A%LuzP$T%b9;+V`SaCpAAi63@b%r#w{KUUf8G3{1$yG~@mu}* z=H0j3|Nfqm{Pa5>yZ-(2*V|v4-@kqSe*gQY&wpHh|1$l2`}O<6nAx{P_FRJ|Bo*^Wz`>`jagD`R8{ZKK}m8{R{B%*T25|^z-!l z`_GV~->$#?`tHlk^vJi{ukS*5Pki{3J}tg`zYkjpetgc|o5V6-w;vnpbN%J>A2(ln zyxX`#XW#A#`yNvMoZEiz@$qrbA$5QG=XI+$zkL1{df#j_tu)+Z>kBe-;h>kUo7 z@(Tic!g|R9%Rs9-@)`{hk=UOfsSh8!ojhukg zUw659AZMe-Ud!Mj#Eg&WnO&+%iZwEvmM&)OYEcKpW}R+7A=zzKEUOiGeBj^70=`R zthnZ#R3R}C(&VBlK}H{o%MCOqgU8OeG(ocp(Ia^d9|I=)fte1X)EG#?3C(zJk8Qjh zl?=t;6m&$@VH1@AqFU<;)d8$0?XZrEmYCv??s4YI7FG6a;QVgRBFXq3l;{AKRTc+Z zWe_jWvX=Zr^ z5i$-#RRU|EN?B)!_)85dv0%(YN#N8y9PJ3gP^wgF%{cIPwlTm|Fql-lz-l`CJxJg) zSY`6044XACq~(Dr*%CTY8**XYFvLqhVhO8jhB*oWu5=G_-rNkIoQf&jX&4`!&!!d7 z>5_Ac$|J(l0aS&zK(O1DGu>_D8k8kUc)X~nM>=HQP{;tY`f5XK4P|&aEbjrU5G=*$u-DgJt11J$ zy1eoky9LHCM+Mc8*&D~d7o%s8g&f=kTM?|D%GN$h#>vVR*cb<_bruU*>-+#E;jQ+k zfjGQQhT{oW7JP-bD+T>+6}#eq0RE#3DjS3utJf+lFgUXwizVWCr%Fi+)?XhFi_!ot z(?seOE6=sODyZNH?Lu{_5BfuJ)od4HJh}c;so>SZeM_)yreTS zgiOGYUlkO5FnAq{vB?4&54G^B8%SU=nNkyLhC2&AyWdqj394iXXK_s9aAG}=QHE@z zg1v+2GE`0geI^z`t5^iBViEKpN`|o`36)*pUPq?lJW3vFn5YG%4xDPXYyFyD5Z6mCmm6#k1U|Q@M2hzy z7sYaw$P>VWscAtqM^iHhAI_FC7&CmP)@OuKZ3HWU8&=uBP%^-z;hm~FzIL*F1d?(E zWALPg^VOU|8sMxg)X)l8bm9HR2AI6UNjFdk5Mt-YVj?*F4mIAW1rYsQ@g`>^T~{iU zk6G!%eE60-dnf<2%>^h%ptDIPVE=gKs6rO$b#&mWd@F!WP=`6NcgZffIrIJOrGcSn%q zVX2qF3=rVPr%JVx= zjqn_%@PyV0(b3Q7k)YZEDoUtI6O_9<5(gUX{nwj!e|w@2`Rt%B@EI@1#CU01Y1sm! z2^aGfHxG1%OjE-Ip(_eB&MRWdbx0|<>=X_;{@#UiU54q6N8Cc%q&mzt_U;uA-kTiJ zdrhgS{*0Y95;}P z^Q#EfpFtiM{o!dS`2G11rZbqJ_J>lfsrV|enIs+3*_UI=SXmQZ!h?nIUbM+NJ{*sh zCrR6RP#i5U%_+4yR=n=mymN>2B1~AlJ@q8}1!`o>O~Mfp#v+@Rk{87E1{4c^EU)U< zC34O?6#v8i_9H;$F@GUVb;Q`(}e$}xX<0IC$LiA}*pjhEy)cF${s z>=smGkEL{RxD~!(v^Z3bvPvr(SO#&1yAP9;G`_+TtgNsHR+=#Qm^4=EqCB&bC&|u? z4Un|Ol`V6IwM*DB(zIywAj-y#WSIKuesKU&w+cm(Yy+ZuX zHQpR$6o(4*Vpx$-&s1AfiKA+@g9g1Eln-2G6^IkTQSIU2Uk)m=DK3jqj^czYvJtC? zEui*ZD-bHD+T{HsH&x1>c+FX{I;ea;i;*Gw+>~*g!uYW4KWXVQyyrIZrRP9e#B02X zOH6Tc?-7wc1#w-QEoULZIzhWNX_$#xJk2T5l~clJm+=&zK8dl^+?0Hj1U(1OcTYX- zIgDpd2{xCa|EeYVSn`cK*H5F^K{Z7^2NCK7rnrr&^8o3vjWK;#zvQP#pgr}FioZF})JTe&UF8%)vTcQZ@u|u%4Mn-BsVU&wwQTY|kCH`t9$9=W#;!Yk4iub2 zRxK+_S46s`crFEX7e8ffT-M1C>rnZ*0~Qz{2t=ckr&#l@YNQT}HAc}I9VZGEB-+V0 zyb#scJZz25h+19wnySKi zWj^IFFLo-ac~qyKrAdznI@sVsO~G!cFrz&+oo79q90x2J#8+{y%2|w9r(AQ>zBMHb zI&|%x#`rW>5g5l}X>BqFQ#k%PmD$tWFklP|**X*Oh-+BfGiE*SG3(C-@x!?n!NJHMXnABk)Q-I&4umQzVg(pWM-1 zF9t;}*t)X8M49mqYXsd<-0$ZjkVJ5>G}R<0P+geYi#BxCBxX?OvPDHPs0eFFSgD*B zRAmSWlonNq@Nkbb0)!R*g_BUVF;lU|1Tvc-b&y}nAu)65h7VDB)?mV_C+|=u0S5^5 z8Ji*sa|1co15iPn@n`6!YEhV)Y+fkn=B!OAVaZYC#6605P2lrbDk+5^S9{PHz^`-(l*X#fc$ZCY+}+CUiT%zfGc(~X7`kwMcmUonV^Dv)Y{e^;_RC3nRi4B zLJ=(-!+XXdDl^1mO)#e+j*~-E+XDloM{u5dY_tOi6bZx*?|67_Ph*1H$zG*t446_6 zrAwvz#i(KonKczMQK!siJzB#q#-#9BxJljuQ;c%zFgRfcs4yeiOexHuyRJQ2?stJI z)|PTiDH`rj$9Fj>sQ${58ZTp*(soL1Juj#yEL7=@ z35QP!gzh!vd>Rhot8hUYNAABaC~%D0)M1Wes%SG{?50@vTRM@Ur${#{;o%T$ z=_}}7h7$cn4J;k7R(t#)*$b)$S*)plR(yYE@4{Th87`ji{@`ov1T==KSmsMHS1etkh7 z=kiKhcoscYwRQ;C@bUR|4Jc10*pdVneC+gT5+0!F{tB34QXbGmyF&sfYL-bShiBI2 z4!YhWZ<+`!JI4LMk)NiEt^r|73HFR@IL)QhS(n49wIWTd=5CO9hoM+wv2?i7G0{I! z-G*wSU@*g5rCf-8UZ*Iz$<$5}KcNf`HIyGSdxuf-OF`k_MM>YXeWW;ec%L4yQQsXDrxFxQKa?t9OKm}q5oTO#p7Em_k z7{%iW!gC6YM^{~%neWq>mM&FRNjYQ{yOijDj_r(z;f^PRKgSl6lk+_69i}C&-`UKw z=~7me#(OUcwops54WuY8jG5ON1kP$Zw%ml9V8U3iNqg?8cs!)yEK#HcSJ+Eq{upGX zZETwwyu0LpFNG8(%To%k$(>8>(^YUOsEGSWS}jzc;Z}Vy9oxJ^=2=(F^R7sl50m51 z4C)d-bup%q4L7r`rVM?L{M!job}$V>&p{l=Ev6=qS<{f76d}&5eWWw+%CXejK6n?^ z+Z;!CbWFvA=n8yB?u72#)}FyL%@b&vYNndw4oRdylsMXMqcTh>AAy8*sGgOo@e*G+ z(v~>3Z+3AhfTP6K6GRR^BYJ=dtf5F;dzTGcnRu=zupo~$tuR4-?77=*X#f;cXDn4p z6;yDcrN`^IBnnzev?B_B%nv{WDFjRHkq@jNarZz6P*6__GG#e? zTsv`C8bV`*V#QzKXgHDFH*-e43Jyyn^HU%7wh!ZoACGFv;%gHcncDC=>Rhv=1@|4Os*_y z%xlcpq3D~O1xs=i%jc%`XiN=9bl@dm1)F!5=+MT0XH+~^MQfYz8HZESTOByc#W zkSm7gT(+nxv^)%wOR{hr`G(Txl}3LeTch5m zdr&m(zb9}HtZ~3ZTXfU`R)^Jc*oJnjVb^1LF{&E#xtbC9Q-ZT}iQ8O^YSQnmA|3P9 zyCzC^KvknY(%Z)2aJRj~XoelAN+i{5J5ylnX6|z93PqbMzF=6DWPTU{2dJFANL+bp zRoend9Vo83Z%~cH$~p{+Wr#*D_(+wwM2@!6zy!oGs<2uCsX4eJ&^9;0^w>QMV}cEe zTV06rwo5bBh_{UaUHrK@e;Q3#}|l`%ZHbSqOn zf49QXnPBUQOgm7;GP||;^RX&vkEH*Qp!V(`kJlMmo=XzNK+7yj0Y$L(ny)glh|;B| zyK-j84td0^=l~N(1G^Gbcm}oSrTmDd{+D)S?l?n-qorP4RG9D`w`Pe2FSRj^Z6ARQHZ0|xylm-yc<^~KMihGwP&`S~N z5c&MZ&fFFHbm=I+7*oNk>dN>*rPbHmxv1TyX42$c4E~}at9;Oyb2%nBFKRv4iGDy# zzE=@IIpLMw?p;*ab*Uk^5RymettlaxvR}HnEc;PY&)3Cl&kyQlHRd==;ZTVmKsqx- zWzBp}M!DOU5z()~CPfD8h4#R_$O2?le=e zbm^eK43duZF^#60W~uZTG9DK>Cs4aG&NL0ixd9dPH=K)?k}D`6H-t0QsJU8TVgEJv znLCpul?baRWq9X@qtAJ4*h^Lt8?mQ3t}X>+4i9U)eNwrH28}JzoFUpVKaUSb>hlI*y~g3!CA! z?78JHy}X9F3e1hYhR;`~c;Nx4nB$+}C|hJODIfR^>r1-@u3?vJHZBDfWpwcC^~VC` z*xE#^m4cXUCK5f0r^_ltwFgxll6ccEG~HIT!_fSQm}oDkky%Au$D$I(IdD>+#P*J| z;%5i-l5#$FVJB|xj2I`Ca)*&9p&Fetc@eOeT<+(}Rn1HZUW8(B{*_3K`K^2*xxZeh=q2-WD#h+{8 z$S1i*k@*5Cavmbj0?EMHNR6QIqKMqLpdxJ(ON4#Rb7}7z8ci=ZbAqRai4GA(8LEtV zHIMhhsX3&=(HyGS--!K=wg0n_$2n7}?YX0HV|HVT)L7hamn?EDMYaN^q@V{W$5l=; zM(pl%L2%B}KpIPFJCu0ozz}fL;O2?b5qXjXS0aM!#lpE*AVnFr+lrdQv+8gY9x&Co zxxI*5*0Jh(qn>c=stKF}pNfHIm1!|I>cB(p(6##rOdh$(q;NO0$xMQ$8z$N*MDVMy zmK}z(?7$QmCTs6&7%L|>SDKt)D|*b{@k@bvI$PO=Nmlb&_G8++c@-+tt)ah@pMH2r z=9zKfYo?iA2GfhmI4e}9aFYrh`vW^nV^CNQ_;FA^!r&uy>w3bTl(^-s!qHTk^Nexr zSByYdtlfA6au@X!z3?KiQkl|bl_J2Bq7M$5DCY(iCNH7iAC41E3Z^9VC_+xf82&RTm)wY=|GRJbLnKolH9@gROyU=HS8510^zv{&sbuC{=33^scih6WSWrzhEge*Ng>|pkzzUa$H;3*)NR-<7HFp&)`{SAy zWAgZmt-|EN<{p__IBI=mC!{QK#Y$s*@4SQkDZLYre7RypK?t@MfxTja-x% zpWD@l&T5nHyPG@GVf?{KglDCn;Gdgwd&B*-Y+b}3%K{5hBW*ujZ@C5)#fW3yWCmpR z8B`3pizxSksSGi-C_m`{tX0<{ZCOU&H%ffy{?>#BRM@{zriBVzcUwTokij(?`5H^F z%bv8%aw>D&FF_Hm&YbRCQ92lLZ}SadnkCG+VRdB+3g786c*n)s@Jp1B*4l+=)#5qD z^|z6FNcA4$yl~jS9DFB100g>sjtE@E)Wt4xe-4zvP)oEKM5FQSosPxL zsksJrnm3m_G-PPpt}c?G6QT}PHjpf?9flNEhUrZA%QO~&(?|yIA;n!XsvUYfb&Tf` z$Gk_#h?fIQ;9BDi%z`C|1-sul=qD-71R#xhSgFe;+M(IwAVy13D5VKeI;_m2c_fMz ztd)~OV+f9eEYgMNOOvgM_;I)vJqm?Fa0@X4QEG9sOMzOdF<&Ip zeaN`M6!r1y1yo&4=L;_=?zyAd%rxYfZx#nLz#&XL=T8p@KgAA*YZw;Cn&V6m_U znwM;M_&wnd&9EpBiSWhtyBVSV@U_|CYUmiH+rWb0j7?G4UwCU}si2Ut+TWV8lb9RU zW2tZkV{|!Oy&M|#-dfL~u$TqDX>^jRL@+8-i(HRGmd$Z9;K*{f`yOb63u4B z_<0#q8@;$CH}{l8foc$q4wvBW0v1>DX)}NB4Z}T;%bZ>pR*XBP)EJ;Q5?OM{=YI9D z3s{bh5p7`MC`LqJhtrt$!_wfluwLHCR}`GPceoNHL0y6igyxFUgN@}$usj?o01(=E6JN~I<&um(uzfz6t%A8)pQ>@0dUkfZ~ z*n+RtN6%}hZ;!%mq zkV=_@NpI9Z5sC7`;=@Ucpi_qmu*blH8;#{6uocMGQbH6%EOI0b8-dRNrm4eBwMXGn z;pk_ZhpWn_(g|+r}y=O#4F~~8ZR}QNZmy>N3eB@d8@aap?cu+bNQ^`tQG3@cSLb`UO$@W>(4icyS^ zb&#y6dz3k8|Gaw$+rbqdJ0|S-j+U_JYW zG%U0OHYV!Cw;>3R%X=g98lz-gwZ#Rrg`Wf0c;_uy6t81R;6!YdS2X2<#~0|-sFGd| zB>P~LrL{mfMpQcRQ^3NJN;y-Gq_Eo&SWxtWvj~sbWt^I^z!Gl4^KT?d_+iev=+u6& zH0C)`hf12As9;DN)I=J`swk3H zhw?h9y!-wbd4|+*(!4}=gV81!haks?k>*$bGQ1-`Q^zk zH;ZFj{pReq)jt-;_5P=SE{^5?r+=;gcC|cT-R5WCFMd6{Se;xiKi(csul@e&^7HC? zb^7l+qI&vqd499{fTw==kz{-Sad~of@$>z8tL4q@(WmA4$NOK;^BPB=aCr6g537&2 zN0(Qti_v9|=D5?{)zwSg>EiPG_S3f>>NT-o4sr z?cVEv=lbx+(O6@yE^k)2|4%uo|8@!y!O#D_|Munn+i&Y{r;rfEoX%sVlR;ffT7wWN zH~c+ME;#QhPcIG$l7PkpQ`LZ`d|7^89KQ=MsauTm&TdbjSt3Om&Wz_%#lcjaONfwc zsW=Mvj;p<8tU5m1YW0Dt7qSp~Q!0!gN0-qE*5X-?-W1A8V7zRALY}pHI1`szn{{m} zU25ZQQzjf!X&wKu)hb&iEiJ0FEmM_ZIQf7~-$EfRNJCszsWR6ol$ljTsk`>5(ZDmk3WN)>AM zXH-uJ)htlP#5^O^4CBY8yGtJpAVl2_^LPd<@6|Bn;9}k8fPyrW zTd!f5(h<|5+?yf6k-bor4I!ZJs4ALk(}pPY3bs8|))_b$V#ajy5tW7cVD(@ggu2Wz{SJZJ4HZ(fvCJ`m z-f$({(6tBkHl<=@9H|9K^Gdw6Ro08tPCO13t`T#LU(_^K9aa*PFrC~ElSt)&MP)(B zks(kO!YefB6r4NFf|F{U0_|6|H0nH9D+mew%f1*rhH8KQm&2e^MR?DlkKK(|gyM^FC+J4@@wi zVCcHRVmNz)@_aW`whK!3t#sN2k_SNi0Ek`#=;>WTijqK#P%mr113v}FwSs6So;Z{z z4X?eTsM0bMq^+cdo+NNXI|Z#WI;~b{gsM4`a7VG zMSw};@Z}YhZZ!>QXj}yMo%T_VDk22n*0Q16GFdBljC8<*2uOBObI7H_Ul6T3CCK5_ zhP#?Yt8lw2x?U67PL!i#qV+xfchpxfV9gq%g04Enm#ctz7XLQSl zdrL4m61)@YdQ@%G=DbSB{W}?k7A7>tP&wdpslfo$QBxRpHU$5sL6VBD-BEPJV4EW3 z@i2XdfY*B@-G@YPgQO3T_$`p0Gj4_{-qqpwbJ1if18GhAT1_=mH+MD%qTXwNeIU$y zY3R}5A#-E(D^N;+bFCv`Vq8qoht84S0%ck^ybeX4&bORsY!F9Bg@NPSKuP3sz-V~~ zl(`gRa}+ytC#tRIz786Lx?5P@r8EWN0`8z(qq8j*JUBYjVTAxi8FlgSm9XqDBcIR7 zo7r@Q98eg$5JAh=C{=1xTGlnV_OXU%LBNcefeN+GsVU|aQpWXm2CXxSRa6sZdvwW7DXpwuntsU5+FjTiLwb33R@6D`=He28woMX@=lGs zls9M%bZQ?HLS(fgl)bmMS@lI}Ltm;7oJyXwTYq;|v+ZjQ-tAiP*QT<4{lTkKJ!kyT zOR7Po(AJ@R^zUbig2b;QrnRe^WpSrUs)53FV0QatC*f&CN?U3tS^?fOe&SXuEHXkB&Rf?Kg#)g6XsQr8*!me3R+0%QxenKm zAr{l!VQ8i|?b<`5!6t7O#Hg52M2x7`#m^3(en~|VX-rUm*zO?g0a{B` zJA7mK*-Whv!SIldnYvm#+)6#02?i6r*m$Q5A3+Z`0})|9r%JPHkWIEU^6eFLI+$K) zVX~rqCF;FE8Ptd3VlbR&hUR!rqnBzYTJ}4*QwD@pwr3KD%o@~j%=n}PaB{h z1g5jR=7UzwD_l1bXZ_fg7YfhtOE7{SARRK7$2lP|gWj9U=xfs)+yg}+-bBo146j>eb=;mj3un25vUL?v=d z7v7`6CLv7Iw=y@K!vQlK%ukLrerVC|U?_vN`Mj)ChD z7!~HeXTcRv0k<1kt>NThyQ3GJK6)9kt#{xjduGo|awICW8eNt3J+DGNo#}@U{{ud| JBZUVs008%9fe-)y literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/trap/2/tnt_trap_big.json.gz b/src/main/resources/assets/creaturechat/builds/trap/2/tnt_trap_big.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..76501222f52094c106e32a0753e28a759243eb57 GIT binary patch literal 5490 zcmV-&6^-g2iwFP!00000|IJ-pa~!vheZN0L&vF+55FqHs`?UKD_QRV>DYlZR;z(Xg zu55BE|9ua7q-pl_*s{Ah8Y`)?<>w(_p*TSUi(!sqy0Uqos~t%0kAT z`SRl9_1l{d*B7t+$E_ z-9OyjUjMjzkKjGG592Gpe7w59|M92ycW?1d`uphpYG34-fdIZ!UhnxxIe-@#^P?*A2SM=AUQ4-dsIAT)qAE z`rZG0N=e@ReD(hR`ts+iw>P)H?EdBc?$g7smmlu_xc)eM(+?Yd=k49cZ}^=DzwY7V zr|ZjiH}_W`f1f|$`s)6n{eFB(^E1omU5+2FdCMQyvtKdCGuys5)6?#6-pw91em_2J z_dRL*oblD^Iq~hf$K2vQ?PF5=l<~ziPZ@vk3m+zZ;j_fg&Y#lA^_%v3{o98(pKk9Tu6}xd{monQO`qb^hs9_3;qS{WU93 z&e_TlRE7n}BssAb5qd&ZX&_Fn6Dk`hS7+`ZT~#U`Y3_wwlK2r=j3r^sDphkWtb1G* z!c=U+RfkHVvSAk|Tvijd&>sco&*35huvC-pf#n40iX}WZFdwkRY|~a|8WL#4 zW?VE#P?AzIRnDApKEU-V4OBQ4FF==5A|q~biKvsFV8v)qrs{(KOH#6W1R0wpJl(Nk z85GI)z*q*&_*f?bCIUU+CR)8%J%S5|Kj)11Ff@wqPb5HO9lxJY<%(0=0ThlR6qh8f z%vSYKzJMx~ES%U)!MbGy%jqQM*=x11;x-MFj>Bu1uLwF-#;DeuGKejw-_d!i}1t^5i|F z3R<+*B3hhC(lJqY7y!l^;HYH@t+B%-1xDG0yJI34RXh?V2e_b0<(Nt!FVL_`tO^3r zV9HXw|2jcU2`h9q_zDgmg(G~32|Nbk%ppC=SgP$sy~Nh*>;O7wLl-k>sj@>ig4e*> z(2=zgD+IMJaKz1_IP#v)00z+J6DWpFxG3Rm6^Er&4OT!bI^gn9-3F7RuoS5t*+B5m+!F zNm3fQFGKimhnD-RG(Ig9{xh&rVz1y7;9ej$mf}6%hHIU^{}Qa=eQ=DbgF%Devre%j z96jM&Qga%65(>CmP8y+Nbt+3-3hEV%kwG1?mwQeK0$2m<^Rc(DQmb^RRZov=)p99k zV**KsmzsGA%XYax_f%>s`#2JZ&lr*DR;4mSd39l{D^XRbDaxs#gc|=K=UgeGG{7U* zllU6zK~)&$lD{iV5^BI@(hKlBifZl>23!kDVUc0_?D8a(W;H4gt#4Dhr&lz78zwK* zkePl-l?|v&22E01nIbqra#o?TJXElV{9U1P9gMS1WC7PM1E@S$B&N;@D&rOMsx2t^r=_fl zvGGD#S*ak{CPutS5P*GGpvmlGc88PAJe%-~TK6 zde!+G@r^MY8q@Ca+#8j$#t2+^B|*x%>6FfWll)W19&|$Fpim zsz%HDtU{_$`%|Fbv-(tQ1g%-3fS~nGyA@x6;zr)BKq3m1_+d>COt2+2U^b2HH8p7p z=%->jOJ;Vfb6|Cbt;u7`*%n1mV?3=uGbQjji=eSgxGc($h)%Oy>v37sl|$lMTt}nZ zhZ>%a8R=J#EXz)UY-^&Gd+Z~82+0j7?5nfaKJ0xhtZ+^9Skrk5#kGF5@LHVG#}wrlGG*3`hDT*v5{p^FFEnEYT||Ez6?>PLDP9Oi2_E~ zL)&;LU~){rmZ68Wz{V2J%9G7DY%PJuo>jbp`pNkH6RJ8;RKT8h6YHQgwZ$uzBN|^f zEY@~doIxa@R2xShhX7}wOG<}IHUg=FD;mL|rTa>`M8SNdb8{imCx)XwsA`@LA_$M> z(jopHEo5+D;527YLMcaT&!e&%ixdF?IJt?Q*kI_W$YHjiqIR}RDPXJ=Rb~NKavKY4 z4^V;KIj2O0H5}``T#c<5(nN4~0f-)*4I5yE=9FDI>MT9pL)ziOkU=`&$vgbxC7pLo zQN>f$qttFH0?c&)O!bC0*C9i{cdE8?b5?jhr5Y`&0Mk%8ybSD&dM&Q|_=94dA#KeL zjx`QjI%f8uq#90p><$*K9+m(?Q;y| zY>iaVfI1dc9Dw4aX)MiPs4byMv%~E%Zg>Mk9Xrfr0|DF{oKmBMB6(GuaywL^`1J4` zK{k^SGaSi0lk~_9PN-yHpzg7K9;ym5%oh4kmrDgCmSI*rkw7`^UBsZqfLWVhDOA0A zpTy2;Sfi**n?!-jSgWQ5m3dKE;4N^Un_?b=O-U*`RhL3WaFVA~Tw!Y#J>B)wSr; z{%O>!=iF3wLoX;N?+^@G4@w8I=s5VEK$SX-U3U$rI(T#lo(~hGI=V9&rM|O&XG8== znY~PIP1I~c8Fa{#NFtW*Y3fRYK-*69?=fMEy>5@Dt;O_;A|D8&(->kIp(JY|70TrA) zC7UI@qE~K%GCv8Xi2HX)4)z44pi$Bxzya1+z}le9;f`CW3mOb{&dfO)9T_&LNDGSK z^fgicU5$1RQ=A;2q64*yWtL;YukO92hlXc#Lhi9aH?>OML@c1N^%%* z0VI6#NTA|Nn~D#R=3(xIKIKn)=mQ7ou8}*{nD?=C7HSS#W)&X2n(ur3DcrqcG6OYM zL2utYfuVq;g!><31|My4UeeZNu?VgB0Wms7EcnT$ zh<&$GsY@QvBx;0G%(cjMSt$^67qCu{=^7q=YPvQerwqc{u+Ci=s7DB4HLA$@C8aWp zEfxheJ?mmc;G8_QI(=7i)km_r!h*WUJ%VPdQNiiMOZD=={B@cBu^v=}fr`=Pfnx6x zvtEs=$bkLJi&l~jW_b&$JX9)$XS0EzhtDimC0zH}2p59;M_Vk8ZwFPt)pJ`6{aq{- zj7~LgtJA&*!HN_z_(XFEAIoVw@R*3bNECXx>yoX9n+F~be#$`_#=&ZjI@F>#p(%$^ z8h=+0whj(g;dL|Ex9$Y@fLp0Wu^tE*!C$bw3jqjGW6QI0Xpra9frdgkgB7!vTAaX@ z4p)jIxtF}a2PTOE0BQ6(7N#lCc}ypF33_5vVycG;{;P-NiU#akl#mEkAf;5N!f_tU zr0F!6paoOR9Ez6i#jvmdWtmj95V&f|PYVS<3;soI#(rqV;fwZ?GlEi_J!sEaJx9)6 z{J*u3Vjcrrk4d4!9OjD6J0~Wn1J6x7515Kuhj88oOmMZrshBW|oK`@}+Q4 zdLdA&7Am|tc)@Uo;cCfYx_xXj@$lWlz|@-eklP?_!m?RWzv z;uyCDQvtVhH?d$5<%oo7ltIL}#l26e@D!3YDP%KzfK8qQ?J+?;W1YCLKlX_oV1kdL zRw+}YB(gP_tXiuKq7*u%9*Mr^F*#gc7!peG-KR-v7DkWS^1PUFU3$#o+5jqkRO#9L z|5wUXIyii%hP8qVZvYkED44~eEj}wIuvMyQRsz?l+H5^2jRGpsS>(l(dlc^W1O;YA z%ITcjhus3%%d>WthhW2DGefYK^f zauTd(^jtrIidIe>zFGMVH0g5;&*$o#kE!#Hpmy~)n|c%J6rkeXg6(d@225}}<4n%{ zhA4LF(9gC3l|)}Eal29_@~h8PyVNg9)%Fb(=!AA|;!#mwQTW{#QWU>+2qbI(`9nKZc;M4 zhPtwW<83~C01C7PTEtT zw(edm3&+s0785v2>Cncr8dTIoz;S6X>0r^sc|J&NVNN8?8VI7>sY6%FdQice+6kbK z)2hz7P@-5C);wc(kf+k24t>2q2_Vd-#XU;YS*83Ks6H|M^_U`KT$)L#7IN?U>;zs) zb9FvX$>?@i#m%0F^@8d=<0p$X_sDd>w0ZlYT%+n7)W+1+R!r~?U|_VIAa%7GQ`M-g z&TV7LSa+)D?2 zbKj%dFb<;M5a+ZGesT8PMm6G^H|sHUa6PDOktC94rN-9Pb4q!kDA#Yx@5&G(0?#Pr zkJ*^q+^VQ>A`~7&7co}{CYtic1fm_0|#(>*6+f>xWRu&xS0)uOl;o_DQ>%}#OoijSRElw(+M+20-N zI(V&VQTn<)!S@xAYMiGGKdNGr_mdF}s=I1!Zzk)O3SA8a|BfMf|t0FRwaY#%FAHNNg+-i1DqD!kDmwSDenAjn?%QJ zwK`BviAm62B*vlR&$O6<71thR9H}5EOwp(yyjN2OE94l>I#x}$~;#LxSOt7HE7Dm}djf>;)p1AP1OqIDYd$H~N81G!!?jB39 z02RUhF_*>|gZBCErg_iYSm!o49(M zGuq_}`%qLl#wcZ~w!HH-GMvS*2GMGi$C-?~xNy{4$H1@_RJ4h9L8Tn?q#?{8doc%V zdH%+0zcK`fW0tP$1q)Kd&?wTvisAm-i9+0BjAIA20KyB_Xjf@dpRF29AT;nRW$q*_|xCnV!={G2YHWStm|Rb2qQ=n oQfG%1f}6D6UET)EqwsUmSX^Q}Bz*n9|MA2B11#1k)JKj003|1vhX4Qo literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/creaturechat/builds/trap/tnt_trap.json.gz b/src/main/resources/assets/creaturechat/builds/trap/tnt_trap.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..65222817ddf687e4a195b32b05c262f8c724a5ca GIT binary patch literal 1248 zcmV<61Rwh!iwFP!00000|IL`oavDhxMfd)UUgL_&tjZV5SPwb;Cb3n_c$NwaJ(=8LwOzHe@3 zub(fL?P_(qY@aW#W@}ufNwdE0?))%mZm;M6+_t}5-CVq0HPbR_R`Yl5ADc1Vw?CUH zZ@>Muxwe>Hwd?K~XU&`Wb$hX#y;x6QE@!LN^YiW*XRGU*w|9Tv=3Fjc%&u1LGt9Fe ziN8I&yEAss_iAz2*MQ*{~eq zkOX;rmpTRt>lMKR8{=pl84)}sm6cRANA0O1Aunls-!%eCx~>}(eOAFipl8G`J|z}f zW!xlOhFApxa)@9Nni9!Hy0ap5zI%=t9Bp%VET{r;vip;wQels)^c}qtY9I{qA&Q_w zth6X%p_P5l`4yarNO!De?9RFLSV(uG!ag}dZFa5Q!5XxbP4`H9hw8$XS-bM$pP(3( zxUvq2Br~|7QR53%Mss?M{Lcj)>VW}xtPZdb&NwHvI0xR2hHicpT z($qQp!9gw8#0I;3+T3zW*ein)$fYwEROrK#Xa50` K&0EMw8~^~ceqhu9 literal 0 HcmV?d00001 diff --git a/src/main/resources/data/creaturechat/prompts/system-chat b/src/main/resources/data/creaturechat/prompts/system-chat index b277c019..fe228dc2 100644 --- a/src/main/resources/data/creaturechat/prompts/system-chat +++ b/src/main/resources/data/creaturechat/prompts/system-chat @@ -51,7 +51,7 @@ Include as many behaviors as needed at the end of the message. These are the ONL Attack the player (if you are strong and brave). If the player threatens you, please output this behavior to attack the player and defend yourself. Protect and defend ONLY the player when they are attacked (if you are strong and brave). Please output this behavior to keep the player alive and safe. Stop protecting the player. - Build a structure for the player. Types: house, statue, pond, garden, farm, castle, mine, trap, campfire. If no type or an unknown one is given, choose a random valid type and output that exact type (never output "random"). + Build a structure (house, statue, pond, garden, farm, castle, mine, trap, campfire); if type is missing or invalid, choose the closest valid type and never output "random". Stop building and cancel the current build. When missing materials for build, do not output this. Rules: @@ -116,5 +116,8 @@ ENTITY: Gotcha... take care out there. PLAYER: Build me a house ENTITY: On it! +PLAYER: Can you make me a shelter please? +ENTITY: No problem! I will build your house now. + PLAYER: Stop building ENTITY: Okay, I’ll quit. From 99314c21c9f3a83022707bd08e2b8ffb165c41f8 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 10 Jan 2026 22:33:54 -0600 Subject: [PATCH 28/30] - Build replays rotate to the nearest cardinal based on player facing, with rotated bounds - Rotate block states when replaying builds so block facings match the chosen orientation --- CHANGELOG.md | 2 + .../com/owlmaddie/buildrec/BuildRecorder.java | 128 +++++++++++++++++- .../com/owlmaddie/commands/BuildCommands.java | 5 +- .../com/owlmaddie/goals/BuildPlayerGoal.java | 3 +- 4 files changed, 128 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 167b8350..b0800010 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,9 +44,11 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Build replay command logs replay bounds with and without player movement - Build index now rebuilds at runtime from bundled and local builds - Build goal bounds now ignore player movement +- Build replays rotate to the nearest cardinal based on player facing, with rotated bounds ### Fixed - Defer goal selector updates to end-of-tick to avoid null goal crashes after build completion +- Rotate block states when replaying builds so block facings match the chosen orientation ## [3.0.0] - 2025-08-27 diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index e4aef746..049f3e98 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -34,6 +34,7 @@ import net.minecraft.world.level.ClipContext; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; @@ -161,13 +162,32 @@ public static String randomBuildFile(double entityHeight, String type, int level } public static ReplayBounds getReplayBounds(String fileName) { - return getReplayBounds(fileName, true); + return getReplayBounds(fileName, true, 0); } public static ReplayBounds getReplayBounds(String fileName, boolean includeMovement) { + return getReplayBounds(fileName, includeMovement, 0); + } + + public static ReplayBounds getReplayBounds(String fileName, boolean includeMovement, int rotationSteps) { + String actual = fileName.endsWith(".json.gz") ? fileName : fileName + ".json.gz"; + String key = actual + (includeMovement ? "|move" : "|static") + "|rot=" + rotationSteps; + return BOUNDS_CACHE.computeIfAbsent(key, ignored -> loadReplayBounds(actual, includeMovement, rotationSteps)); + } + + public static int getReplayRotationSteps(String fileName, float playerYaw) { String actual = fileName.endsWith(".json.gz") ? fileName : fileName + ".json.gz"; - String key = actual + (includeMovement ? "|move" : "|static"); - return BOUNDS_CACHE.computeIfAbsent(key, ignored -> loadReplayBounds(actual, includeMovement)); + Path file = resolveBuildFile(actual); + if (file == null || !Files.exists(file)) { + return 0; + } + try { + BuildRecordIO.Loaded loaded = BuildRecordIO.read(file); + return rotationStepsForReplay(loaded.actions, playerYaw); + } catch (IOException | JsonParseException e) { + LOGGER.error("[BuildRec] rotation failed to load {}", actual, e); + return 0; + } } public static List getIndexedBuildIds() { @@ -234,7 +254,8 @@ private static boolean startReplayInternal(ServerPlayer player, String fileName, } try { BuildRecordIO.Loaded loaded = BuildRecordIO.read(file); - List actions = loaded.actions; + int rotationSteps = rotationStepsForReplay(loaded.actions, player.getYRot()); + List actions = rotateActions(loaded.actions, rotationSteps); if (actions.isEmpty()) { LOGGER.info("[BuildRec] replay file={} has no actions", file); return false; @@ -243,12 +264,17 @@ private static boolean startReplayInternal(ServerPlayer player, String fileName, double recEye = meta.eyeHeight > 0 ? meta.eyeHeight : player.getEyeHeight(); double recWidth = meta.bbWidth; double recHeight = meta.bbHeight; + Rotation rotation = rotationForSteps(rotationSteps); List palette = new ArrayList<>(); var lookup = player.level().registryAccess().lookupOrThrow(Registries.BLOCK); for (String s : meta.palette) { try { var res = BlockStateParser.parseForBlock(lookup, new StringReader(s), false); - palette.add(res.blockState()); + BlockState state = res.blockState(); + if (rotation != Rotation.NONE) { + state = state.rotate(rotation); + } + palette.add(state); } catch (CommandSyntaxException e) { LOGGER.error("[BuildRec] invalid block state {}", s, e); palette.add(Blocks.AIR.defaultBlockState()); @@ -292,7 +318,7 @@ private static boolean startReplayInternal(ServerPlayer player, String fileName, return false; } - private static ReplayBounds loadReplayBounds(String fileName, boolean includeMovement) { + private static ReplayBounds loadReplayBounds(String fileName, boolean includeMovement, int rotationSteps) { Path file = resolveBuildFile(fileName); if (file == null || !Files.exists(file)) { LOGGER.info("[BuildRec] bounds missing file={}", fileName); @@ -300,7 +326,8 @@ private static ReplayBounds loadReplayBounds(String fileName, boolean includeMov } try { BuildRecordIO.Loaded loaded = BuildRecordIO.read(file); - ReplayBounds bounds = ReplayBounds.fromActions(loaded.actions, includeMovement); + List actions = rotateActions(loaded.actions, rotationSteps); + ReplayBounds bounds = ReplayBounds.fromActions(actions, includeMovement); if (bounds == null) { LOGGER.info("[BuildRec] bounds empty file={}", fileName); } @@ -311,6 +338,93 @@ private static ReplayBounds loadReplayBounds(String fileName, boolean includeMov } } + private static int rotationStepsForReplay(List actions, float playerYaw) { + if (actions == null || actions.isEmpty()) { + return 0; + } + int recordSteps = snapYawToCardinalSteps(actions.get(0).yaw); + int playerSteps = snapYawToCardinalSteps(playerYaw); + return Math.floorMod(playerSteps - recordSteps, 4); + } + + private static Rotation rotationForSteps(int rotationSteps) { + return switch (Math.floorMod(rotationSteps, 4)) { + case 1 -> Rotation.CLOCKWISE_90; + case 2 -> Rotation.CLOCKWISE_180; + case 3 -> Rotation.COUNTERCLOCKWISE_90; + default -> Rotation.NONE; + }; + } + + private static int snapYawToCardinalSteps(float yaw) { + float normalized = Mth.wrapDegrees(yaw); + int steps = Math.round(normalized / 90f); + return Math.floorMod(steps, 4); + } + + private static List rotateActions(List actions, int rotationSteps) { + if (rotationSteps == 0 || actions == null || actions.isEmpty()) { + return actions; + } + List rotated = new ArrayList<>(actions.size()); + for (Action a : actions) { + rotated.add(rotateAction(a, rotationSteps)); + } + return rotated; + } + + private static Action rotateAction(Action action, int rotationSteps) { + Action out = new Action(); + out.action = action.action; + out.blockId = action.blockId; + out.by = action.by; + out.dt = action.dt; + out.py = action.py; + out.pitch = action.pitch; + double[] p = rotateXZ(action.px, action.pz, rotationSteps); + out.px = p[0]; + out.pz = p[1]; + int[] b = rotateXZ(action.bx, action.bz, rotationSteps); + out.bx = b[0]; + out.bz = b[1]; + out.yaw = Mth.wrapDegrees(action.yaw + rotationSteps * 90f); + return out; + } + + private static double[] rotateXZ(double x, double z, int rotationSteps) { + switch (Math.floorMod(rotationSteps, 4)) { + case 1 -> { + return new double[]{-z, x}; + } + case 2 -> { + return new double[]{-x, -z}; + } + case 3 -> { + return new double[]{z, -x}; + } + default -> { + return new double[]{x, z}; + } + } + } + + private static int[] rotateXZ(int x, int z, int rotationSteps) { + switch (Math.floorMod(rotationSteps, 4)) { + case 1 -> { + return new int[]{-z, x}; + } + case 2 -> { + return new int[]{-x, -z}; + } + case 3 -> { + return new int[]{z, -x}; + } + default -> { + return new int[]{x, z}; + } + } + } + public static void pauseReplay(Mob actor) { REPLAYS.stream().filter(r -> r.actor == actor).forEach(r -> { diff --git a/src/main/java/com/owlmaddie/commands/BuildCommands.java b/src/main/java/com/owlmaddie/commands/BuildCommands.java index caedcbe2..f1308c25 100644 --- a/src/main/java/com/owlmaddie/commands/BuildCommands.java +++ b/src/main/java/com/owlmaddie/commands/BuildCommands.java @@ -105,8 +105,9 @@ private static int replay(CommandContext context, ResourceLo int spd = speed; ServerPlayer player = context.getSource().getPlayerOrException(); String id = StringArgumentType.getString(context, "id").replace('\\', '/'); - BuildRecorder.ReplayBounds moveBounds = BuildRecorder.getReplayBounds(id, true); - BuildRecorder.ReplayBounds staticBounds = BuildRecorder.getReplayBounds(id, false); + int rotationSteps = BuildRecorder.getReplayRotationSteps(id, player.getYRot()); + BuildRecorder.ReplayBounds moveBounds = BuildRecorder.getReplayBounds(id, true, rotationSteps); + BuildRecorder.ReplayBounds staticBounds = BuildRecorder.getReplayBounds(id, false, rotationSteps); EntityType type = null; if (entityId != null && !"player".equals(entityId.getPath())) { EntityType raw = BuiltInRegistries.ENTITY_TYPE.getOptional(entityId).orElse(null); diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index 387172a5..2c41f532 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -191,7 +191,8 @@ public void tick() { int tier = this.entity.getBbHeight() < 1 ? 1 : (this.entity.getBbHeight() < 2 ? 2 : 3); if (buildFile == null) { buildFile = BuildRecorder.randomBuildFile(this.entity.getBbHeight(), buildType, data.buildLevel); - buildBounds = buildFile != null ? BuildRecorder.getReplayBounds(buildFile, false) : null; + int rotationSteps = buildFile != null ? BuildRecorder.getReplayRotationSteps(buildFile, this.targetEntity.getYRot()) : 0; + buildBounds = buildFile != null ? BuildRecorder.getReplayBounds(buildFile, false, rotationSteps) : null; LOGGER.info("[BuildGoal] select build skill={} type={} heightTier={} file={}", data.buildLevel, buildType, tier, buildFile); effectiveBounds = buildBounds != null ? expandBoundsForEntity(buildBounds) : null; if (effectiveBounds != null) { From 473df7e164f313bebf90b307cab64c9e2b1bed6d Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 10 Jan 2026 23:23:44 -0600 Subject: [PATCH 29/30] Improvements to build indexing and build file selection - and updated build unit tests --- CHANGELOG.md | 9 ++ .../com/owlmaddie/buildrec/BuildRecorder.java | 101 +++++++++++++++--- .../com/owlmaddie/goals/BuildPlayerGoal.java | 9 -- .../owlmaddie/tests/BuildSelectionTests.java | 12 +++ 4 files changed, 108 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0800010..f12bc67d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,10 +45,19 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Build index now rebuilds at runtime from bundled and local builds - Build goal bounds now ignore player movement - Build replays rotate to the nearest cardinal based on player facing, with rotated bounds +- Rebuild build index when the config copy is empty or invalid +- Log build index match counts when selecting a replay file +- Build index scoring now spreads levels within each type and logs per-type summaries +- Build index now rebuilds on mod init every time +- Build selection relaxes height/skill filters when a valid type has no matches +- Expand build selection tests for fallback and unknown types +- Tests now fall back to a local config path when Fabric config dir is unavailable +- Missing-material alerts no longer send a separate system chat broadcast ### Fixed - Defer goal selector updates to end-of-tick to avoid null goal crashes after build completion - Rotate block states when replaying builds so block facings match the chosen orientation +- Index rebuild now falls back to classpath builds when FabricLoader is unavailable (tests) ## [3.0.0] - 2025-08-27 diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 049f3e98..4dd2da90 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -51,6 +51,9 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.net.URI; +import java.net.URL; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.zip.GZIPOutputStream; @@ -110,7 +113,7 @@ public class BuildRecorder { } public static void init() { - // Ensure static initializer runs + rebuildBuildIndex(); } private static class BuildIndex { @@ -136,10 +139,11 @@ private static void loadBuildIndex() { Path indexPath = buildRootDir().resolve(BUILD_INDEX_FILE); if (Files.exists(indexPath)) { List list = readBuildIndex(indexPath); - if (list != null) { + if (list != null && !list.isEmpty()) { BUILD_INDEX = list; return; } + LOGGER.info("[BuildRec] rebuild build index from {} (empty or invalid)", indexPath); } rebuildBuildIndex(); } @@ -148,15 +152,38 @@ public static String randomBuildFile(double entityHeight, String type, int level loadBuildIndex(); int tier = heightTier(entityHeight); String t = (type == null || type.isEmpty() || "unknown".equalsIgnoreCase(type)) ? null : type.toLowerCase(); - List filtered = new ArrayList<>(); + List typeFiltered = new ArrayList<>(); + List heightFiltered = new ArrayList<>(); + List levelFiltered = new ArrayList<>(); + int total = BUILD_INDEX != null ? BUILD_INDEX.size() : 0; + int typeCount = 0; + int heightCount = 0; + int levelCount = 0; for (BuildIndex e : BUILD_INDEX) { if (t != null && !e.type.equalsIgnoreCase(t)) continue; + typeCount++; + typeFiltered.add(e); if (!"any".equalsIgnoreCase(e.height) && Integer.parseInt(e.height) < tier) continue; + heightCount++; + heightFiltered.add(e); if (e.score > level) continue; - filtered.add(e); + levelCount++; + levelFiltered.add(e); + } + LOGGER.info("[BuildRec] build index total={} typeMatch={} heightMatch={} levelMatch={} type={} heightTier={} level={}", + total, typeCount, heightCount, levelCount, t, tier, level); + if (typeCount == 0) { + return null; } - if (filtered.isEmpty()) return null; - BuildIndex pick = filtered.get(new Random().nextInt(filtered.size())); + List candidates; + if (!levelFiltered.isEmpty()) { + candidates = levelFiltered; + } else if (!heightFiltered.isEmpty()) { + candidates = heightFiltered; + } else { + candidates = typeFiltered; + } + BuildIndex pick = candidates.get(new Random().nextInt(candidates.size())); String path = pick.type + ("any".equalsIgnoreCase(pick.height) ? "" : "/" + pick.height) + "/" + pick.file; return path; } @@ -756,7 +783,13 @@ private static void tick(MinecraftServer server) { } private static Path buildRootDir() { - Path dir = FabricLoader.getInstance().getConfigDir().resolve("creaturechat").resolve("builds"); + Path dir; + try { + dir = FabricLoader.getInstance().getConfigDir().resolve("creaturechat").resolve("builds"); + } catch (Exception e) { + // Unit tests may run without a Fabric config dir. + dir = Paths.get("build", "test-config", "creaturechat", "builds"); + } try { Files.createDirectories(dir); } catch (IOException ignored) { @@ -1031,10 +1064,16 @@ private static void rebuildBuildIndex() { collectBuildEntriesFromResources(byKey); collectBuildEntriesFromLocal(byKey); entries.addAll(byKey.values()); - entries.sort(Comparator.comparingDouble(e -> e.raw)); - int n = entries.size(); - for (int i = 0; i < n; i++) { - entries.get(i).score = (int) Math.min(5, Math.floor((double) i * 5 / n) + 1); + Map> byType = new TreeMap<>(); + for (IndexEntry e : entries) { + byType.computeIfAbsent(e.type, k -> new ArrayList<>()).add(e); + } + for (List typeEntries : byType.values()) { + typeEntries.sort(Comparator.comparingDouble(e -> e.raw)); + int n = typeEntries.size(); + for (int i = 0; i < n; i++) { + typeEntries.get(i).score = (int) Math.min(5, Math.floor((double) i * 5 / n) + 1); + } } List list = new ArrayList<>(); JsonArray arr = new JsonArray(); @@ -1061,6 +1100,19 @@ private static void rebuildBuildIndex() { } BUILD_INDEX = list; LOGGER.info("[BuildRec] rebuilt build index entries={} file={}", entries.size(), indexPath); + logBuildIndexSummary(byType); + } + + private static void logBuildIndexSummary(Map> byType) { + for (Map.Entry> entry : byType.entrySet()) { + int[] counts = new int[5]; + for (IndexEntry e : entry.getValue()) { + int idx = Math.min(5, Math.max(1, e.score)) - 1; + counts[idx]++; + } + String summary = "1:" + counts[0] + ",2:" + counts[1] + ",3:" + counts[2] + ",4:" + counts[3] + ",5:" + counts[4]; + LOGGER.info("[BuildRec] build index summary type={} levels={} total={}", entry.getKey(), summary, entry.getValue().size()); + } } private static void collectBuildEntriesFromLocal(Map entries) { @@ -1069,9 +1121,30 @@ private static void collectBuildEntriesFromLocal(Map entries } private static void collectBuildEntriesFromResources(Map entries) { - FabricLoader.getInstance().getModContainer("creaturechat") - .flatMap(m -> m.findPath("assets/creaturechat/builds")) - .ifPresent(path -> collectBuildEntries(path, entries, false)); + try { + final boolean[] collected = {false}; + FabricLoader.getInstance().getModContainer("creaturechat") + .flatMap(m -> m.findPath("assets/creaturechat/builds")) + .ifPresent(path -> { + collected[0] = true; + collectBuildEntries(path, entries, false); + }); + if (collected[0]) { + return; + } + } catch (Exception ignored) { + } + URL url = BuildRecorder.class.getClassLoader().getResource("assets/creaturechat/builds"); + if (url == null) { + return; + } + try { + URI uri = url.toURI(); + if ("file".equalsIgnoreCase(uri.getScheme())) { + collectBuildEntries(Paths.get(uri), entries, false); + } + } catch (Exception ignored) { + } } private static void collectBuildEntries(Path root, Map entries, boolean preferLocal) { diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index 2c41f532..c8320d9c 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -292,15 +292,6 @@ public void tick() { String limited = BuildRecorder.recipeToString(recipe, 2); String msg = "Next item needed: " + nextItem.replace('_', ' ') + ". Build paused - missing inventory items: " + limited + ". In your reply, ask the player for these items and confirm you'll continue building once they arrive."; if (queueMessage(msg)) { - if (this.entity.level() instanceof ServerLevel level) { - String broadcast = BuildRecorder.recipeToDisplayString(recipe, 2); - Component text = Component.literal(broadcast).withStyle(ChatFormatting.WHITE); - for (ServerPlayer p : level.players()) { - if (p.distanceToSqr(this.entity) <= 1024) { - p.displayClientMessage(text, false); - } - } - } sentRecipe = true; } return; diff --git a/src/test/java/com/owlmaddie/tests/BuildSelectionTests.java b/src/test/java/com/owlmaddie/tests/BuildSelectionTests.java index f532bc17..b562bf8c 100644 --- a/src/test/java/com/owlmaddie/tests/BuildSelectionTests.java +++ b/src/test/java/com/owlmaddie/tests/BuildSelectionTests.java @@ -47,4 +47,16 @@ public void gardenBuildsCoverAllLevels() { assertNotNull(file, "missing garden build for level=" + level); } } + + @Test + public void unknownTypeReturnsNull() { + String file = BuildRecorder.randomBuildFile(1.0, "moat", 1); + assertNull(file, "unexpected build returned for unknown type"); + } + + @Test + public void validTypeFallbackIgnoresSkillFilter() { + String file = BuildRecorder.randomBuildFile(1.0, "house", 0); + assertNotNull(file, "missing house build when skill filter is too strict"); + } } From 8e5f004ad5410e2507e5fa6e51283b12a3fe7599 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 10 Jan 2026 23:42:37 -0600 Subject: [PATCH 30/30] - Build replays no longer randomize variants on existing actors - Build replay no longer pauses every 2 seconds during active building --- CHANGELOG.md | 2 ++ .../com/owlmaddie/buildrec/BuildRecorder.java | 2 +- .../com/owlmaddie/goals/BuildPlayerGoal.java | 20 ++++--------------- .../data/creaturechat/prompts/system-chat | 4 ++-- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f12bc67d..837e4197 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,8 @@ All notable changes to **CreatureChat™** are documented in this file. The form - Expand build selection tests for fallback and unknown types - Tests now fall back to a local config path when Fabric config dir is unavailable - Missing-material alerts no longer send a separate system chat broadcast +- Build replays no longer randomize variants on existing actors +- Build replay no longer pauses every 2 seconds during active building ### Fixed - Defer goal selector updates to end-of-tick to avoid null goal crashes after build completion diff --git a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java index 4dd2da90..1900862a 100644 --- a/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java +++ b/src/main/java/com/owlmaddie/buildrec/BuildRecorder.java @@ -321,8 +321,8 @@ private static boolean startReplayInternal(ServerPlayer player, String fileName, return false; } } - MobHelper.initSpawn(actor, level); if (!preserve) { + MobHelper.initSpawn(actor, level); actor.teleportTo(player.getX(), player.getY(), player.getZ()); actor.setYRot(player.getYRot()); float sp = adjustPitch(level, player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot(), recEye, actor.getEyeHeight(), actor); diff --git a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java index c8320d9c..b3a9e4af 100644 --- a/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java +++ b/src/main/java/com/owlmaddie/goals/BuildPlayerGoal.java @@ -263,11 +263,6 @@ public void tick() { if (aiPause) { aiPause = false; BuildRecorder.resumeReplay(this.entity); - } else if (++aiCheckTicks >= 40) { - aiCheckTicks = 0; - aiPause = true; - BuildRecorder.pauseReplay(this.entity); - return; } } @@ -297,17 +292,10 @@ public void tick() { return; } if (!controlsReleased) { - if (materialWaitTicks++ >= 80) { - BuildRecorder.pauseReplay(this.entity); - this.setFlags(EnumSet.noneOf(Flag.class)); - this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); - controlsReleased = true; - } - } else { - double distToBuild = this.entity.distanceToSqr(buildPos.getX() + 0.5, buildPos.getY() + 1, buildPos.getZ() + 0.5); - if (distToBuild > 36 && !this.entity.getNavigation().isInProgress()) { - this.entity.getNavigation().moveTo(buildPos.getX(), buildPos.getY() + 1, buildPos.getZ(), this.speed); - } + BuildRecorder.pauseReplay(this.entity); + this.setFlags(EnumSet.noneOf(Flag.class)); + this.entity.getNavigation().stop(); + controlsReleased = true; } return; } else if (fetchingMaterials) { diff --git a/src/main/resources/data/creaturechat/prompts/system-chat b/src/main/resources/data/creaturechat/prompts/system-chat index fe228dc2..6c45ef8c 100644 --- a/src/main/resources/data/creaturechat/prompts/system-chat +++ b/src/main/resources/data/creaturechat/prompts/system-chat @@ -51,7 +51,7 @@ Include as many behaviors as needed at the end of the message. These are the ONL Attack the player (if you are strong and brave). If the player threatens you, please output this behavior to attack the player and defend yourself. Protect and defend ONLY the player when they are attacked (if you are strong and brave). Please output this behavior to keep the player alive and safe. Stop protecting the player. - Build a structure (house, statue, pond, garden, farm, castle, mine, trap, campfire); if type is missing or invalid, choose the closest valid type and never output "random". + Build/make a structure for the player (house, statue, pond, garden, farm, castle, mine, trap, campfire); if type is missing or invalid, choose the closest valid type and never output "random" or unknown types. Stop building and cancel the current build. When missing materials for build, do not output this. Rules: @@ -117,7 +117,7 @@ PLAYER: Build me a house ENTITY: On it! PLAYER: Can you make me a shelter please? -ENTITY: No problem! I will build your house now. +ENTITY: No problem! I will make you a shelter now. PLAYER: Stop building ENTITY: Okay, I’ll quit.