package com.tiedup.remake.items; import com.tiedup.remake.core.SystemMessageManager; import com.tiedup.remake.items.base.ItemOwnerTarget; import com.tiedup.remake.state.IBondageState; import com.tiedup.remake.util.KidnappedHelper; import com.tiedup.remake.v2.BodyRegionV2; import java.util.List; import java.util.UUID; import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; 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 net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; import org.jetbrains.annotations.Nullable; /** * Collar Key - Used to lock/unlock bondage items via the Slave Management GUI. * *

Key-Lock System

* */ public class ItemKey extends ItemOwnerTarget { private static final String NBT_KEY_UUID = "keyUUID"; public ItemKey() { super(new net.minecraft.world.item.Item.Properties().durability(64)); } // ========== Key UUID System ========== /** * Get the unique UUID for this key. * Generates one if it doesn't exist yet. * This UUID is used to identify locks created by this specific key. * * @param stack The key ItemStack * @return The key's unique UUID */ public UUID getKeyUUID(ItemStack stack) { CompoundTag tag = stack.getOrCreateTag(); if (!tag.hasUUID(NBT_KEY_UUID)) { // Generate a new UUID for this key tag.putUUID(NBT_KEY_UUID, UUID.randomUUID()); } return tag.getUUID(NBT_KEY_UUID); } /** * Check if this key matches the given UUID. * * @param stack The key ItemStack * @param lockUUID The lock's UUID to check against * @return true if this key matches the lock */ public boolean matchesLock(ItemStack stack, UUID lockUUID) { if (lockUUID == null) return false; return lockUUID.equals(getKeyUUID(stack)); } /** * Shows ownership and target information when hovering over the item in inventory. */ @Override public void appendHoverText( ItemStack stack, @Nullable Level level, List tooltip, TooltipFlag flag ) { super.appendHoverText(stack, level, tooltip, flag); if (hasOwner(stack)) { tooltip.add( Component.literal("Owner: ") .withStyle(ChatFormatting.GOLD) .append( Component.literal(getOwnerName(stack)).withStyle( ChatFormatting.WHITE ) ) ); } else { tooltip.add( Component.literal( "Unclaimed (Right-click a collar wearer to claim)" ).withStyle(ChatFormatting.GRAY) ); } if (hasTarget(stack)) { tooltip.add( Component.literal("Target: ") .withStyle(ChatFormatting.BLUE) .append( Component.literal(getTargetName(stack)).withStyle( ChatFormatting.WHITE ) ) ); } tooltip.add( Component.literal("Right-click a collared player to toggle LOCK") .withStyle(ChatFormatting.DARK_GRAY) .withStyle(ChatFormatting.ITALIC) ); } /** * Logic for interacting with entities wearing collars. * Opens the Slave Item Management GUI to lock/unlock individual items. * */ @Override public InteractionResult interactLivingEntity( ItemStack stack, Player player, LivingEntity target, InteractionHand hand ) { // Check if target can wear collars (Player, EntityDamsel, EntityKidnapper) IBondageState targetState = KidnappedHelper.getKidnappedState(target); // Target must be wearing a collar if (targetState == null || !targetState.hasCollar()) { if (!player.level().isClientSide) { SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.ERROR, "Target is not wearing a collar!" ); } return InteractionResult.FAIL; } // Server-side: Handle claiming and validation if (!player.level().isClientSide) { // 1. Claim logic - first interaction with a collar wearer links the key if (!hasOwner(stack)) { setOwner(stack, player); setTarget(stack, target); // Ensure key UUID is generated getKeyUUID(stack); // Also link the player to the collar (become collar owner) linkPlayerToCollar(player, target, targetState); SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.KEY_CLAIMED, target ); player.setItemInHand(hand, stack); // Sync NBT to client return InteractionResult.SUCCESS; } // 2. Ownership check - only the person who claimed the key can use it if (!isOwner(stack, player)) { SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.KEY_NOT_OWNER ); return InteractionResult.FAIL; } // 3. Target check - this key only fits the entity it was first linked to if ( target instanceof Player targetPlayer && !isTarget(stack, targetPlayer) ) { SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.KEY_WRONG_TARGET ); return InteractionResult.FAIL; } else if ( !(target instanceof Player) && !target.getUUID().equals(getTargetId(stack)) ) { SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.KEY_WRONG_TARGET ); return InteractionResult.FAIL; } // Server validation passed - client will open GUI return InteractionResult.SUCCESS; } // Client-side: Open the Slave Item Management GUI // Only open if key is already claimed and we're the owner (client trusts server validation) if (hasOwner(stack) && isOwner(stack, player)) { // Verify target matches (client-side check for responsiveness) boolean targetMatches = false; if (target instanceof Player targetPlayer) { targetMatches = isTarget(stack, targetPlayer); } else { targetMatches = target.getUUID().equals(getTargetId(stack)); } if (targetMatches) { openUnifiedBondageScreen(target); } } return InteractionResult.SUCCESS; } /** * Opens the UnifiedBondageScreen in master mode targeting a specific entity. * Called from client-side code only. * * @param target The living entity to manage bondage for */ @OnlyIn(Dist.CLIENT) private void openUnifiedBondageScreen( net.minecraft.world.entity.LivingEntity target ) { net.minecraft.client.Minecraft.getInstance().setScreen( new com.tiedup.remake.client.gui.screens.UnifiedBondageScreen( target ) ); } /** * Link a player to a collar - make them an owner. * *

When a key is claimed on a collared entity: *

* * @param player The player claiming the key * @param target The collared entity * @param targetState The target's IBondageState state */ private void linkPlayerToCollar( Player player, LivingEntity target, IBondageState targetState ) { ItemStack collarStack = targetState.getEquipment(BodyRegionV2.NECK); if (collarStack.isEmpty()) return; if (com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collarStack)) { // Add player as owner to the collar (if not already) if (!com.tiedup.remake.v2.bondage.CollarHelper.isOwner(collarStack, player)) { com.tiedup.remake.v2.bondage.CollarHelper.addOwner(collarStack, player); // Update the collar in the target's inventory targetState.equip(BodyRegionV2.NECK, collarStack); } // Register in CollarRegistry (if on server) if ( player.level() instanceof net.minecraft.server.level.ServerLevel serverLevel ) { com.tiedup.remake.state.CollarRegistry registry = com.tiedup.remake.state.CollarRegistry.get(serverLevel); if (registry != null) { registry.registerCollar(target.getUUID(), player.getUUID()); // Sync the registry to the new owner if ( player instanceof net.minecraft.server.level.ServerPlayer serverPlayer ) { java.util.Set slaves = registry.getSlaves( player.getUUID() ); com.tiedup.remake.network.ModNetwork.sendToPlayer( new com.tiedup.remake.network.sync.PacketSyncCollarRegistry( slaves ), serverPlayer ); } } // Sync the target's inventory (collar was modified) if ( target instanceof net.minecraft.server.level.ServerPlayer targetPlayer ) { com.tiedup.remake.network.sync.SyncManager.syncInventory( targetPlayer ); } } } } }