Strip all Phase references, TODO/FUTURE roadmap notes, and internal planning comments from the codebase. Run Prettier for consistent formatting across all Java files.
370 lines
9.3 KiB
Java
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
|
|
);
|
|
}
|
|
}
|
|
}
|