package com.tiedup.remake.items; import com.tiedup.remake.state.IRestrainable; import com.tiedup.remake.util.KidnappedHelper; import java.util.ArrayList; import java.util.List; import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.level.Level; import org.jetbrains.annotations.Nullable; /** * GPS Collar - Advanced shock collar with tracking and safe zone features. * *

Mechanics:

* */ public class ItemGpsCollar extends ItemShockCollar { private static final String NBT_PUBLIC_TRACKING = "publicTracking"; private static final String NBT_GPS_ACTIVE = "gpsActive"; private static final String NBT_SAFE_SPOTS = "gpsSafeSpots"; private static final String NBT_SHOCK_INTERVAL = "gpsShockInterval"; private static final String NBT_WARN_MASTERS = "warn_masters"; private final int defaultInterval; public ItemGpsCollar() { this(200); // 10 seconds default } public ItemGpsCollar(int defaultInterval) { super(); this.defaultInterval = defaultInterval; } @Override public boolean hasGPS() { return true; } /** * Renders detailed GPS status, safe zone list, and alert settings in the item tooltip. */ @Override public void appendHoverText( ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag ) { super.appendHoverText(stack, level, tooltip, flag); tooltip.add( Component.literal("GPS Enabled").withStyle( ChatFormatting.DARK_GREEN ) ); if (hasPublicTracking(stack)) { tooltip.add( Component.literal("Public Tracking Enabled").withStyle( ChatFormatting.GREEN ) ); } if (shouldWarnMasters(stack)) { tooltip.add( Component.literal("Alert Masters on Violation").withStyle( ChatFormatting.GOLD ) ); } List safeSpots = getSafeSpots(stack); if (!safeSpots.isEmpty()) { tooltip.add( Component.literal("GPS Shocks: ") .withStyle(ChatFormatting.GREEN) .append( Component.literal( isActive(stack) ? "ENABLED" : "DISABLED" ).withStyle( isActive(stack) ? ChatFormatting.RED : ChatFormatting.GRAY ) ) ); tooltip.add( Component.literal( "Safe Spots (" + safeSpots.size() + "):" ).withStyle(ChatFormatting.GREEN) ); for (int i = 0; i < safeSpots.size(); i++) { SafeSpot spot = safeSpots.get(i); tooltip.add( Component.literal( (spot.active ? "[+] " : "[-] ") + (i + 1) + ": " + spot.x + "," + spot.y + "," + spot.z + " (Range: " + spot.distance + "m)" ).withStyle(ChatFormatting.GRAY) ); } } } public boolean shouldWarnMasters(ItemStack stack) { CompoundTag tag = stack.getTag(); // Default to true if tag doesn't exist return ( tag == null || !tag.contains(NBT_WARN_MASTERS) || tag.getBoolean(NBT_WARN_MASTERS) ); } public void setWarnMasters(ItemStack stack, boolean warn) { stack.getOrCreateTag().putBoolean(NBT_WARN_MASTERS, warn); } public boolean hasPublicTracking(ItemStack stack) { CompoundTag tag = stack.getTag(); return tag != null && tag.getBoolean(NBT_PUBLIC_TRACKING); } public void setPublicTracking(ItemStack stack, boolean publicTracking) { stack.getOrCreateTag().putBoolean(NBT_PUBLIC_TRACKING, publicTracking); } public boolean isActive(ItemStack stack) { CompoundTag tag = stack.getTag(); // Default to active if tag doesn't exist return ( tag == null || !tag.contains(NBT_GPS_ACTIVE) || tag.getBoolean(NBT_GPS_ACTIVE) ); } public void setActive(ItemStack stack, boolean active) { stack.getOrCreateTag().putBoolean(NBT_GPS_ACTIVE, active); } /** * Parses the NBT List into a Java List of SafeSpot objects. */ public List getSafeSpots(ItemStack stack) { List list = new ArrayList<>(); CompoundTag tag = stack.getTag(); if (tag != null && tag.contains(NBT_SAFE_SPOTS)) { ListTag spotList = tag.getList(NBT_SAFE_SPOTS, 10); for (int i = 0; i < spotList.size(); i++) { list.add(new SafeSpot(spotList.getCompound(i))); } } return list; } /** * Adds a new safe zone to the collar's NBT data. */ public void addSafeSpot( ItemStack stack, int x, int y, int z, String dimension, int distance ) { CompoundTag tag = stack.getOrCreateTag(); ListTag spotList = tag.getList(NBT_SAFE_SPOTS, 10); SafeSpot spot = new SafeSpot(x, y, z, dimension, distance, true); spotList.add(spot.toNBT()); tag.put(NBT_SAFE_SPOTS, spotList); } /** * Gets frequency of GPS violation shocks. */ public int getShockInterval(ItemStack stack) { CompoundTag tag = stack.getTag(); if (tag != null && tag.contains(NBT_SHOCK_INTERVAL)) { return tag.getInt(NBT_SHOCK_INTERVAL); } return defaultInterval; } /** */ @Override public void onUnequipped(ItemStack stack, LivingEntity entity) { // Use IRestrainable interface instead of Player-only IRestrainable state = KidnappedHelper.getKidnappedState(entity); if (state != null) { state.resetAutoShockTimer(); } super.onUnequipped(stack, entity); } /** * Represents a defined safe zone in the 3D world. */ public static class SafeSpot { public int x, y, z; public String dimension; public int distance; public boolean active; public SafeSpot( int x, int y, int z, String dimension, int distance, boolean active ) { this.x = x; this.y = y; this.z = z; this.dimension = dimension; this.distance = distance; this.active = active; } public SafeSpot(CompoundTag nbt) { this.x = nbt.getInt("x"); this.y = nbt.getInt("y"); this.z = nbt.getInt("z"); this.dimension = nbt.getString("dim"); this.distance = nbt.getInt("dist"); this.active = !nbt.contains("active") || nbt.getBoolean("active"); } public CompoundTag toNBT() { CompoundTag nbt = new CompoundTag(); nbt.putInt("x", x); nbt.putInt("y", y); nbt.putInt("z", z); nbt.putString("dim", dimension); nbt.putInt("dist", distance); nbt.putBoolean("active", active); return nbt; } /** * Checks if an entity is within the cuboid boundaries of this safe zone. * Faithful to original 1.12.2 distance logic. */ public boolean isInside(Entity entity) { if (!active) return true; // LOW FIX: Cross-dimension GPS fix // If entity is in a different dimension, consider them as "inside" the zone // to prevent false positive shocks when traveling between dimensions if ( !entity .level() .dimension() .location() .toString() .equals(dimension) ) return true; // Changed from false to true // Cuboid distance check return ( Math.abs(entity.getX() - x) < distance && Math.abs(entity.getY() - y) < distance && Math.abs(entity.getZ() - z) < distance ); } } }