Files
TiedUp-/src/main/java/com/tiedup/remake/items/ItemGpsCollar.java
NotEvil a71093ba9c Remove internal phase comments and format code
Strip all Phase references, TODO/FUTURE roadmap notes, and internal
planning comments from the codebase. Run Prettier for consistent
formatting across all Java files.
2026-04-12 01:25:55 +02:00

370 lines
9.3 KiB
Java

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.
*
* <p>Mechanics:</p>
* <ul>
* <li><b>Safe Zones:</b> Can store multiple coordinates (SafeSpots) where the wearer is allowed to be.</li>
* <li><b>Auto-Shock:</b> If the wearer is outside ALL active safe zones, they are shocked at intervals.</li>
* <li><b>Master Warning:</b> Masters receive an alert message when a safe zone violation is detected.</li>
* <li><b>Public Tracking:</b> If enabled, allows anyone with a Locator to see distance and direction.</li>
* </ul>
*/
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<Component> 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<SafeSpot> 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<SafeSpot> getSafeSpots(ItemStack stack) {
List<SafeSpot> 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
);
}
}
}