119 new translation keys across 3 domains: - Items/Blocks/Misc (46 keys): tooltips, action messages, trap states - Entities/AI Goals (55 keys): NPC speech, maid/master/guard messages - Client GUI (18 keys): widget labels, screen buttons, merchant display Remaining 119 Component.literal() are all intentional: - Debug/Admin/Command wands (47) — dev tools, not player-facing - Entity display names (~25) — dynamic getNpcName() calls - Empty string roots (~15) — .append() chain bases - User-typed text (~10) — /me, /pm, /norp chat content - Runtime data (~12) — StringBuilder, gag muffling, MCA compat
239 lines
8.5 KiB
Java
239 lines
8.5 KiB
Java
package com.tiedup.remake.items;
|
|
|
|
import com.tiedup.remake.core.SystemMessageManager;
|
|
import com.tiedup.remake.items.base.ItemOwnerTarget;
|
|
import com.tiedup.remake.v2.bondage.CollarHelper;
|
|
import com.tiedup.remake.state.IBondageState;
|
|
import com.tiedup.remake.util.KidnappedHelper;
|
|
import com.tiedup.remake.v2.BodyRegionV2;
|
|
import java.util.List;
|
|
import net.minecraft.ChatFormatting;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.InteractionResult;
|
|
import net.minecraft.world.InteractionResultHolder;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.TooltipFlag;
|
|
import net.minecraft.world.level.Level;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class ItemGpsLocator extends ItemOwnerTarget {
|
|
|
|
public ItemGpsLocator() {
|
|
super(new net.minecraft.world.item.Item.Properties().stacksTo(1));
|
|
}
|
|
|
|
@Override
|
|
public void appendHoverText(
|
|
ItemStack stack,
|
|
@Nullable Level level,
|
|
List<Component> tooltip,
|
|
TooltipFlag flag
|
|
) {
|
|
super.appendHoverText(stack, level, tooltip, flag);
|
|
|
|
appendOwnerTooltip(stack, tooltip, "Right-click a player");
|
|
|
|
if (hasTarget(stack)) {
|
|
String displayName = resolveTargetDisplayName(stack, level);
|
|
tooltip.add(
|
|
Component.translatable("item.tiedup.gps_locator.target", displayName)
|
|
.withStyle(ChatFormatting.BLUE)
|
|
);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public InteractionResultHolder<ItemStack> use(
|
|
Level level,
|
|
Player player,
|
|
InteractionHand hand
|
|
) {
|
|
ItemStack stack = player.getItemInHand(hand);
|
|
|
|
if (level.isClientSide) return InteractionResultHolder.success(stack);
|
|
|
|
if (!hasOwner(stack)) {
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.ERROR,
|
|
"You must claim this locator first! (Right-click a player)"
|
|
);
|
|
return InteractionResultHolder.fail(stack);
|
|
}
|
|
|
|
if (!isOwner(stack, player)) {
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.LOCATOR_NOT_OWNER
|
|
);
|
|
return InteractionResultHolder.fail(stack);
|
|
}
|
|
|
|
if (hasTarget(stack)) {
|
|
// Use server player list for cross-dimension tracking
|
|
Player target = level
|
|
.getServer()
|
|
.getPlayerList()
|
|
.getPlayer(getTargetId(stack));
|
|
if (target != null) {
|
|
IBondageState targetState = KidnappedHelper.getKidnappedState(
|
|
target
|
|
);
|
|
if (targetState != null && targetState.hasCollar()) {
|
|
ItemStack collarStack = targetState.getEquipment(
|
|
BodyRegionV2.NECK
|
|
);
|
|
if (CollarHelper.hasGPS(collarStack)) {
|
|
if (
|
|
CollarHelper.isOwner(collarStack, player) ||
|
|
CollarHelper.hasPublicTracking(collarStack)
|
|
) {
|
|
// Check if same dimension
|
|
boolean sameDimension = player
|
|
.level()
|
|
.dimension()
|
|
.equals(target.level().dimension());
|
|
|
|
if (sameDimension) {
|
|
double distance = player.distanceTo(target);
|
|
String direction = getDirection(player, target);
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.LOCATOR_DETECTED,
|
|
(int) distance + "m [" + direction + "]"
|
|
);
|
|
} else {
|
|
// Cross-dimension: show dimension name
|
|
String dimName = getDimensionDisplayName(
|
|
target
|
|
.level()
|
|
.dimension()
|
|
.location()
|
|
.getPath()
|
|
);
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.LOCATOR_DETECTED,
|
|
"Target is in [" + dimName + "]"
|
|
);
|
|
}
|
|
|
|
playLocatorSound(player);
|
|
} else {
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.ERROR,
|
|
"You are not allowed to access this GPS Collar!"
|
|
);
|
|
}
|
|
} else {
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.ERROR,
|
|
"Target is not wearing a GPS Collar!"
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.ERROR,
|
|
"Unable to locate target! (Offline)"
|
|
);
|
|
}
|
|
} else {
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.ERROR,
|
|
"No target connected!"
|
|
);
|
|
}
|
|
|
|
return InteractionResultHolder.success(stack);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
@Override
|
|
public InteractionResult interactLivingEntity(
|
|
ItemStack stack,
|
|
Player player,
|
|
LivingEntity target,
|
|
InteractionHand hand
|
|
) {
|
|
if (player.level().isClientSide) return InteractionResult.SUCCESS;
|
|
|
|
IBondageState playerState = KidnappedHelper.getKidnappedState(player);
|
|
if (
|
|
playerState != null && playerState.isTiedUp()
|
|
) return InteractionResult.FAIL;
|
|
|
|
if (!hasOwner(stack)) {
|
|
setOwner(stack, player);
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.LOCATOR_CLAIMED
|
|
);
|
|
} else if (!isOwner(stack, player)) {
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.LOCATOR_NOT_OWNER
|
|
);
|
|
return InteractionResult.FAIL;
|
|
}
|
|
|
|
IBondageState targetState = KidnappedHelper.getKidnappedState(target);
|
|
if (targetState != null) {
|
|
setTarget(stack, target);
|
|
player.setItemInHand(hand, stack); // Force sync
|
|
SystemMessageManager.sendChatToPlayer(
|
|
player,
|
|
"Connected to " + target.getName().getString(),
|
|
ChatFormatting.GREEN
|
|
);
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
|
|
return InteractionResult.PASS;
|
|
}
|
|
|
|
private String getDirection(Player source, Player target) {
|
|
double dx = target.getX() - source.getX();
|
|
double dz = target.getZ() - source.getZ();
|
|
|
|
if (Math.abs(dx) > Math.abs(dz)) {
|
|
return dx > 0 ? "EAST" : "WEST";
|
|
} else {
|
|
return dz > 0 ? "SOUTH" : "NORTH";
|
|
}
|
|
}
|
|
|
|
private void playLocatorSound(Player player) {
|
|
player
|
|
.level()
|
|
.playSound(
|
|
null,
|
|
player.blockPosition(),
|
|
com.tiedup.remake.core.ModSounds.SHOCKER_ACTIVATED.get(),
|
|
net.minecraft.sounds.SoundSource.PLAYERS,
|
|
0.5f,
|
|
1.0f
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get a user-friendly display name for a dimension.
|
|
*/
|
|
private String getDimensionDisplayName(String dimensionPath) {
|
|
return switch (dimensionPath) {
|
|
case "overworld" -> "Overworld";
|
|
case "the_nether" -> "The Nether";
|
|
case "the_end" -> "The End";
|
|
default -> dimensionPath.replace("_", " ");
|
|
};
|
|
}
|
|
}
|