aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java21
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java42
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java52
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java46
4 files changed, 143 insertions, 18 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
index 2d109524..51b88d30 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
@@ -1,23 +1,42 @@
package me.xmrvizzy.skyblocker.mixin;
import com.llamalad7.mixinextras.injector.WrapWithCondition;
-
+import com.llamalad7.mixinextras.sugar.Local;
import dev.cbyrne.betterinject.annotations.Inject;
import me.xmrvizzy.skyblocker.skyblock.FishingHelper;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets;
import me.xmrvizzy.skyblocker.utils.Utils;
+import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler;
+import net.minecraft.entity.ItemEntity;
+import net.minecraft.entity.LivingEntity;
import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket;
import org.slf4j.Logger;
+import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerMixin {
+ @Shadow
+ @Final
+ private MinecraftClient client;
+
@Inject(method = "onPlaySound", at = @At("RETURN"))
private void skyblocker$onPlaySound(PlaySoundS2CPacket packet) {
FishingHelper.onSound(packet);
}
+ @ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "STORE", ordinal = 0))
+ private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity, @Local LivingEntity collector) {
+ if (collector == client.player) {
+ DungeonSecrets.onItemPickup(itemEntity, collector);
+ }
+ return itemEntity;
+ }
+
@WrapWithCondition(method = "onEntityPassengersSet", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", remap = false))
private boolean skyblocker$cancelEntityPassengersWarning(Logger instance, String msg) {
return !Utils.isOnHypixel();
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
index 9e31fe5f..3eb4e036 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
@@ -6,16 +6,23 @@ import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
+import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.entity.ItemEntity;
+import net.minecraft.entity.LivingEntity;
import net.minecraft.item.FilledMapItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.map.MapState;
import net.minecraft.text.Text;
+import net.minecraft.util.ActionResult;
import net.minecraft.util.Identifier;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.world.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector2ic;
@@ -37,7 +44,6 @@ import java.util.zip.InflaterInputStream;
public class DungeonSecrets {
protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class);
- private static final String DUNGEONS_DATA_DIR = "/assets/skyblocker/dungeons";
/**
* Block data for dungeon rooms. See {@link me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU} for format details and how it's generated.
* All access to this map must check {@link #isRoomsLoaded()} to prevent concurrent modification.
@@ -73,6 +79,9 @@ public class DungeonSecrets {
Map.entry("minecraft:cyan_terracotta", (byte) 20),
Map.entry("minecraft:black_terracotta", (byte) 21)
));
+ private static final String DUNGEONS_DATA_DIR = "/assets/skyblocker/dungeons";
+ @NotNull
+ private static final Map<Vector2ic, Room> rooms = new HashMap<>();
private static JsonObject roomsJson;
private static JsonObject waypointsJson;
@Nullable
@@ -91,14 +100,13 @@ public class DungeonSecrets {
*/
@Nullable
private static Vector2ic physicalEntrancePos;
- @NotNull
- private static final Map<Vector2ic, Room> rooms = new HashMap<>();
private static Room currentRoom;
public static boolean isRoomsLoaded() {
return roomsLoaded != null && roomsLoaded.isDone();
}
+ @SuppressWarnings("unused")
public static JsonObject getRoomsJson() {
return roomsJson;
}
@@ -118,6 +126,9 @@ public class DungeonSecrets {
CompletableFuture.runAsync(DungeonSecrets::load);
SkyblockerMod.getInstance().scheduler.scheduleCyclic(DungeonSecrets::update, 10);
WorldRenderEvents.AFTER_TRANSLUCENT.register(DungeonSecrets::render);
+ ClientReceiveMessageEvents.GAME.register(DungeonSecrets::onChatMessage);
+ ClientReceiveMessageEvents.GAME_CANCELED.register(DungeonSecrets::onChatMessage);
+ UseBlockCallback.EVENT.register((world, hand, hitResult, hitResult2) -> onUseBlock(hand, hitResult2));
}
private static void load() {
@@ -272,11 +283,34 @@ public class DungeonSecrets {
}
private static void render(WorldRenderContext context) {
- if (SkyblockerConfig.get().locations.dungeons.secretWaypoints && Utils.isInDungeons() && currentRoom != null && currentRoom.isMatched()) {
+ if (isCurrentRoomMatched()) {
currentRoom.render(context);
}
}
+ private static void onChatMessage(Text text, boolean overlay) {
+ if (overlay && isCurrentRoomMatched()) {
+ currentRoom.onChatMessage(text.getString());
+ }
+ }
+
+ private static ActionResult onUseBlock(World world, BlockHitResult hitResult) {
+ if (isCurrentRoomMatched()) {
+ currentRoom.onUseBlock(world, hitResult);
+ }
+ return ActionResult.PASS;
+ }
+
+ public static void onItemPickup(ItemEntity itemEntity, LivingEntity collector) {
+ if (isCurrentRoomMatched()) {
+ currentRoom.onItemPickup(itemEntity, collector);
+ }
+ }
+
+ private static boolean isCurrentRoomMatched() {
+ return SkyblockerConfig.get().locations.dungeons.secretWaypoints && Utils.isInDungeons() && currentRoom != null && currentRoom.isMatched();
+ }
+
private static void reset() {
mapEntrancePos = null;
mapRoomSize = 0;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java
index e7ed7014..7833233f 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java
@@ -1,5 +1,8 @@
package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Table;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
@@ -7,13 +10,19 @@ import it.unimi.dsi.fastutil.ints.IntSortedSet;
import it.unimi.dsi.fastutil.ints.IntSortedSets;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.util.TriState;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.Blocks;
import net.minecraft.block.MapColor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
+import net.minecraft.entity.ItemEntity;
+import net.minecraft.entity.LivingEntity;
import net.minecraft.registry.Registries;
+import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
+import net.minecraft.world.World;
import org.apache.commons.lang3.tuple.MutableTriple;
import org.apache.commons.lang3.tuple.Triple;
import org.joml.Vector2i;
@@ -37,7 +46,7 @@ public class Room {
* <li>{@link TriState#TRUE} means that the room has been checked and there is a match.
*/
private TriState matched = TriState.DEFAULT;
- private List<SecretWaypoint> secretWaypoints;
+ private Table<Integer, BlockPos, SecretWaypoint> secretWaypoints;
public Room(Type type, Vector2ic... physicalPositions) {
long startTime = System.currentTimeMillis();
@@ -178,25 +187,58 @@ public class Room {
private void roomMatched(Triple<Direction, Vector2ic, List<String>> directionRooms) {
matched = TriState.TRUE;
- secretWaypoints = new ArrayList<>();
+ Table<Integer, BlockPos, SecretWaypoint> secretWaypointsMutable = HashBasedTable.create();
String name = directionRooms.getRight().get(0);
for (JsonElement waypointElement : DungeonSecrets.getWaypointsJson().get(name).getAsJsonArray()) {
JsonObject waypoint = waypointElement.getAsJsonObject();
String secretName = waypoint.get("secretName").getAsString();
int secretIndex = Integer.parseInt(secretName.substring(0, Character.isDigit(secretName.charAt(1)) ? 2 : 1));
- secretWaypoints.add(new SecretWaypoint(secretIndex, waypoint, secretName, DungeonMapUtils.relativeToActual(directionRooms.getMiddle(), directionRooms.getLeft(), waypoint)));
+ BlockPos pos = DungeonMapUtils.relativeToActual(directionRooms.getMiddle(), directionRooms.getLeft(), waypoint);
+ secretWaypointsMutable.put(secretIndex, pos, new SecretWaypoint(secretIndex, waypoint, secretName, pos));
}
+ secretWaypoints = ImmutableTable.copyOf(secretWaypointsMutable);
DungeonSecrets.LOGGER.info("[Skyblocker] Room {} matched after checking {} block(s)", name, checkedBlocks.size()); // TODO change to debug
}
protected void render(WorldRenderContext context) {
- for (SecretWaypoint secretWaypoint : secretWaypoints) {
- if (secretWaypoint.missing()) {
+ for (SecretWaypoint secretWaypoint : secretWaypoints.values()) {
+ if (secretWaypoint.isMissing()) {
secretWaypoint.render(context);
}
}
}
+ protected void onChatMessage(String message) {
+ if (message.toLowerCase().contains("secret")) { // TODO for dev purposes only
+ DungeonSecrets.LOGGER.info(message);
+ }
+ }
+
+ protected void onUseBlock(World world, BlockHitResult hitResult) {
+ BlockState state = world.getBlockState(hitResult.getBlockPos());
+ DungeonSecrets.LOGGER.info(state.getBlock().toString()); // TODO for dev purposes only
+ if (!state.isOf(Blocks.CHEST) && !state.isOf(Blocks.PLAYER_HEAD) && !state.isOf(Blocks.PLAYER_WALL_HEAD)) {
+ return;
+ }
+ secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(secretWaypoint -> secretWaypoint.category.needsInteraction()).findAny()
+ .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} interaction, setting secret #{} as found", secretWaypoint.category, secretWaypoint.secretIndex));
+ }
+
+ protected void onItemPickup(ItemEntity itemEntity, LivingEntity collector) {
+ String name = itemEntity.getName().getString();
+ DungeonSecrets.LOGGER.info(name); // TODO for dev purposes only
+ if (Arrays.stream(SecretWaypoint.SECRET_ITEMS).noneMatch(name::contains)) {
+ return;
+ }
+ secretWaypoints.values().stream().filter(secretWaypoint -> secretWaypoint.category.needsItemPickup()).min(Comparator.comparingDouble(secretWaypoint -> collector.squaredDistanceTo(secretWaypoint.centerPos))).filter(secretWaypoint -> collector.squaredDistanceTo(secretWaypoint.centerPos) <= 36D)
+ .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} picked up a {} from a {} secret, setting secret #{} as found", collector.getName().getString(), itemEntity.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex));
+ }
+
+ private void onSecretFound(SecretWaypoint secretWaypoint, String msg, Object... args) {
+ secretWaypoints.row(secretWaypoint.secretIndex).values().forEach(SecretWaypoint::setFound);
+ DungeonSecrets.LOGGER.info(msg, args);
+ }
+
/**
* Resets fields after room matching completes, where either a room is found or none matched.
* These fields are no longer needed and are discarded to save memory.
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java
index 08cba782..71459e50 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java
@@ -4,24 +4,46 @@ import com.google.gson.JsonObject;
import me.xmrvizzy.skyblocker.utils.RenderHelper;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.client.MinecraftClient;
-import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+
+public class SecretWaypoint {
+ static final String[] SECRET_ITEMS = {"Decoy", "Defuse Kit", "Dungeon Chest Key", "Healing VIII", "Inflatable Jerry", "Spirit Leap", "Training Weights", "Trap", "treasure Talisman"};
+ final int secretIndex;
+ final Category category;
+ private final Text name;
+ final BlockPos pos;
+ final Vec3d centerPos;
+ private boolean missing;
-public record SecretWaypoint(int secretIndex, Category category, Text name, BlockPos pos, PlayerEntity player, boolean missing) {
SecretWaypoint(int secretIndex, JsonObject waypoint, String name, BlockPos pos) {
- this(secretIndex, Category.get(waypoint), Text.of(name), pos, MinecraftClient.getInstance().player, true);
+ this.secretIndex = secretIndex;
+ this.category = Category.get(waypoint);
+ this.name = Text.of(name);
+ this.pos = pos;
+ this.centerPos = pos.toCenterPos();
+ this.missing = true;
+ }
+
+ public boolean isMissing() {
+ return missing;
+ }
+
+ public void setFound() {
+ this.missing = false;
}
void render(WorldRenderContext context) {
- RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, pos(), category().colorComponents, 0.5F);
- RenderHelper.renderText(context, name(), pos().up().toCenterPos(), true);
- double distance = player().getPos().distanceTo(pos().toCenterPos());
- RenderHelper.renderText(context, Text.literal(Math.round(distance) + "m").formatted(Formatting.YELLOW), pos().up().toCenterPos(), 1, MinecraftClient.getInstance().textRenderer.fontHeight + 1, true);
+ RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, pos, category.colorComponents, 0.5F);
+ Vec3d posUp = centerPos.add(0, 1, 0);
+ RenderHelper.renderText(context, name, posUp, true);
+ double distance = context.camera().getPos().distanceTo(centerPos);
+ RenderHelper.renderText(context, Text.literal(Math.round(distance) + "m").formatted(Formatting.YELLOW), posUp, 1, MinecraftClient.getInstance().textRenderer.fontHeight + 1, true);
}
- private enum Category {
+ enum Category {
ENTRANCE(0, 255, 0),
SUPERBOOM(255, 0, 0),
CHEST(2, 213, 250),
@@ -55,5 +77,13 @@ public record SecretWaypoint(int secretIndex, Category category, Text name, Bloc
default -> Category.DEFAULT;
};
}
+
+ boolean needsInteraction() {
+ return this == CHEST || this == WITHER;
+ }
+
+ boolean needsItemPickup() {
+ return this == ITEM || this == BAT;
+ }
}
}