D1: ThreadLocal alert suppression moved from ItemCollar to CollarHelper.
onCollarRemoved() logic (kidnapper alert) moved to CollarHelper.
D2+D3: Deleted 17 V1 item classes + 4 V1-only interfaces:
ItemBind, ItemGag, ItemBlindfold, ItemCollar, ItemEarplugs, ItemMittens,
ItemColor, ItemClassicCollar, ItemShockCollar, ItemShockCollarAuto,
ItemGpsCollar, ItemChokeCollar, ItemHood, ItemMedicalGag,
IBondageItem, IHasGaggingEffect, IHasBlindingEffect, IAdjustable
D4: KidnapperTheme/KidnapperItemSelector/DispenserBehaviors migrated
from variant enums to string-based DataDrivenItemRegistry IDs.
D5: Deleted 11 variant enums + Generic* factories + ItemBallGag3D:
BindVariant, GagVariant, BlindfoldVariant, EarplugsVariant, MittensVariant,
GenericBind, GenericGag, GenericBlindfold, GenericEarplugs, GenericMittens
D6: ModItems cleaned — all V1 bondage registrations removed.
D7: ModCreativeTabs rewritten — iterates DataDrivenItemRegistry.
D8+D9: All V2 helpers cleaned (V1 fallbacks removed), orphan imports removed.
Zero V1 bondage code references remain (only Javadoc comments).
All bondage items are now data-driven via 47 JSON definitions.
311 lines
11 KiB
Java
311 lines
11 KiB
Java
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.
|
|
*
|
|
* <p>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));
|
|
}
|
|
|
|
// ========== 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.
|
|
*
|
|
*/
|
|
@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 (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<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
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|