Files
TiedUp-/src/main/java/com/tiedup/remake/items/ItemKey.java
NotEvil f6466360b6 Clean repo for open source release
Remove build artifacts, dev tool configs, unused dependencies,
and third-party source dumps. Add proper README, update .gitignore,
clean up Makefile.
2026-04-12 00:51:22 +02:00

314 lines
11 KiB
Java

package com.tiedup.remake.items;
import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.items.base.ItemOwnerTarget;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.util.KidnappedHelper;
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.
*
* <p>Phase 20: Key-Lock System</p>
* <ul>
* <li><b>Linking:</b> Right-click a player wearing a collar to link (claim) the key to them.</li>
* <li><b>Management:</b> Opens SlaveItemManagementScreen to lock/unlock individual items.</li>
* <li><b>Key UUID:</b> Each key has a unique UUID used to identify which locks it created.</li>
* <li><b>Security:</b> Only items locked with this key can be unlocked by it.</li>
* </ul>
*/
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));
}
// ========== Phase 20: 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<Component> 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.
*
* Phase 14.1.5: Refactored to support IBondageState (LivingEntity + NPCs)
* Phase 20: Opens GUI instead of direct toggle
*/
@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.
*
* <p>When a key is claimed on a collared entity:
* <ul>
* <li>Add player as owner to the collar item</li>
* <li>Register the relationship in CollarRegistry</li>
* </ul>
*
* @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 (
collarStack.getItem() instanceof
com.tiedup.remake.items.base.ItemCollar collar
) {
// Add player as owner to the collar (if not already)
if (!collar.getOwners(collarStack).contains(player.getUUID())) {
collar.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<UUID> 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
);
}
}
}
}
}