Compare commits
27 Commits
feature/d0
...
feature/d0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d128124e6 | ||
|
|
3df979ceee | ||
|
|
a572513640 | ||
|
|
dfa7024e21 | ||
|
|
9302b6ccaf | ||
|
|
099cd0d984 | ||
| fccb99ef9a | |||
|
|
b04497b5a1 | ||
|
|
3515c89f82 | ||
|
|
3d61c9e9e6 | ||
| 52d1044e9a | |||
|
|
530b86a9a7 | ||
|
|
258223bf68 | ||
|
|
679d7033f9 | ||
| 8bfd97ba57 | |||
|
|
df56ebb6bc | ||
|
|
b97bdf367e | ||
|
|
eb7f06bfc8 | ||
|
|
5c4e4c2352 | ||
|
|
eee4825aba | ||
|
|
b359c6be35 | ||
|
|
19cc69985d | ||
|
|
737a4fd59b | ||
|
|
b79225d684 | ||
|
|
751bad418d | ||
|
|
b81d3eed95 | ||
| fa4c332a10 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -45,3 +45,4 @@ build_output.log
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
docs/
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package com.tiedup.remake.blocks.entity;
|
||||
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.ItemBlindfold;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.items.base.ItemEarplugs;
|
||||
import com.tiedup.remake.items.base.ItemGag;
|
||||
import com.tiedup.remake.items.clothes.GenericClothes;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -191,55 +194,43 @@ public abstract class BondageItemBlockEntity
|
||||
|
||||
@Override
|
||||
public void readBondageData(CompoundTag tag) {
|
||||
// Read bind with type validation
|
||||
// Read bind with type validation (V2 ARMS-region item)
|
||||
if (tag.contains("bind")) {
|
||||
ItemStack bindStack = ItemStack.of(tag.getCompound("bind"));
|
||||
if (
|
||||
!bindStack.isEmpty() && bindStack.getItem() instanceof ItemBind
|
||||
) {
|
||||
if (!bindStack.isEmpty() && BindModeHelper.isBindItem(bindStack)) {
|
||||
this.bind = bindStack;
|
||||
}
|
||||
}
|
||||
|
||||
// Read gag with type validation
|
||||
// Read gag with type validation (V2 GAGGING component)
|
||||
if (tag.contains("gag")) {
|
||||
ItemStack gagStack = ItemStack.of(tag.getCompound("gag"));
|
||||
if (!gagStack.isEmpty() && gagStack.getItem() instanceof ItemGag) {
|
||||
if (!gagStack.isEmpty()
|
||||
&& DataDrivenBondageItem.getComponent(gagStack, ComponentType.GAGGING, GaggingComponent.class) != null) {
|
||||
this.gag = gagStack;
|
||||
}
|
||||
}
|
||||
|
||||
// Read blindfold with type validation
|
||||
// Read blindfold with type validation (V2 EYES-region item)
|
||||
if (tag.contains("blindfold")) {
|
||||
ItemStack blindfoldStack = ItemStack.of(
|
||||
tag.getCompound("blindfold")
|
||||
);
|
||||
if (
|
||||
!blindfoldStack.isEmpty() &&
|
||||
blindfoldStack.getItem() instanceof ItemBlindfold
|
||||
) {
|
||||
ItemStack blindfoldStack = ItemStack.of(tag.getCompound("blindfold"));
|
||||
if (!blindfoldStack.isEmpty() && isDataDrivenForRegion(blindfoldStack, BodyRegionV2.EYES)) {
|
||||
this.blindfold = blindfoldStack;
|
||||
}
|
||||
}
|
||||
|
||||
// Read earplugs with type validation
|
||||
// Read earplugs with type validation (V2 EARS-region item)
|
||||
if (tag.contains("earplugs")) {
|
||||
ItemStack earplugsStack = ItemStack.of(tag.getCompound("earplugs"));
|
||||
if (
|
||||
!earplugsStack.isEmpty() &&
|
||||
earplugsStack.getItem() instanceof ItemEarplugs
|
||||
) {
|
||||
if (!earplugsStack.isEmpty() && isDataDrivenForRegion(earplugsStack, BodyRegionV2.EARS)) {
|
||||
this.earplugs = earplugsStack;
|
||||
}
|
||||
}
|
||||
|
||||
// Read collar with type validation
|
||||
// Read collar with type validation (V2 collar)
|
||||
if (tag.contains("collar")) {
|
||||
ItemStack collarStack = ItemStack.of(tag.getCompound("collar"));
|
||||
if (
|
||||
!collarStack.isEmpty() &&
|
||||
collarStack.getItem() instanceof ItemCollar
|
||||
) {
|
||||
if (!collarStack.isEmpty() && CollarHelper.isCollar(collarStack)) {
|
||||
this.collar = collarStack;
|
||||
}
|
||||
}
|
||||
@@ -279,6 +270,14 @@ public abstract class BondageItemBlockEntity
|
||||
return tag;
|
||||
}
|
||||
|
||||
// V2 HELPERS
|
||||
|
||||
/** Check if a stack is a data-driven item occupying the given body region. */
|
||||
private static boolean isDataDrivenForRegion(ItemStack stack, BodyRegionV2 region) {
|
||||
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
||||
return def != null && def.occupiedRegions().contains(region);
|
||||
}
|
||||
|
||||
// NETWORK SYNC
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package com.tiedup.remake.blocks.entity;
|
||||
|
||||
import com.tiedup.remake.items.base.*;
|
||||
import com.tiedup.remake.items.clothes.GenericClothes;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -45,7 +52,7 @@ public class TrappedChestBlockEntity
|
||||
|
||||
@Override
|
||||
public void setBind(ItemStack stack) {
|
||||
if (stack.isEmpty() || stack.getItem() instanceof ItemBind) {
|
||||
if (stack.isEmpty() || BindModeHelper.isBindItem(stack)) {
|
||||
this.bind = stack;
|
||||
setChangedAndSync();
|
||||
}
|
||||
@@ -58,7 +65,8 @@ public class TrappedChestBlockEntity
|
||||
|
||||
@Override
|
||||
public void setGag(ItemStack stack) {
|
||||
if (stack.isEmpty() || stack.getItem() instanceof ItemGag) {
|
||||
if (stack.isEmpty()
|
||||
|| DataDrivenBondageItem.getComponent(stack, ComponentType.GAGGING, GaggingComponent.class) != null) {
|
||||
this.gag = stack;
|
||||
setChangedAndSync();
|
||||
}
|
||||
@@ -71,7 +79,7 @@ public class TrappedChestBlockEntity
|
||||
|
||||
@Override
|
||||
public void setBlindfold(ItemStack stack) {
|
||||
if (stack.isEmpty() || stack.getItem() instanceof ItemBlindfold) {
|
||||
if (stack.isEmpty() || isDataDrivenForRegion(stack, BodyRegionV2.EYES)) {
|
||||
this.blindfold = stack;
|
||||
setChangedAndSync();
|
||||
}
|
||||
@@ -84,7 +92,7 @@ public class TrappedChestBlockEntity
|
||||
|
||||
@Override
|
||||
public void setEarplugs(ItemStack stack) {
|
||||
if (stack.isEmpty() || stack.getItem() instanceof ItemEarplugs) {
|
||||
if (stack.isEmpty() || isDataDrivenForRegion(stack, BodyRegionV2.EARS)) {
|
||||
this.earplugs = stack;
|
||||
setChangedAndSync();
|
||||
}
|
||||
@@ -97,7 +105,7 @@ public class TrappedChestBlockEntity
|
||||
|
||||
@Override
|
||||
public void setCollar(ItemStack stack) {
|
||||
if (stack.isEmpty() || stack.getItem() instanceof ItemCollar) {
|
||||
if (stack.isEmpty() || CollarHelper.isCollar(stack)) {
|
||||
this.collar = stack;
|
||||
setChangedAndSync();
|
||||
}
|
||||
@@ -183,6 +191,14 @@ public class TrappedChestBlockEntity
|
||||
writeBondageData(tag);
|
||||
}
|
||||
|
||||
// V2 HELPERS
|
||||
|
||||
/** Check if a stack is a data-driven item occupying the given body region. */
|
||||
private static boolean isDataDrivenForRegion(ItemStack stack, BodyRegionV2 region) {
|
||||
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
||||
return def != null && def.occupiedRegions().contains(region);
|
||||
}
|
||||
|
||||
// NETWORK SYNC
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,8 +2,8 @@ package com.tiedup.remake.cells;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.prison.PrisonerManager;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.state.IRestrainable;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -231,7 +231,7 @@ public final class CampLifecycleManager {
|
||||
}
|
||||
|
||||
// Suppress collar removal alerts - this is a legitimate release (camp death)
|
||||
ItemCollar.runWithSuppressedAlert(() -> {
|
||||
CollarHelper.runWithSuppressedAlert(() -> {
|
||||
// Unlock collar if owned by the dead camp/trader
|
||||
unlockCollarIfOwnedBy(prisoner, state, traderUUID);
|
||||
|
||||
@@ -285,8 +285,8 @@ public final class CampLifecycleManager {
|
||||
return;
|
||||
}
|
||||
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
List<UUID> owners = collarItem.getOwners(collar);
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
List<UUID> owners = CollarHelper.getOwners(collar);
|
||||
|
||||
// If the dead trader/camp is an owner, unlock the collar
|
||||
if (owners.contains(ownerUUID)) {
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.tiedup.remake.client;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.GenericBind;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
|
||||
@@ -141,12 +141,7 @@ public class FirstPersonMittensRenderer {
|
||||
net.minecraft.world.item.ItemStack bindStack =
|
||||
V2EquipmentHelper.getInRegion(player, BodyRegionV2.ARMS);
|
||||
if (bindStack.isEmpty()) return false;
|
||||
if (bindStack.getItem() instanceof GenericBind bind) {
|
||||
BindVariant variant = bind.getVariant();
|
||||
return (
|
||||
variant == BindVariant.WRAP || variant == BindVariant.LATEX_SACK
|
||||
);
|
||||
}
|
||||
return false;
|
||||
PoseType poseType = PoseTypeHelper.getPoseType(bindStack);
|
||||
return poseType == PoseType.WRAP || poseType == PoseType.LATEX_SACK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.tiedup.remake.client.gui.screens.UnifiedBondageScreen;
|
||||
import com.tiedup.remake.core.ModConfig;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.network.action.PacketForceSeatModifier;
|
||||
import com.tiedup.remake.network.action.PacketStruggle;
|
||||
@@ -428,11 +428,8 @@ public class ModKeybindings {
|
||||
target,
|
||||
BodyRegionV2.NECK
|
||||
);
|
||||
if (
|
||||
!collarStack.isEmpty() &&
|
||||
collarStack.getItem() instanceof ItemCollar collar
|
||||
) {
|
||||
return collar.isOwner(collarStack, player);
|
||||
if (!collarStack.isEmpty() && CollarHelper.isCollar(collarStack)) {
|
||||
return CollarHelper.isOwner(collarStack, player);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.tiedup.remake.client.animation.render;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.state.HumanChairHelper;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -93,14 +93,11 @@ public class DogPoseRenderHandler {
|
||||
}
|
||||
|
||||
ItemStack bindForPose = state.getEquipment(BodyRegionV2.ARMS);
|
||||
if (
|
||||
bindForPose.isEmpty() ||
|
||||
!(bindForPose.getItem() instanceof ItemBind itemBind)
|
||||
) {
|
||||
if (bindForPose.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PoseType bindPoseType = itemBind.getPoseType();
|
||||
PoseType bindPoseType = PoseTypeHelper.getPoseType(bindForPose);
|
||||
// Check for humanChairMode NBT override
|
||||
bindPoseType = HumanChairHelper.resolveEffectivePose(
|
||||
bindPoseType,
|
||||
|
||||
@@ -2,8 +2,8 @@ package com.tiedup.remake.client.animation.render;
|
||||
|
||||
import com.tiedup.remake.client.state.PetBedClientState;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.state.HumanChairHelper;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -83,11 +83,9 @@ public class PetBedRenderHandler {
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
if (state == null) return false;
|
||||
ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
|
||||
if (
|
||||
bind.isEmpty() || !(bind.getItem() instanceof ItemBind itemBind)
|
||||
) return false;
|
||||
if (bind.isEmpty()) return false;
|
||||
PoseType pose = HumanChairHelper.resolveEffectivePose(
|
||||
itemBind.getPoseType(),
|
||||
PoseTypeHelper.getPoseType(bind),
|
||||
bind
|
||||
);
|
||||
return pose == PoseType.DOG || pose == PoseType.HUMAN_CHAIR;
|
||||
|
||||
@@ -2,8 +2,8 @@ package com.tiedup.remake.client.animation.render;
|
||||
|
||||
import com.tiedup.remake.client.renderer.layers.ClothesRenderHelper;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.items.clothes.ClothesProperties;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -71,10 +71,8 @@ public class PlayerArmHideEventHandler {
|
||||
// === HIDE ARMS (wrap/latex_sack poses) ===
|
||||
if (state.hasArmsBound()) {
|
||||
ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
|
||||
if (
|
||||
!bind.isEmpty() && bind.getItem() instanceof ItemBind itemBind
|
||||
) {
|
||||
PoseType poseType = itemBind.getPoseType();
|
||||
if (!bind.isEmpty()) {
|
||||
PoseType poseType = PoseTypeHelper.getPoseType(bind);
|
||||
|
||||
// Only hide arms for wrap/sack poses (arms are covered by the item)
|
||||
if (
|
||||
|
||||
@@ -14,8 +14,9 @@ import com.tiedup.remake.client.gltf.GltfAnimationApplier;
|
||||
import com.tiedup.remake.client.state.ClothesClientCache;
|
||||
import com.tiedup.remake.client.state.MovementStyleClientState;
|
||||
import com.tiedup.remake.client.state.PetBedClientState;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.state.HumanChairHelper;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -249,14 +250,10 @@ public class AnimationTickHandler {
|
||||
PlayerBindState state
|
||||
) {
|
||||
ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
|
||||
PoseType poseType = PoseType.STANDARD;
|
||||
PoseType poseType = PoseTypeHelper.getPoseType(bind);
|
||||
|
||||
if (bind.getItem() instanceof ItemBind itemBind) {
|
||||
poseType = itemBind.getPoseType();
|
||||
|
||||
// Human chair mode: override DOG pose to HUMAN_CHAIR (straight limbs)
|
||||
poseType = HumanChairHelper.resolveEffectivePose(poseType, bind);
|
||||
}
|
||||
// Human chair mode: override DOG pose to HUMAN_CHAIR (straight limbs)
|
||||
poseType = HumanChairHelper.resolveEffectivePose(poseType, bind);
|
||||
|
||||
// Derive bound state from V2 regions (works client-side, synced via capability)
|
||||
boolean armsBound = V2EquipmentHelper.isRegionOccupied(
|
||||
@@ -268,10 +265,10 @@ public class AnimationTickHandler {
|
||||
BodyRegionV2.LEGS
|
||||
);
|
||||
|
||||
// V1 fallback: if no V2 regions are set but player is tied, derive from ItemBind NBT
|
||||
if (!armsBound && !legsBound && bind.getItem() instanceof ItemBind) {
|
||||
armsBound = ItemBind.hasArmsBound(bind);
|
||||
legsBound = ItemBind.hasLegsBound(bind);
|
||||
// V1 fallback: if no V2 regions are set but player is tied, derive from bind mode NBT
|
||||
if (!armsBound && !legsBound && BindModeHelper.isBindItem(bind)) {
|
||||
armsBound = BindModeHelper.hasArmsBound(bind);
|
||||
legsBound = BindModeHelper.hasLegsBound(bind);
|
||||
}
|
||||
|
||||
boolean isStruggling = state.isStruggling();
|
||||
|
||||
@@ -9,8 +9,9 @@ import com.tiedup.remake.client.gltf.GltfAnimationApplier;
|
||||
import com.tiedup.remake.entities.AbstractTiedUpNpc;
|
||||
import com.tiedup.remake.entities.EntityMaster;
|
||||
import com.tiedup.remake.entities.ai.master.MasterState;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.IV2BondageEquipment;
|
||||
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
|
||||
@@ -161,13 +162,8 @@ public class NpcAnimationTickHandler {
|
||||
net.minecraft.world.item.ItemStack bind = entity.getEquipment(
|
||||
BodyRegionV2.ARMS
|
||||
);
|
||||
PoseType poseType = PoseType.STANDARD;
|
||||
boolean hasBind = false;
|
||||
|
||||
if (bind.getItem() instanceof ItemBind itemBind) {
|
||||
poseType = itemBind.getPoseType();
|
||||
hasBind = true;
|
||||
}
|
||||
PoseType poseType = PoseTypeHelper.getPoseType(bind);
|
||||
boolean hasBind = BindModeHelper.isBindItem(bind);
|
||||
|
||||
// Derive bound state from V2 regions (AbstractTiedUpNpc implements IV2EquipmentHolder)
|
||||
boolean armsBound = V2EquipmentHelper.isRegionOccupied(
|
||||
@@ -179,10 +175,10 @@ public class NpcAnimationTickHandler {
|
||||
BodyRegionV2.LEGS
|
||||
);
|
||||
|
||||
// V1 fallback: if no V2 regions set but NPC has a bind, derive from ItemBind NBT
|
||||
if (!armsBound && !legsBound && bind.getItem() instanceof ItemBind) {
|
||||
armsBound = ItemBind.hasArmsBound(bind);
|
||||
legsBound = ItemBind.hasLegsBound(bind);
|
||||
// V1 fallback: if no V2 regions set but NPC has a bind, derive from bind mode NBT
|
||||
if (!armsBound && !legsBound && BindModeHelper.isBindItem(bind)) {
|
||||
armsBound = BindModeHelper.hasArmsBound(bind);
|
||||
legsBound = BindModeHelper.hasLegsBound(bind);
|
||||
}
|
||||
|
||||
boolean isStruggling = entity.isStruggling();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.tiedup.remake.client.events;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -138,7 +138,7 @@ public class LeashProxyClientHandler {
|
||||
ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
|
||||
if (
|
||||
!bind.isEmpty() &&
|
||||
bind.getItem() == ModItems.getBind(BindVariant.DOGBINDER)
|
||||
PoseTypeHelper.getPoseType(bind) == PoseType.DOG
|
||||
) {
|
||||
return DOGWALK_Y_OFFSET;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
package com.tiedup.remake.client.events;
|
||||
|
||||
import com.tiedup.remake.items.base.*;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.network.selfbondage.PacketSelfBondage;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.v2.bondage.IV2BondageItem;
|
||||
import com.tiedup.remake.v2.bondage.component.BlindingComponent;
|
||||
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
@@ -70,7 +76,7 @@ public class SelfBondageInputHandler {
|
||||
if (!event.getLevel().isClientSide()) return;
|
||||
|
||||
ItemStack stack = event.getItemStack();
|
||||
if (isSelfBondageItem(stack.getItem())) {
|
||||
if (isSelfBondageItem(stack)) {
|
||||
event.setCanceled(true);
|
||||
startSelfBondage();
|
||||
}
|
||||
@@ -87,11 +93,11 @@ public class SelfBondageInputHandler {
|
||||
InteractionHand hand = InteractionHand.MAIN_HAND;
|
||||
ItemStack stack = player.getMainHandItem();
|
||||
|
||||
if (!isSelfBondageItem(stack.getItem())) {
|
||||
if (!isSelfBondageItem(stack)) {
|
||||
stack = player.getOffhandItem();
|
||||
hand = InteractionHand.OFF_HAND;
|
||||
|
||||
if (!isSelfBondageItem(stack.getItem())) {
|
||||
if (!isSelfBondageItem(stack)) {
|
||||
return; // No bondage item in either hand
|
||||
}
|
||||
}
|
||||
@@ -130,7 +136,7 @@ public class SelfBondageInputHandler {
|
||||
|
||||
// Check if still holding bondage item in the active hand
|
||||
ItemStack stack = player.getItemInHand(activeHand);
|
||||
if (!isSelfBondageItem(stack.getItem())) {
|
||||
if (!isSelfBondageItem(stack)) {
|
||||
stopSelfBondage();
|
||||
return;
|
||||
}
|
||||
@@ -153,27 +159,31 @@ public class SelfBondageInputHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an item supports self-bondage.
|
||||
* Check if a stack supports self-bondage.
|
||||
* Collar is explicitly excluded.
|
||||
*/
|
||||
private static boolean isSelfBondageItem(Item item) {
|
||||
// Collar cannot be self-equipped (V1 collar guard)
|
||||
if (item instanceof ItemCollar) {
|
||||
private static boolean isSelfBondageItem(ItemStack stack) {
|
||||
if (stack.isEmpty()) return false;
|
||||
|
||||
// Collar cannot be self-equipped (V2 ownership component)
|
||||
if (CollarHelper.isCollar(stack)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// V2 bondage items support self-bondage (left-click hold with tying duration)
|
||||
if (item instanceof IV2BondageItem) {
|
||||
if (stack.getItem() instanceof IV2BondageItem) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// V1 bondage items (legacy)
|
||||
return (
|
||||
item instanceof ItemBind ||
|
||||
item instanceof ItemGag ||
|
||||
item instanceof ItemBlindfold ||
|
||||
item instanceof ItemMittens ||
|
||||
item instanceof ItemEarplugs
|
||||
);
|
||||
// V2 data-driven items: check if it occupies any bondage region
|
||||
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
||||
if (def != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// V1 fallback: bind items
|
||||
return BindModeHelper.isBindItem(stack)
|
||||
|| DataDrivenBondageItem.getComponent(stack, ComponentType.GAGGING, GaggingComponent.class) != null
|
||||
|| DataDrivenBondageItem.getComponent(stack, ComponentType.BLINDING, BlindingComponent.class) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.tiedup.remake.client.gui.screens;
|
||||
import com.tiedup.remake.client.gui.util.GuiColors;
|
||||
import com.tiedup.remake.client.gui.util.GuiLayoutConstants;
|
||||
import com.tiedup.remake.client.gui.widgets.SlaveEntryWidget;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.network.slave.PacketSlaveAction;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
@@ -145,8 +145,8 @@ public class SlaveManagementScreen extends BaseScreen {
|
||||
ItemStack collarStack = kidnapped.getEquipment(
|
||||
BodyRegionV2.NECK
|
||||
);
|
||||
if (collarStack.getItem() instanceof ItemCollar collar) {
|
||||
if (collar.isOwner(collarStack, player)) {
|
||||
if (CollarHelper.isCollar(collarStack)) {
|
||||
if (CollarHelper.isOwner(collarStack, player)) {
|
||||
addSlaveEntry(kidnapped);
|
||||
addedUUIDs.add(entity.getUUID());
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.tiedup.remake.items.ItemLockpick;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.IHasResistance;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.network.action.PacketSetKnifeCutTarget;
|
||||
import com.tiedup.remake.network.minigame.PacketLockpickMiniGameStart;
|
||||
@@ -474,10 +474,10 @@ public class ActionPanel extends AbstractWidget {
|
||||
// Bondage Service toggle (NECK collar only, prison configured)
|
||||
if (
|
||||
selectedRegion == BodyRegionV2.NECK &&
|
||||
selectedItem.getItem() instanceof ItemCollar collar
|
||||
CollarHelper.isCollar(selectedItem)
|
||||
) {
|
||||
if (collar.hasCellAssigned(selectedItem)) {
|
||||
boolean svcEnabled = collar.isBondageServiceEnabled(
|
||||
if (CollarHelper.hasCellAssigned(selectedItem)) {
|
||||
boolean svcEnabled = CollarHelper.isBondageServiceEnabled(
|
||||
selectedItem
|
||||
);
|
||||
String svcKey = svcEnabled
|
||||
|
||||
@@ -4,8 +4,7 @@ import static com.tiedup.remake.client.gui.util.GuiLayoutConstants.*;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.tiedup.remake.client.gui.util.GuiColors;
|
||||
import com.tiedup.remake.items.ItemGpsCollar;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import java.util.ArrayList;
|
||||
@@ -370,9 +369,8 @@ public class SlaveEntryWidget
|
||||
// GPS zone status (right of health)
|
||||
if (hasGPSCollar()) {
|
||||
ItemStack collarStack = slave.getEquipment(BodyRegionV2.NECK);
|
||||
if (collarStack.getItem() instanceof ItemGpsCollar gps) {
|
||||
if (CollarHelper.hasGPS(collarStack)) {
|
||||
boolean inSafeZone = isInAnySafeZone(
|
||||
gps,
|
||||
collarStack,
|
||||
entity
|
||||
);
|
||||
@@ -560,33 +558,35 @@ public class SlaveEntryWidget
|
||||
private boolean hasShockCollar() {
|
||||
if (!slave.hasCollar()) return false;
|
||||
ItemStack collar = slave.getEquipment(BodyRegionV2.NECK);
|
||||
return (
|
||||
collar.getItem() instanceof ItemCollar itemCollar &&
|
||||
itemCollar.canShock()
|
||||
);
|
||||
return CollarHelper.canShock(collar);
|
||||
}
|
||||
|
||||
private boolean hasGPSCollar() {
|
||||
if (!slave.hasCollar()) return false;
|
||||
ItemStack collar = slave.getEquipment(BodyRegionV2.NECK);
|
||||
return (
|
||||
collar.getItem() instanceof ItemCollar itemCollar &&
|
||||
itemCollar.hasGPS()
|
||||
);
|
||||
return CollarHelper.hasGPS(collar);
|
||||
}
|
||||
|
||||
private boolean isInAnySafeZone(
|
||||
ItemGpsCollar gps,
|
||||
ItemStack collarStack,
|
||||
LivingEntity entity
|
||||
) {
|
||||
if (!gps.isActive(collarStack)) return true;
|
||||
if (!CollarHelper.isActive(collarStack)) return true;
|
||||
|
||||
var safeSpots = gps.getSafeSpots(collarStack);
|
||||
// Read safe spots from NBT
|
||||
net.minecraft.nbt.CompoundTag tag = collarStack.getTag();
|
||||
if (tag == null || !tag.contains("safeSpots", net.minecraft.nbt.Tag.TAG_LIST)) return true;
|
||||
net.minecraft.nbt.ListTag safeSpots = tag.getList("safeSpots", net.minecraft.nbt.Tag.TAG_COMPOUND);
|
||||
if (safeSpots.isEmpty()) return true;
|
||||
|
||||
for (var spot : safeSpots) {
|
||||
if (spot.isInside(entity)) {
|
||||
for (int i = 0; i < safeSpots.size(); i++) {
|
||||
net.minecraft.nbt.CompoundTag spot = safeSpots.getCompound(i);
|
||||
double x = spot.getDouble("x");
|
||||
double y = spot.getDouble("y");
|
||||
double z = spot.getDouble("z");
|
||||
int radius = spot.contains("radius") ? spot.getInt("radius") : 50;
|
||||
double dist = entity.distanceToSqr(x, y, z);
|
||||
if (dist <= (double) radius * radius) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,9 @@ import com.tiedup.remake.entities.AbstractTiedUpNpc;
|
||||
import com.tiedup.remake.entities.EntityKidnapperArcher;
|
||||
import com.tiedup.remake.entities.EntityMaster;
|
||||
import com.tiedup.remake.entities.ai.master.MasterState;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.items.clothes.GenericClothes;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
|
||||
@@ -230,11 +231,7 @@ public class DamselModel
|
||||
|
||||
if (inPose) {
|
||||
ItemStack bind = entity.getEquipment(BodyRegionV2.ARMS);
|
||||
PoseType poseType = PoseType.STANDARD;
|
||||
|
||||
if (bind.getItem() instanceof ItemBind itemBind) {
|
||||
poseType = itemBind.getPoseType();
|
||||
}
|
||||
PoseType poseType = PoseTypeHelper.getPoseType(bind);
|
||||
|
||||
// Hide arms for wrap/latex_sack poses
|
||||
if (poseType == PoseType.WRAP || poseType == PoseType.LATEX_SACK) {
|
||||
@@ -252,9 +249,7 @@ public class DamselModel
|
||||
PoseType currentPoseType = PoseType.STANDARD;
|
||||
if (inPose) {
|
||||
ItemStack bindForPoseType = entity.getEquipment(BodyRegionV2.ARMS);
|
||||
if (bindForPoseType.getItem() instanceof ItemBind itemBindForType) {
|
||||
currentPoseType = itemBindForType.getPoseType();
|
||||
}
|
||||
currentPoseType = PoseTypeHelper.getPoseType(bindForPoseType);
|
||||
}
|
||||
|
||||
// Check if this is a Master in human chair mode (head should look around freely)
|
||||
@@ -306,11 +301,7 @@ public class DamselModel
|
||||
// Animation not yet active (1-frame delay) - apply static pose as fallback
|
||||
// This ensures immediate visual feedback when bind is applied
|
||||
ItemStack bind = entity.getEquipment(BodyRegionV2.ARMS);
|
||||
PoseType fallbackPoseType = PoseType.STANDARD;
|
||||
|
||||
if (bind.getItem() instanceof ItemBind itemBind) {
|
||||
fallbackPoseType = itemBind.getPoseType();
|
||||
}
|
||||
PoseType fallbackPoseType = PoseTypeHelper.getPoseType(bind);
|
||||
|
||||
// Derive bound state from V2 regions (AbstractTiedUpNpc implements IV2EquipmentHolder)
|
||||
boolean armsBound = V2EquipmentHelper.isRegionOccupied(
|
||||
@@ -323,10 +314,10 @@ public class DamselModel
|
||||
);
|
||||
|
||||
if (
|
||||
!armsBound && !legsBound && bind.getItem() instanceof ItemBind
|
||||
!armsBound && !legsBound && BindModeHelper.isBindItem(bind)
|
||||
) {
|
||||
armsBound = ItemBind.hasArmsBound(bind);
|
||||
legsBound = ItemBind.hasLegsBound(bind);
|
||||
armsBound = BindModeHelper.hasArmsBound(bind);
|
||||
legsBound = BindModeHelper.hasLegsBound(bind);
|
||||
}
|
||||
|
||||
// Apply static pose directly to model parts
|
||||
|
||||
@@ -7,8 +7,8 @@ import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.tiedup.remake.cells.CellDataV2;
|
||||
import com.tiedup.remake.cells.CellRegistryV2;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.util.teleport.Position;
|
||||
import com.tiedup.remake.util.teleport.TeleportHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -160,8 +160,8 @@ public class CollarCommand {
|
||||
}
|
||||
|
||||
if (source.getEntity() instanceof ServerPlayer executor) {
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
collarItem.addOwner(collar, executor);
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
CollarHelper.addOwner(collar, executor);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
@@ -195,8 +195,8 @@ public class CollarCommand {
|
||||
}
|
||||
|
||||
if (source.getEntity() instanceof ServerPlayer executor) {
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
collarItem.removeOwner(collar, executor.getUUID());
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
CollarHelper.removeOwner(collar, executor.getUUID());
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
@@ -230,8 +230,8 @@ public class CollarCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
collarItem.setNickname(collar, name);
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
CollarHelper.setNickname(collar, name);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
@@ -261,8 +261,8 @@ public class CollarCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
collarItem.addOwner(collar, owner);
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
CollarHelper.addOwner(collar, owner);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
@@ -296,8 +296,8 @@ public class CollarCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
collarItem.removeOwner(collar, owner.getUUID());
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
CollarHelper.removeOwner(collar, owner.getUUID());
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
@@ -348,8 +348,8 @@ public class CollarCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
collarItem.setCellId(collar, cell.getId());
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
CollarHelper.setCellId(collar, cell.getId());
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
@@ -388,8 +388,8 @@ public class CollarCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (!collarItem.hasCellAssigned(collar)) {
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
if (!CollarHelper.hasCellAssigned(collar)) {
|
||||
source.sendFailure(
|
||||
Component.literal("No cell assigned to collar")
|
||||
);
|
||||
@@ -397,7 +397,7 @@ public class CollarCommand {
|
||||
}
|
||||
|
||||
// Get cell position and teleport
|
||||
java.util.UUID cellId = collarItem.getCellId(collar);
|
||||
java.util.UUID cellId = CollarHelper.getCellId(collar);
|
||||
ServerLevel serverLevel = source.getLevel();
|
||||
CellDataV2 cell = CellRegistryV2.get(serverLevel).getCell(cellId);
|
||||
|
||||
@@ -449,7 +449,7 @@ public class CollarCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
@@ -460,24 +460,24 @@ public class CollarCommand {
|
||||
false
|
||||
);
|
||||
|
||||
String nickname = collarItem.getNickname(collar);
|
||||
String nickname = CollarHelper.getNickname(collar);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§7Nickname: §f" +
|
||||
(nickname.isEmpty() ? "None" : nickname)
|
||||
(nickname == null || nickname.isEmpty() ? "None" : nickname)
|
||||
),
|
||||
false
|
||||
);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§7Has Owner: §f" + collarItem.hasOwner(collar)
|
||||
"§7Has Owner: §f" + CollarHelper.hasOwner(collar)
|
||||
),
|
||||
false
|
||||
);
|
||||
// Cell assignment
|
||||
java.util.UUID cellId = collarItem.getCellId(collar);
|
||||
java.util.UUID cellId = CollarHelper.getCellId(collar);
|
||||
if (cellId != null) {
|
||||
ServerLevel serverLevel = source.getLevel();
|
||||
CellRegistryV2 registry = CellRegistryV2.get(serverLevel);
|
||||
@@ -510,10 +510,12 @@ public class CollarCommand {
|
||||
);
|
||||
}
|
||||
|
||||
boolean locked = collar.getItem() instanceof com.tiedup.remake.items.base.ILockable lockable
|
||||
&& lockable.isLocked(collar);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§7Locked: §f" + collarItem.isLocked(collar)
|
||||
"§7Locked: §f" + locked
|
||||
),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -4,12 +4,10 @@ import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.items.base.BlindfoldVariant;
|
||||
import com.tiedup.remake.items.base.EarplugsVariant;
|
||||
import com.tiedup.remake.items.base.GagVariant;
|
||||
import com.tiedup.remake.items.base.KnifeVariant;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -82,53 +80,32 @@ public class KidnapSetCommand {
|
||||
int given = 0;
|
||||
|
||||
// Binds
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.getBind(BindVariant.ROPES), 8)
|
||||
);
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.getBind(BindVariant.CHAIN), 4)
|
||||
);
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.getBind(BindVariant.LEATHER_STRAPS), 4)
|
||||
);
|
||||
given += giveDataDrivenItems(player, "ropes", 8);
|
||||
given += giveDataDrivenItems(player, "chain", 4);
|
||||
given += giveDataDrivenItems(player, "leather_straps", 4);
|
||||
|
||||
// Gags
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.getGag(GagVariant.CLOTH_GAG), 4)
|
||||
);
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.getGag(GagVariant.BALL_GAG), 4)
|
||||
);
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.getGag(GagVariant.TAPE_GAG), 4)
|
||||
);
|
||||
given += giveDataDrivenItems(player, "cloth_gag", 4);
|
||||
given += giveDataDrivenItems(player, "ball_gag", 4);
|
||||
given += giveDataDrivenItems(player, "tape_gag", 4);
|
||||
|
||||
// Blindfolds
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.getBlindfold(BlindfoldVariant.CLASSIC), 4)
|
||||
);
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.getBlindfold(BlindfoldVariant.MASK), 2)
|
||||
);
|
||||
given += giveDataDrivenItems(player, "classic_blindfold", 4);
|
||||
given += giveDataDrivenItems(player, "blindfold_mask", 2);
|
||||
|
||||
// Collars
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.CLASSIC_COLLAR.get(), 4)
|
||||
);
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.SHOCK_COLLAR.get(), 2)
|
||||
);
|
||||
given += giveItem(player, new ItemStack(ModItems.GPS_COLLAR.get(), 2));
|
||||
// Collars (data-driven)
|
||||
ItemStack classicCollars = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(
|
||||
new net.minecraft.resources.ResourceLocation("tiedup", "classic_collar"));
|
||||
classicCollars.setCount(4);
|
||||
given += giveItem(player, classicCollars);
|
||||
ItemStack shockCollars = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(
|
||||
new net.minecraft.resources.ResourceLocation("tiedup", "shock_collar"));
|
||||
shockCollars.setCount(2);
|
||||
given += giveItem(player, shockCollars);
|
||||
ItemStack gpsCollars = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(
|
||||
new net.minecraft.resources.ResourceLocation("tiedup", "gps_collar"));
|
||||
gpsCollars.setCount(2);
|
||||
given += giveItem(player, gpsCollars);
|
||||
|
||||
// Tools
|
||||
given += giveItem(
|
||||
@@ -155,10 +132,7 @@ public class KidnapSetCommand {
|
||||
given += giveItem(player, new ItemStack(ModItems.MASTER_KEY.get(), 1));
|
||||
|
||||
// Earplugs
|
||||
given += giveItem(
|
||||
player,
|
||||
new ItemStack(ModItems.getEarplugs(EarplugsVariant.CLASSIC), 4)
|
||||
);
|
||||
given += giveDataDrivenItems(player, "classic_earplugs", 4);
|
||||
|
||||
// Rope arrows
|
||||
given += giveItem(player, new ItemStack(ModItems.ROPE_ARROW.get(), 16));
|
||||
@@ -182,6 +156,18 @@ public class KidnapSetCommand {
|
||||
return finalGiven;
|
||||
}
|
||||
|
||||
private static int giveDataDrivenItems(ServerPlayer player, String itemName, int count) {
|
||||
int given = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
ItemStack stack = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", itemName));
|
||||
if (!stack.isEmpty()) {
|
||||
giveItem(player, stack);
|
||||
given++;
|
||||
}
|
||||
}
|
||||
return given;
|
||||
}
|
||||
|
||||
private static int giveItem(ServerPlayer player, ItemStack stack) {
|
||||
if (!player.getInventory().add(stack)) {
|
||||
// Drop on ground if inventory full
|
||||
|
||||
@@ -7,11 +7,9 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.tiedup.remake.entities.*;
|
||||
import com.tiedup.remake.entities.skins.EliteKidnapperSkinManager;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.items.base.BlindfoldVariant;
|
||||
import com.tiedup.remake.items.base.EarplugsVariant;
|
||||
import com.tiedup.remake.items.base.GagVariant;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -507,7 +505,7 @@ public class NPCCommand {
|
||||
|
||||
npc.equip(
|
||||
BodyRegionV2.ARMS,
|
||||
new ItemStack(ModItems.getBind(BindVariant.ROPES))
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes"))
|
||||
);
|
||||
context
|
||||
.getSource()
|
||||
@@ -538,7 +536,7 @@ public class NPCCommand {
|
||||
|
||||
npc.equip(
|
||||
BodyRegionV2.MOUTH,
|
||||
new ItemStack(ModItems.getGag(GagVariant.CLOTH_GAG))
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag"))
|
||||
);
|
||||
context
|
||||
.getSource()
|
||||
@@ -571,7 +569,7 @@ public class NPCCommand {
|
||||
|
||||
npc.equip(
|
||||
BodyRegionV2.EYES,
|
||||
new ItemStack(ModItems.getBlindfold(BlindfoldVariant.CLASSIC))
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold"))
|
||||
);
|
||||
context
|
||||
.getSource()
|
||||
@@ -605,7 +603,7 @@ public class NPCCommand {
|
||||
|
||||
npc.equip(
|
||||
BodyRegionV2.NECK,
|
||||
new ItemStack(ModItems.CLASSIC_COLLAR.get())
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_collar"))
|
||||
);
|
||||
context
|
||||
.getSource()
|
||||
@@ -656,11 +654,11 @@ public class NPCCommand {
|
||||
com.tiedup.remake.entities.AbstractTiedUpNpc npcEntity
|
||||
) {
|
||||
npcEntity.applyBondage(
|
||||
new ItemStack(ModItems.getBind(BindVariant.ROPES)),
|
||||
new ItemStack(ModItems.getGag(GagVariant.CLOTH_GAG)),
|
||||
new ItemStack(ModItems.getBlindfold(BlindfoldVariant.CLASSIC)),
|
||||
new ItemStack(ModItems.getEarplugs(EarplugsVariant.CLASSIC)),
|
||||
new ItemStack(ModItems.CLASSIC_COLLAR.get()),
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes")),
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag")),
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold")),
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_earplugs")),
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_collar")),
|
||||
ItemStack.EMPTY // No clothes
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,12 +9,10 @@ import com.tiedup.remake.commands.CommandHelper;
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.AdjustmentHelper;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.items.base.BlindfoldVariant;
|
||||
import com.tiedup.remake.items.base.EarplugsVariant;
|
||||
import com.tiedup.remake.items.base.GagVariant;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.network.sync.PacketSyncBindState;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
@@ -256,7 +254,7 @@ public class BondageSubCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ItemStack ropes = new ItemStack(ModItems.getBind(BindVariant.ROPES));
|
||||
ItemStack ropes = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes"));
|
||||
state.putBindOn(ropes);
|
||||
|
||||
CommandHelper.syncPlayerState(targetPlayer, state);
|
||||
@@ -387,7 +385,7 @@ public class BondageSubCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ItemStack gag = new ItemStack(ModItems.getGag(GagVariant.CLOTH_GAG));
|
||||
ItemStack gag = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag"));
|
||||
state.putGagOn(gag);
|
||||
|
||||
CommandHelper.syncPlayerState(targetPlayer, state);
|
||||
@@ -440,9 +438,7 @@ public class BondageSubCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ItemStack blindfold = new ItemStack(
|
||||
ModItems.getBlindfold(BlindfoldVariant.CLASSIC)
|
||||
);
|
||||
ItemStack blindfold = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold"));
|
||||
state.putBlindfoldOn(blindfold);
|
||||
|
||||
CommandHelper.syncPlayerState(targetPlayer, state);
|
||||
@@ -497,11 +493,10 @@ public class BondageSubCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ItemStack collar = new ItemStack(ModItems.CLASSIC_COLLAR.get());
|
||||
ItemStack collar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(new net.minecraft.resources.ResourceLocation("tiedup", "classic_collar"));
|
||||
|
||||
if (context.getSource().getEntity() instanceof ServerPlayer executor) {
|
||||
ItemCollar collarItem = (ItemCollar) collar.getItem();
|
||||
collarItem.addOwner(collar, executor);
|
||||
CollarHelper.addOwner(collar, executor);
|
||||
}
|
||||
|
||||
state.putCollarOn(collar);
|
||||
@@ -1021,9 +1016,7 @@ public class BondageSubCommand {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ItemStack earplugs = new ItemStack(
|
||||
ModItems.getEarplugs(EarplugsVariant.CLASSIC)
|
||||
);
|
||||
ItemStack earplugs = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_earplugs"));
|
||||
state.putEarplugsOn(earplugs);
|
||||
CommandHelper.syncPlayerState(targetPlayer, state);
|
||||
|
||||
@@ -1067,45 +1060,36 @@ public class BondageSubCommand {
|
||||
int applied = 0;
|
||||
|
||||
if (!state.isTiedUp()) {
|
||||
ItemStack ropes = new ItemStack(
|
||||
ModItems.getBind(BindVariant.ROPES)
|
||||
);
|
||||
ItemStack ropes = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes"));
|
||||
state.putBindOn(ropes);
|
||||
applied++;
|
||||
}
|
||||
|
||||
if (!state.isGagged()) {
|
||||
ItemStack gag = new ItemStack(
|
||||
ModItems.getGag(GagVariant.CLOTH_GAG)
|
||||
);
|
||||
ItemStack gag = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag"));
|
||||
state.putGagOn(gag);
|
||||
applied++;
|
||||
}
|
||||
|
||||
if (!state.isBlindfolded()) {
|
||||
ItemStack blindfold = new ItemStack(
|
||||
ModItems.getBlindfold(BlindfoldVariant.CLASSIC)
|
||||
);
|
||||
ItemStack blindfold = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold"));
|
||||
state.putBlindfoldOn(blindfold);
|
||||
applied++;
|
||||
}
|
||||
|
||||
if (!state.hasCollar()) {
|
||||
ItemStack collar = new ItemStack(ModItems.CLASSIC_COLLAR.get());
|
||||
ItemStack collar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(new net.minecraft.resources.ResourceLocation("tiedup", "classic_collar"));
|
||||
if (
|
||||
context.getSource().getEntity() instanceof ServerPlayer executor
|
||||
) {
|
||||
ItemCollar collarItem = (ItemCollar) collar.getItem();
|
||||
collarItem.addOwner(collar, executor);
|
||||
CollarHelper.addOwner(collar, executor);
|
||||
}
|
||||
state.putCollarOn(collar);
|
||||
applied++;
|
||||
}
|
||||
|
||||
if (!state.hasEarplugs()) {
|
||||
ItemStack earplugs = new ItemStack(
|
||||
ModItems.getEarplugs(EarplugsVariant.CLASSIC)
|
||||
);
|
||||
ItemStack earplugs = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_earplugs"));
|
||||
state.putEarplugsOn(earplugs);
|
||||
applied++;
|
||||
}
|
||||
@@ -1167,37 +1151,28 @@ public class BondageSubCommand {
|
||||
|
||||
// First fully restrain
|
||||
if (!state.isTiedUp()) {
|
||||
ItemStack ropes = new ItemStack(
|
||||
ModItems.getBind(BindVariant.ROPES)
|
||||
);
|
||||
ItemStack ropes = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes"));
|
||||
state.putBindOn(ropes);
|
||||
}
|
||||
if (!state.isGagged()) {
|
||||
ItemStack gag = new ItemStack(
|
||||
ModItems.getGag(GagVariant.CLOTH_GAG)
|
||||
);
|
||||
ItemStack gag = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag"));
|
||||
state.putGagOn(gag);
|
||||
}
|
||||
if (!state.isBlindfolded()) {
|
||||
ItemStack blindfold = new ItemStack(
|
||||
ModItems.getBlindfold(BlindfoldVariant.CLASSIC)
|
||||
);
|
||||
ItemStack blindfold = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold"));
|
||||
state.putBlindfoldOn(blindfold);
|
||||
}
|
||||
if (!state.hasCollar()) {
|
||||
ItemStack collar = new ItemStack(ModItems.CLASSIC_COLLAR.get());
|
||||
ItemStack collar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(new net.minecraft.resources.ResourceLocation("tiedup", "classic_collar"));
|
||||
if (
|
||||
context.getSource().getEntity() instanceof ServerPlayer executor
|
||||
) {
|
||||
ItemCollar collarItem = (ItemCollar) collar.getItem();
|
||||
collarItem.addOwner(collar, executor);
|
||||
CollarHelper.addOwner(collar, executor);
|
||||
}
|
||||
state.putCollarOn(collar);
|
||||
}
|
||||
if (!state.hasEarplugs()) {
|
||||
ItemStack earplugs = new ItemStack(
|
||||
ModItems.getEarplugs(EarplugsVariant.CLASSIC)
|
||||
);
|
||||
ItemStack earplugs = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_earplugs"));
|
||||
state.putEarplugsOn(earplugs);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@ package com.tiedup.remake.compat.mca.capability;
|
||||
|
||||
import com.tiedup.remake.compat.mca.MCABondageManager;
|
||||
import com.tiedup.remake.compat.mca.MCACompat;
|
||||
import com.tiedup.remake.items.base.IHasBlindingEffect;
|
||||
import com.tiedup.remake.items.base.IHasGaggingEffect;
|
||||
import com.tiedup.remake.v2.bondage.component.BlindingComponent;
|
||||
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import com.tiedup.remake.items.base.IHasResistance;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.state.ICaptor;
|
||||
import com.tiedup.remake.state.IRestrainable;
|
||||
import com.tiedup.remake.state.IRestrainableEntity;
|
||||
@@ -277,16 +278,15 @@ public class MCAKidnappedAdapter implements IRestrainable {
|
||||
@Override
|
||||
public boolean hasGaggingEffect() {
|
||||
ItemStack gag = cap.getGag();
|
||||
return !gag.isEmpty() && gag.getItem() instanceof IHasGaggingEffect;
|
||||
if (gag.isEmpty()) return false;
|
||||
return DataDrivenBondageItem.getComponent(gag, ComponentType.GAGGING, GaggingComponent.class) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBlindingEffect() {
|
||||
ItemStack blindfold = cap.getBlindfold();
|
||||
return (
|
||||
!blindfold.isEmpty() &&
|
||||
blindfold.getItem() instanceof IHasBlindingEffect
|
||||
);
|
||||
if (blindfold.isEmpty()) return false;
|
||||
return DataDrivenBondageItem.getComponent(blindfold, ComponentType.BLINDING, BlindingComponent.class) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -160,7 +160,7 @@ public class SettingsAccessor {
|
||||
* <p><b>BUG-003 fix:</b> Previously, {@code IHasResistance.getBaseResistance()}
|
||||
* called {@code ModGameRules.getResistance()} which only knew 4 types (rope, gag,
|
||||
* blindfold, collar) and returned hardcoded 100 for the other 10 types. Meanwhile
|
||||
* {@code BindVariant.getResistance()} read from ModConfig which had all 14 types.
|
||||
* the old BindVariant.getResistance() read from ModConfig which had all 14 types.
|
||||
* This caused a display-vs-struggle desync (display: 250, struggle: 100).
|
||||
* Now both paths use this method.
|
||||
*
|
||||
@@ -207,8 +207,7 @@ public class SettingsAccessor {
|
||||
/**
|
||||
* Normalize a raw bind item name to its config key.
|
||||
*
|
||||
* <p>Replicates the mapping from
|
||||
* {@link com.tiedup.remake.items.base.BindVariant#getResistance()} so that
|
||||
* <p>Normalizes raw item names to config keys so that
|
||||
* every call site resolves to the same config entry.
|
||||
*
|
||||
* @param bindType Raw item name (e.g., "ropes", "vine_seed", "collar")
|
||||
|
||||
@@ -3,8 +3,10 @@ package com.tiedup.remake.dialogue;
|
||||
import static com.tiedup.remake.util.GameConstants.*;
|
||||
|
||||
import com.tiedup.remake.dialogue.EmotionalContext.EmotionType;
|
||||
import com.tiedup.remake.items.base.ItemGag;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import com.tiedup.remake.util.GagMaterial;
|
||||
import com.tiedup.remake.util.PhoneticMapper;
|
||||
import com.tiedup.remake.util.SyllableAnalyzer;
|
||||
@@ -58,8 +60,11 @@ public class GagTalkManager {
|
||||
) {
|
||||
LivingEntity entity = kidnapped.asLivingEntity();
|
||||
GagMaterial material = GagMaterial.CLOTH;
|
||||
if (gagStack.getItem() instanceof ItemGag gag) {
|
||||
material = gag.getGagMaterial();
|
||||
// V2: check data-driven GaggingComponent first
|
||||
GaggingComponent gaggingComp = DataDrivenBondageItem.getComponent(
|
||||
gagStack, ComponentType.GAGGING, GaggingComponent.class);
|
||||
if (gaggingComp != null && gaggingComp.getMaterial() != null) {
|
||||
material = gaggingComp.getMaterial();
|
||||
}
|
||||
|
||||
// 1. EFFET DE SUFFOCATION (Si message trop long)
|
||||
@@ -514,8 +519,13 @@ public class GagTalkManager {
|
||||
}
|
||||
|
||||
GagMaterial material = GagMaterial.CLOTH;
|
||||
if (gagStack != null && gagStack.getItem() instanceof ItemGag gag) {
|
||||
material = gag.getGagMaterial();
|
||||
if (gagStack != null && !gagStack.isEmpty()) {
|
||||
// V2: check data-driven GaggingComponent first
|
||||
GaggingComponent comp = DataDrivenBondageItem.getComponent(
|
||||
gagStack, ComponentType.GAGGING, GaggingComponent.class);
|
||||
if (comp != null && comp.getMaterial() != null) {
|
||||
material = comp.getMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder muffled = new StringBuilder();
|
||||
|
||||
@@ -5,9 +5,9 @@ import com.tiedup.remake.dialogue.DialogueBridge;
|
||||
import com.tiedup.remake.entities.EntityMaster;
|
||||
import com.tiedup.remake.entities.ai.master.MasterPlaceBlockGoal;
|
||||
import com.tiedup.remake.entities.ai.master.MasterState;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.network.master.PacketOpenPetRequestMenu;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -171,9 +171,7 @@ public class PetRequestManager {
|
||||
|
||||
// Put dogbind on player (if not already tied)
|
||||
if (!state.isTiedUp()) {
|
||||
ItemStack dogbind = new ItemStack(
|
||||
ModItems.getBind(BindVariant.DOGBINDER)
|
||||
);
|
||||
ItemStack dogbind = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "dogbinder"));
|
||||
state.equip(BodyRegionV2.ARMS, dogbind);
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PetRequestManager] Equipped dogbind on {} for walk",
|
||||
@@ -228,7 +226,7 @@ public class PetRequestManager {
|
||||
}
|
||||
|
||||
// Master equips armbinder on pet (classic pet play restraint)
|
||||
ItemStack bind = new ItemStack(ModItems.getBind(BindVariant.ARMBINDER));
|
||||
ItemStack bind = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "armbinder"));
|
||||
state.equip(BodyRegionV2.ARMS, bind);
|
||||
|
||||
DialogueBridge.talkTo(master, pet, "petplay.tie_accept");
|
||||
|
||||
@@ -2,16 +2,19 @@ package com.tiedup.remake.dispenser;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.*;
|
||||
import net.minecraft.world.level.block.DispenserBlock;
|
||||
|
||||
/**
|
||||
* Registration class for all TiedUp dispenser behaviors.
|
||||
*
|
||||
* Allows dispensers to:
|
||||
* - Equip bondage items (binds, gags, blindfolds, collars, earplugs, clothes) on entities
|
||||
* - Equip bondage items (via data-driven V2 system) on entities
|
||||
* - Shoot rope arrows
|
||||
*
|
||||
* Note: V1 per-variant dispenser registrations have been removed.
|
||||
* Data-driven bondage items use a single universal dispenser behavior
|
||||
* registered via DataDrivenBondageItem system.
|
||||
*
|
||||
* Based on original behaviors package from 1.12.2
|
||||
*/
|
||||
public class DispenserBehaviors {
|
||||
@@ -25,72 +28,15 @@ public class DispenserBehaviors {
|
||||
"[DispenserBehaviors] Registering dispenser behaviors..."
|
||||
);
|
||||
|
||||
registerBindBehaviors();
|
||||
registerGagBehaviors();
|
||||
registerBlindfoldBehaviors();
|
||||
registerCollarBehaviors();
|
||||
registerEarplugsBehaviors();
|
||||
registerClothesBehaviors();
|
||||
registerRopeArrowBehavior();
|
||||
registerBondageItemBehavior();
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[DispenserBehaviors] Dispenser behaviors registered!"
|
||||
);
|
||||
}
|
||||
|
||||
private static void registerBindBehaviors() {
|
||||
var behavior = GenericBondageDispenseBehavior.forBind();
|
||||
for (BindVariant variant : BindVariant.values()) {
|
||||
DispenserBlock.registerBehavior(
|
||||
ModItems.getBind(variant),
|
||||
behavior
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerGagBehaviors() {
|
||||
var behavior = GenericBondageDispenseBehavior.forGag();
|
||||
for (GagVariant variant : GagVariant.values()) {
|
||||
DispenserBlock.registerBehavior(ModItems.getGag(variant), behavior);
|
||||
}
|
||||
DispenserBlock.registerBehavior(ModItems.MEDICAL_GAG.get(), behavior);
|
||||
DispenserBlock.registerBehavior(ModItems.HOOD.get(), behavior);
|
||||
}
|
||||
|
||||
private static void registerBlindfoldBehaviors() {
|
||||
var behavior = GenericBondageDispenseBehavior.forBlindfold();
|
||||
for (BlindfoldVariant variant : BlindfoldVariant.values()) {
|
||||
DispenserBlock.registerBehavior(
|
||||
ModItems.getBlindfold(variant),
|
||||
behavior
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerCollarBehaviors() {
|
||||
var behavior = GenericBondageDispenseBehavior.forCollar();
|
||||
DispenserBlock.registerBehavior(
|
||||
ModItems.CLASSIC_COLLAR.get(),
|
||||
behavior
|
||||
);
|
||||
DispenserBlock.registerBehavior(ModItems.SHOCK_COLLAR.get(), behavior);
|
||||
DispenserBlock.registerBehavior(
|
||||
ModItems.SHOCK_COLLAR_AUTO.get(),
|
||||
behavior
|
||||
);
|
||||
DispenserBlock.registerBehavior(ModItems.GPS_COLLAR.get(), behavior);
|
||||
}
|
||||
|
||||
private static void registerEarplugsBehaviors() {
|
||||
var behavior = GenericBondageDispenseBehavior.forEarplugs();
|
||||
for (EarplugsVariant variant : EarplugsVariant.values()) {
|
||||
DispenserBlock.registerBehavior(
|
||||
ModItems.getEarplugs(variant),
|
||||
behavior
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerClothesBehaviors() {
|
||||
DispenserBlock.registerBehavior(
|
||||
ModItems.CLOTHES.get(),
|
||||
@@ -98,6 +44,17 @@ public class DispenserBehaviors {
|
||||
);
|
||||
}
|
||||
|
||||
private static void registerBondageItemBehavior() {
|
||||
// Single registration for the V2 data-driven item singleton.
|
||||
// GenericBondageDispenseBehavior inspects the stack's definition to determine behavior.
|
||||
if (com.tiedup.remake.v2.bondage.V2BondageItems.DATA_DRIVEN_ITEM != null) {
|
||||
DispenserBlock.registerBehavior(
|
||||
com.tiedup.remake.v2.bondage.V2BondageItems.DATA_DRIVEN_ITEM.get(),
|
||||
GenericBondageDispenseBehavior.forAnyDataDriven()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerRopeArrowBehavior() {
|
||||
DispenserBlock.registerBehavior(
|
||||
ModItems.ROPE_ARROW.get(),
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
package com.tiedup.remake.dispenser;
|
||||
|
||||
import com.tiedup.remake.items.base.*;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Generic dispenser behavior for equipping bondage items.
|
||||
* Replaces individual BindDispenseBehavior, GagDispenseBehavior, etc.
|
||||
* Uses V2 data-driven item detection instead of V1 class checks.
|
||||
*
|
||||
* Use factory methods to create instances for each bondage type.
|
||||
*/
|
||||
@@ -18,23 +23,23 @@ public class GenericBondageDispenseBehavior
|
||||
extends EquipBondageDispenseBehavior
|
||||
{
|
||||
|
||||
private final Class<? extends Item> itemClass;
|
||||
private final Predicate<ItemStack> itemCheck;
|
||||
private final Predicate<IBondageState> canEquipCheck;
|
||||
private final BiConsumer<IBondageState, ItemStack> equipAction;
|
||||
|
||||
private GenericBondageDispenseBehavior(
|
||||
Class<? extends Item> itemClass,
|
||||
Predicate<ItemStack> itemCheck,
|
||||
Predicate<IBondageState> canEquipCheck,
|
||||
BiConsumer<IBondageState, ItemStack> equipAction
|
||||
) {
|
||||
this.itemClass = itemClass;
|
||||
this.itemCheck = itemCheck;
|
||||
this.canEquipCheck = canEquipCheck;
|
||||
this.equipAction = equipAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isValidItem(ItemStack stack) {
|
||||
return !stack.isEmpty() && itemClass.isInstance(stack.getItem());
|
||||
return !stack.isEmpty() && itemCheck.test(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -51,9 +56,35 @@ public class GenericBondageDispenseBehavior
|
||||
|
||||
// Factory Methods
|
||||
|
||||
/** Universal behavior for the V2 data-driven item singleton. Dispatches by region. */
|
||||
public static GenericBondageDispenseBehavior forAnyDataDriven() {
|
||||
return new GenericBondageDispenseBehavior(
|
||||
stack -> DataDrivenItemRegistry.get(stack) != null,
|
||||
state -> true, // let equip() handle the check
|
||||
(state, stack) -> {
|
||||
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
||||
if (def == null) return;
|
||||
java.util.Set<BodyRegionV2> regions = def.occupiedRegions();
|
||||
if (regions.contains(BodyRegionV2.ARMS) && !state.isTiedUp()) {
|
||||
state.equip(BodyRegionV2.ARMS, stack);
|
||||
} else if (regions.contains(BodyRegionV2.MOUTH) && !state.isGagged()) {
|
||||
state.equip(BodyRegionV2.MOUTH, stack);
|
||||
} else if (regions.contains(BodyRegionV2.EYES) && !state.isBlindfolded()) {
|
||||
state.equip(BodyRegionV2.EYES, stack);
|
||||
} else if (regions.contains(BodyRegionV2.NECK) && !state.hasCollar()) {
|
||||
state.equip(BodyRegionV2.NECK, stack);
|
||||
} else if (regions.contains(BodyRegionV2.EARS) && !state.hasEarplugs()) {
|
||||
state.equip(BodyRegionV2.EARS, stack);
|
||||
} else if (regions.contains(BodyRegionV2.HANDS) && !state.hasMittens()) {
|
||||
state.equip(BodyRegionV2.HANDS, stack);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static GenericBondageDispenseBehavior forBind() {
|
||||
return new GenericBondageDispenseBehavior(
|
||||
ItemBind.class,
|
||||
BindModeHelper::isBindItem,
|
||||
state -> !state.isTiedUp(),
|
||||
(s, i) -> s.equip(BodyRegionV2.ARMS, i)
|
||||
);
|
||||
@@ -61,7 +92,7 @@ public class GenericBondageDispenseBehavior
|
||||
|
||||
public static GenericBondageDispenseBehavior forGag() {
|
||||
return new GenericBondageDispenseBehavior(
|
||||
ItemGag.class,
|
||||
stack -> DataDrivenBondageItem.getComponent(stack, ComponentType.GAGGING, GaggingComponent.class) != null,
|
||||
state -> !state.isGagged(),
|
||||
(s, i) -> s.equip(BodyRegionV2.MOUTH, i)
|
||||
);
|
||||
@@ -69,7 +100,10 @@ public class GenericBondageDispenseBehavior
|
||||
|
||||
public static GenericBondageDispenseBehavior forBlindfold() {
|
||||
return new GenericBondageDispenseBehavior(
|
||||
ItemBlindfold.class,
|
||||
stack -> {
|
||||
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
||||
return def != null && def.occupiedRegions().contains(BodyRegionV2.EYES);
|
||||
},
|
||||
state -> !state.isBlindfolded(),
|
||||
(s, i) -> s.equip(BodyRegionV2.EYES, i)
|
||||
);
|
||||
@@ -77,7 +111,7 @@ public class GenericBondageDispenseBehavior
|
||||
|
||||
public static GenericBondageDispenseBehavior forCollar() {
|
||||
return new GenericBondageDispenseBehavior(
|
||||
ItemCollar.class,
|
||||
CollarHelper::isCollar,
|
||||
state -> !state.hasCollar(),
|
||||
(s, i) -> s.equip(BodyRegionV2.NECK, i)
|
||||
);
|
||||
@@ -85,7 +119,10 @@ public class GenericBondageDispenseBehavior
|
||||
|
||||
public static GenericBondageDispenseBehavior forEarplugs() {
|
||||
return new GenericBondageDispenseBehavior(
|
||||
ItemEarplugs.class,
|
||||
stack -> {
|
||||
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
||||
return def != null && def.occupiedRegions().contains(BodyRegionV2.EARS);
|
||||
},
|
||||
state -> !state.hasEarplugs(),
|
||||
(s, i) -> s.equip(BodyRegionV2.EARS, i)
|
||||
);
|
||||
|
||||
@@ -4,7 +4,12 @@ import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.damsel.components.*;
|
||||
import com.tiedup.remake.entities.skins.Gender;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
||||
import com.tiedup.remake.v2.bondage.component.BlindingComponent;
|
||||
import com.tiedup.remake.state.ICaptor;
|
||||
import com.tiedup.remake.state.IRestrainable;
|
||||
import com.tiedup.remake.state.IRestrainableEntity;
|
||||
@@ -455,16 +460,8 @@ public abstract class AbstractTiedUpNpc
|
||||
*/
|
||||
public boolean isDogPose() {
|
||||
ItemStack bind = this.getEquipment(BodyRegionV2.ARMS);
|
||||
if (
|
||||
bind.getItem() instanceof
|
||||
com.tiedup.remake.items.base.ItemBind itemBind
|
||||
) {
|
||||
return (
|
||||
itemBind.getPoseType() ==
|
||||
com.tiedup.remake.items.base.PoseType.DOG
|
||||
);
|
||||
}
|
||||
return false;
|
||||
if (bind.isEmpty()) return false;
|
||||
return PoseTypeHelper.getPoseType(bind) == com.tiedup.remake.items.base.PoseType.DOG;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -679,10 +676,8 @@ public abstract class AbstractTiedUpNpc
|
||||
// Exception: collar owner can leash even if not tied
|
||||
if (this.hasCollar()) {
|
||||
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (collarItem.getOwners(collar).contains(player.getUUID())) {
|
||||
return true;
|
||||
}
|
||||
if (CollarHelper.isOwner(collar, player)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,20 +796,14 @@ public abstract class AbstractTiedUpNpc
|
||||
public boolean hasGaggingEffect() {
|
||||
ItemStack gag = this.getEquipment(BodyRegionV2.MOUTH);
|
||||
if (gag.isEmpty()) return false;
|
||||
return (
|
||||
gag.getItem() instanceof
|
||||
com.tiedup.remake.items.base.IHasGaggingEffect
|
||||
);
|
||||
return DataDrivenBondageItem.getComponent(gag, ComponentType.GAGGING, GaggingComponent.class) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBlindingEffect() {
|
||||
ItemStack blindfold = this.getEquipment(BodyRegionV2.EYES);
|
||||
if (blindfold.isEmpty()) return false;
|
||||
return (
|
||||
blindfold.getItem() instanceof
|
||||
com.tiedup.remake.items.base.IHasBlindingEffect
|
||||
);
|
||||
return DataDrivenBondageItem.getComponent(blindfold, ComponentType.BLINDING, BlindingComponent.class) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -990,9 +979,9 @@ public abstract class AbstractTiedUpNpc
|
||||
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.isEmpty()) return false;
|
||||
|
||||
if (!(collar.getItem() instanceof ItemCollar itemCollar)) return false;
|
||||
if (!CollarHelper.isCollar(collar)) return false;
|
||||
|
||||
java.util.UUID cellId = itemCollar.getCellId(collar);
|
||||
java.util.UUID cellId = CollarHelper.getCellId(collar);
|
||||
if (cellId == null) return false;
|
||||
|
||||
// Get cell position from registry
|
||||
@@ -1096,9 +1085,7 @@ public abstract class AbstractTiedUpNpc
|
||||
public boolean hasShockCollar() {
|
||||
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.isEmpty()) return false;
|
||||
return (
|
||||
collar.getItem() instanceof com.tiedup.remake.items.ItemShockCollar
|
||||
);
|
||||
return com.tiedup.remake.v2.bondage.CollarHelper.canShock(collar);
|
||||
}
|
||||
|
||||
// BONDAGE SERVICE (delegated to BondageManager)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package com.tiedup.remake.entities;
|
||||
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.items.base.GagVariant;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.util.MessageDispatcher;
|
||||
import com.tiedup.remake.util.teleport.Position;
|
||||
@@ -53,13 +52,10 @@ public class BondageServiceHandler {
|
||||
if (!npc.hasCollar()) return false;
|
||||
|
||||
ItemStack collar = npc.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar itemCollar) {
|
||||
return (
|
||||
itemCollar.hasCellAssigned(collar) &&
|
||||
itemCollar.isBondageServiceEnabled(collar)
|
||||
);
|
||||
}
|
||||
return false;
|
||||
return (
|
||||
CollarHelper.hasCellAssigned(collar) &&
|
||||
CollarHelper.isBondageServiceEnabled(collar)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,11 +66,9 @@ public class BondageServiceHandler {
|
||||
public String getMessage() {
|
||||
if (npc.hasCollar()) {
|
||||
ItemStack collar = npc.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar itemCollar) {
|
||||
String message = itemCollar.getServiceSentence(collar);
|
||||
if (message != null && !message.isEmpty()) {
|
||||
return message;
|
||||
}
|
||||
String message = CollarHelper.getServiceSentence(collar);
|
||||
if (message != null && !message.isEmpty()) {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
return DEFAULT_MESSAGE;
|
||||
@@ -119,9 +113,9 @@ public class BondageServiceHandler {
|
||||
*/
|
||||
private void capturePlayer(Player player) {
|
||||
ItemStack collar = npc.getEquipment(BodyRegionV2.NECK);
|
||||
if (!(collar.getItem() instanceof ItemCollar itemCollar)) return;
|
||||
if (!CollarHelper.isCollar(collar)) return;
|
||||
|
||||
java.util.UUID cellId = itemCollar.getCellId(collar);
|
||||
java.util.UUID cellId = CollarHelper.getCellId(collar);
|
||||
if (cellId == null) return;
|
||||
|
||||
// Get cell position from registry
|
||||
@@ -141,7 +135,7 @@ public class BondageServiceHandler {
|
||||
);
|
||||
|
||||
// Warn masters if configured
|
||||
warnOwners(player, itemCollar, collar);
|
||||
warnOwners(player, collar);
|
||||
|
||||
// Get player's kidnapped state
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
@@ -149,18 +143,18 @@ public class BondageServiceHandler {
|
||||
// Apply bondage
|
||||
state.equip(
|
||||
BodyRegionV2.ARMS,
|
||||
new ItemStack(ModItems.getBind(BindVariant.ROPES))
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes"))
|
||||
);
|
||||
state.equip(
|
||||
BodyRegionV2.MOUTH,
|
||||
new ItemStack(ModItems.getGag(GagVariant.BALL_GAG))
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ball_gag"))
|
||||
);
|
||||
|
||||
// Teleport to cell
|
||||
state.teleportToPosition(cellPosition);
|
||||
|
||||
// Tie to pole if configured on collar
|
||||
if (itemCollar.shouldTieToPole(collar)) {
|
||||
if (CollarHelper.shouldTieToPole(collar)) {
|
||||
state.tieToClosestPole(3);
|
||||
}
|
||||
}
|
||||
@@ -178,10 +172,9 @@ public class BondageServiceHandler {
|
||||
*/
|
||||
private void warnOwners(
|
||||
Player capturedPlayer,
|
||||
ItemCollar itemCollar,
|
||||
ItemStack collarStack
|
||||
) {
|
||||
if (!itemCollar.shouldWarnMasters(collarStack)) {
|
||||
if (!CollarHelper.shouldWarnMasters(collarStack)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -191,7 +184,7 @@ public class BondageServiceHandler {
|
||||
capturedPlayer.getName().getString() +
|
||||
" via bondage service!";
|
||||
|
||||
for (UUID ownerUUID : itemCollar.getOwners(collarStack)) {
|
||||
for (UUID ownerUUID : CollarHelper.getOwners(collarStack)) {
|
||||
Player owner = npc.level().getPlayerByUUID(ownerUUID);
|
||||
if (owner != null) {
|
||||
SystemMessageManager.sendChatToPlayer(
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.damsel.components.*;
|
||||
import com.tiedup.remake.entities.skins.DamselSkinManager;
|
||||
import com.tiedup.remake.entities.skins.Gender;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.state.ICaptor;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import java.util.UUID;
|
||||
@@ -527,8 +527,8 @@ public class EntityDamsel
|
||||
if (!this.hasCollar()) return false;
|
||||
|
||||
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
||||
if (!(collar.getItem() instanceof ItemCollar collarItem)) return false;
|
||||
if (!collarItem.getOwners(collar).contains(commander.getUUID())) {
|
||||
if (!CollarHelper.isCollar(collar)) return false;
|
||||
if (!CollarHelper.isOwner(collar, commander.getUUID())) {
|
||||
if (!this.isGagged()) {
|
||||
com.tiedup.remake.dialogue.EntityDialogueManager.talkByDialogueId(
|
||||
this,
|
||||
@@ -653,8 +653,8 @@ public class EntityDamsel
|
||||
return net.minecraft.world.InteractionResult.FAIL;
|
||||
}
|
||||
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (!collarItem.isOwner(collar, player)) {
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
if (!CollarHelper.isOwner(collar, player)) {
|
||||
if (
|
||||
player instanceof
|
||||
net.minecraft.server.level.ServerPlayer sp
|
||||
@@ -693,9 +693,9 @@ public class EntityDamsel
|
||||
this.hasCollar()
|
||||
) {
|
||||
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
if (
|
||||
collarItem.isOwner(collar, player) &&
|
||||
CollarHelper.isOwner(collar, player) &&
|
||||
player instanceof
|
||||
net.minecraft.server.level.ServerPlayer serverPlayer
|
||||
) {
|
||||
@@ -822,8 +822,8 @@ public class EntityDamsel
|
||||
public String getTargetRelation(Player player) {
|
||||
if (hasCollar()) {
|
||||
ItemStack collar = getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (collarItem.isOwner(collar, player)) {
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
if (CollarHelper.isOwner(collar, player)) {
|
||||
return "master";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import com.tiedup.remake.entities.kidnapper.components.KidnapperAggressionSystem
|
||||
import com.tiedup.remake.entities.skins.Gender;
|
||||
import com.tiedup.remake.entities.skins.KidnapperSkinManager;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.personality.PersonalityType;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.state.ICaptor;
|
||||
@@ -1367,10 +1367,7 @@ public class EntityKidnapper
|
||||
if (!this.hasCollar()) return false;
|
||||
|
||||
ItemStack collar = this.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
return collarItem.isOwner(collar, player);
|
||||
}
|
||||
return false;
|
||||
return CollarHelper.isOwner(collar, player);
|
||||
}
|
||||
|
||||
/** Damage reduction multiplier against monsters (50% damage taken) */
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.state.ICaptor;
|
||||
import com.tiedup.remake.util.MessageDispatcher;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -615,78 +616,10 @@ public class EntityKidnapperMerchant extends EntityKidnapperElite {
|
||||
private List<ItemStack> collectAllModItems() {
|
||||
List<ItemStack> items = new ArrayList<>();
|
||||
|
||||
// All binds (15)
|
||||
// Items with colors get multiple variants (one per color)
|
||||
for (BindVariant variant : BindVariant.values()) {
|
||||
if (variant.supportsColor()) {
|
||||
// Add one item per color (16 standard colors)
|
||||
for (ItemColor color : ItemColor.values()) {
|
||||
if (
|
||||
color != ItemColor.CAUTION && color != ItemColor.CLEAR
|
||||
) {
|
||||
ItemStack stack = new ItemStack(
|
||||
ModItems.getBind(variant)
|
||||
);
|
||||
KidnapperItemSelector.applyColor(stack, color);
|
||||
items.add(stack);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No color variants
|
||||
items.add(new ItemStack(ModItems.getBind(variant)));
|
||||
}
|
||||
}
|
||||
|
||||
// All gags (19)
|
||||
for (GagVariant variant : GagVariant.values()) {
|
||||
if (variant.supportsColor()) {
|
||||
// Add one item per color
|
||||
for (ItemColor color : ItemColor.values()) {
|
||||
// TAPE_GAG can use caution/clear, others only standard colors
|
||||
if (
|
||||
variant == GagVariant.TAPE_GAG ||
|
||||
(color != ItemColor.CAUTION && color != ItemColor.CLEAR)
|
||||
) {
|
||||
ItemStack stack = new ItemStack(
|
||||
ModItems.getGag(variant)
|
||||
);
|
||||
KidnapperItemSelector.applyColor(stack, color);
|
||||
items.add(stack);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items.add(new ItemStack(ModItems.getGag(variant)));
|
||||
}
|
||||
}
|
||||
|
||||
// All blindfolds (2) - BOTH support colors
|
||||
for (BlindfoldVariant variant : BlindfoldVariant.values()) {
|
||||
if (variant.supportsColor()) {
|
||||
// Add one item per color (16 standard colors)
|
||||
for (ItemColor color : ItemColor.values()) {
|
||||
if (
|
||||
color != ItemColor.CAUTION && color != ItemColor.CLEAR
|
||||
) {
|
||||
ItemStack stack = new ItemStack(
|
||||
ModItems.getBlindfold(variant)
|
||||
);
|
||||
KidnapperItemSelector.applyColor(stack, color);
|
||||
items.add(stack);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
items.add(new ItemStack(ModItems.getBlindfold(variant)));
|
||||
}
|
||||
}
|
||||
|
||||
// Earplugs - no color support
|
||||
for (EarplugsVariant variant : EarplugsVariant.values()) {
|
||||
items.add(new ItemStack(ModItems.getEarplugs(variant)));
|
||||
}
|
||||
|
||||
// Mittens - no color support
|
||||
for (MittensVariant variant : MittensVariant.values()) {
|
||||
items.add(new ItemStack(ModItems.getMittens(variant)));
|
||||
// All data-driven bondage items (binds, gags, blindfolds, earplugs, mittens, collars, etc.)
|
||||
for (com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition def :
|
||||
com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry.getAll()) {
|
||||
items.add(DataDrivenBondageItem.createStack(def.id()));
|
||||
}
|
||||
|
||||
// Knives - no color support
|
||||
@@ -694,16 +627,11 @@ public class EntityKidnapperMerchant extends EntityKidnapperElite {
|
||||
items.add(new ItemStack(ModItems.getKnife(variant)));
|
||||
}
|
||||
|
||||
// Complex items
|
||||
items.add(new ItemStack(ModItems.CLASSIC_COLLAR.get()));
|
||||
items.add(new ItemStack(ModItems.SHOCK_COLLAR.get()));
|
||||
items.add(new ItemStack(ModItems.GPS_COLLAR.get()));
|
||||
// Tools
|
||||
items.add(new ItemStack(ModItems.WHIP.get()));
|
||||
// BLACKLIST: TASER (too powerful)
|
||||
// BLACKLIST: LOCKPICK (now in guaranteed utilities)
|
||||
// BLACKLIST: MASTER_KEY (too OP - unlocks everything)
|
||||
items.add(new ItemStack(ModItems.MEDICAL_GAG.get()));
|
||||
items.add(new ItemStack(ModItems.HOOD.get()));
|
||||
items.add(new ItemStack(ModItems.CLOTHES.get()));
|
||||
|
||||
return items;
|
||||
@@ -749,13 +677,13 @@ public class EntityKidnapperMerchant extends EntityKidnapperElite {
|
||||
Item i = item.getItem();
|
||||
|
||||
// Tier 4: GPS collar
|
||||
if (i == ModItems.GPS_COLLAR.get()) {
|
||||
if (com.tiedup.remake.v2.bondage.CollarHelper.hasGPS(item)) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
// Tier 3: Shock collar, taser, master key
|
||||
if (
|
||||
i == ModItems.SHOCK_COLLAR.get() ||
|
||||
com.tiedup.remake.v2.bondage.CollarHelper.canShock(item) ||
|
||||
i == ModItems.TASER.get() ||
|
||||
i == ModItems.MASTER_KEY.get()
|
||||
) {
|
||||
@@ -764,11 +692,9 @@ public class EntityKidnapperMerchant extends EntityKidnapperElite {
|
||||
|
||||
// Tier 2: Collars, whip, tools, complex items, clothes
|
||||
if (
|
||||
i == ModItems.CLASSIC_COLLAR.get() ||
|
||||
com.tiedup.remake.v2.bondage.CollarHelper.isCollar(item) ||
|
||||
i == ModItems.WHIP.get() ||
|
||||
i == ModItems.LOCKPICK.get() ||
|
||||
i == ModItems.MEDICAL_GAG.get() ||
|
||||
i == ModItems.HOOD.get() ||
|
||||
i instanceof GenericClothes
|
||||
) {
|
||||
return 2;
|
||||
|
||||
@@ -94,10 +94,8 @@ public class EntityRopeArrow extends AbstractArrow {
|
||||
int roll = this.random.nextInt(100) + 1;
|
||||
if (roll <= bindChance) {
|
||||
// Success! Bind the target
|
||||
ItemStack ropeItem = new ItemStack(
|
||||
ModItems.getBind(
|
||||
com.tiedup.remake.items.base.BindVariant.ROPES
|
||||
)
|
||||
ItemStack ropeItem = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(
|
||||
new net.minecraft.resources.ResourceLocation("tiedup", "ropes")
|
||||
);
|
||||
targetState.equip(BodyRegionV2.ARMS, ropeItem);
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package com.tiedup.remake.entities;
|
||||
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.v2.bondage.IV2BondageItem;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -46,7 +47,7 @@ public class KidnapperCaptureEquipment {
|
||||
) {
|
||||
return mainHand;
|
||||
}
|
||||
return new ItemStack(ModItems.getBind(BindVariant.ROPES));
|
||||
return DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +115,8 @@ public class KidnapperCaptureEquipment {
|
||||
@Nullable
|
||||
public ItemStack getCollarItem() {
|
||||
// Kidnappers always have a shock collar to mark their captives
|
||||
return new ItemStack(ModItems.SHOCK_COLLAR.get());
|
||||
return com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(
|
||||
new net.minecraft.resources.ResourceLocation("tiedup", "shock_collar"));
|
||||
}
|
||||
|
||||
// HELD ITEM MANAGEMENT
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package com.tiedup.remake.entities;
|
||||
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.util.teleport.Position;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -31,21 +29,7 @@ public class KidnapperCollarConfig {
|
||||
this.kidnapper = kidnapper;
|
||||
}
|
||||
|
||||
// COLLAR ITEM ACCESS
|
||||
|
||||
/**
|
||||
* Get the collar item if equipped.
|
||||
* @return ItemCollar or null if no collar or not an ItemCollar
|
||||
*/
|
||||
@Nullable
|
||||
public ItemCollar getCollarItem() {
|
||||
ItemStack collar = kidnapper.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.isEmpty()) return null;
|
||||
if (collar.getItem() instanceof ItemCollar itemCollar) {
|
||||
return itemCollar;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// COLLAR STACK ACCESS
|
||||
|
||||
/**
|
||||
* Get the collar ItemStack.
|
||||
@@ -55,30 +39,29 @@ public class KidnapperCollarConfig {
|
||||
return kidnapper.getEquipment(BodyRegionV2.NECK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the kidnapper has a valid collar.
|
||||
*/
|
||||
private boolean hasValidCollar() {
|
||||
return kidnapper.hasCollar() && CollarHelper.isCollar(getCollarStack());
|
||||
}
|
||||
|
||||
// KIDNAPPING MODE
|
||||
|
||||
/**
|
||||
* Check if kidnapping mode is enabled via collar.
|
||||
*/
|
||||
public boolean isKidnappingModeEnabled() {
|
||||
if (!kidnapper.hasCollar()) return false;
|
||||
|
||||
ItemCollar itemCollar = getCollarItem();
|
||||
if (itemCollar == null) return false;
|
||||
|
||||
return itemCollar.isKidnappingModeEnabled(getCollarStack());
|
||||
if (!hasValidCollar()) return false;
|
||||
return CollarHelper.isKidnappingModeEnabled(getCollarStack());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if kidnapping mode is fully ready (enabled + prison set).
|
||||
*/
|
||||
public boolean isKidnappingModeReady() {
|
||||
if (!kidnapper.hasCollar()) return false;
|
||||
|
||||
ItemCollar itemCollar = getCollarItem();
|
||||
if (itemCollar == null) return false;
|
||||
|
||||
return itemCollar.isKidnappingModeReady(getCollarStack());
|
||||
if (!hasValidCollar()) return false;
|
||||
return CollarHelper.isKidnappingModeReady(getCollarStack());
|
||||
}
|
||||
|
||||
// POSITION GETTERS
|
||||
@@ -88,20 +71,16 @@ public class KidnapperCollarConfig {
|
||||
*/
|
||||
@Nullable
|
||||
public java.util.UUID getCellId() {
|
||||
ItemCollar itemCollar = getCollarItem();
|
||||
if (itemCollar == null) return null;
|
||||
|
||||
return itemCollar.getCellId(getCollarStack());
|
||||
if (!hasValidCollar()) return null;
|
||||
return CollarHelper.getCellId(getCollarStack());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if collar has a cell assigned.
|
||||
*/
|
||||
public boolean hasCellAssigned() {
|
||||
ItemCollar itemCollar = getCollarItem();
|
||||
if (itemCollar == null) return false;
|
||||
|
||||
return itemCollar.hasCellAssigned(getCollarStack());
|
||||
if (!hasValidCollar()) return false;
|
||||
return CollarHelper.hasCellAssigned(getCollarStack());
|
||||
}
|
||||
|
||||
// BEHAVIOR FLAGS
|
||||
@@ -110,20 +89,16 @@ public class KidnapperCollarConfig {
|
||||
* Check if should warn masters after capturing slave.
|
||||
*/
|
||||
public boolean shouldWarnMasters() {
|
||||
ItemCollar itemCollar = getCollarItem();
|
||||
if (itemCollar == null) return false;
|
||||
|
||||
return itemCollar.shouldWarnMasters(getCollarStack());
|
||||
if (!hasValidCollar()) return false;
|
||||
return CollarHelper.shouldWarnMasters(getCollarStack());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if should tie slave to pole in prison.
|
||||
*/
|
||||
public boolean shouldTieToPole() {
|
||||
ItemCollar itemCollar = getCollarItem();
|
||||
if (itemCollar == null) return false;
|
||||
|
||||
return itemCollar.shouldTieToPole(getCollarStack());
|
||||
if (!hasValidCollar()) return false;
|
||||
return CollarHelper.shouldTieToPole(getCollarStack());
|
||||
}
|
||||
|
||||
// BLACKLIST/WHITELIST
|
||||
@@ -139,19 +114,18 @@ public class KidnapperCollarConfig {
|
||||
* </ul>
|
||||
*/
|
||||
public boolean isValidKidnappingTarget(Player player) {
|
||||
ItemCollar itemCollar = getCollarItem();
|
||||
if (itemCollar == null) return true; // No collar config = everyone is valid
|
||||
if (!hasValidCollar()) return true; // No collar config = everyone is valid
|
||||
|
||||
ItemStack collarStack = getCollarStack();
|
||||
UUID playerUUID = player.getUUID();
|
||||
|
||||
// If whitelist exists and is not empty, player MUST be on it
|
||||
List<UUID> whitelist = itemCollar.getWhitelist(collarStack);
|
||||
List<UUID> whitelist = CollarHelper.getWhitelist(collarStack);
|
||||
if (!whitelist.isEmpty()) {
|
||||
return whitelist.contains(playerUUID);
|
||||
}
|
||||
|
||||
// Otherwise, check blacklist (blacklisted = not a valid target)
|
||||
return !itemCollar.isBlacklisted(collarStack, playerUUID);
|
||||
return !CollarHelper.isBlacklisted(collarStack, playerUUID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package com.tiedup.remake.entities;
|
||||
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.*;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import java.util.Random;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -11,6 +12,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
* Helper class for selecting themed items for kidnappers.
|
||||
* Handles probability-based item selection and color application.
|
||||
*
|
||||
* All bondage items are now created via DataDrivenBondageItem.createStack().
|
||||
*
|
||||
* Item selection order (most to least common):
|
||||
* 1. Bind (arms) - 100% (always)
|
||||
* 2. Gag - 50%
|
||||
@@ -78,33 +81,10 @@ public class KidnapperItemSelector {
|
||||
this.blindfold = blindfold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this selection has a gag.
|
||||
*/
|
||||
public boolean hasGag() {
|
||||
return !gag.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this selection has mittens.
|
||||
*/
|
||||
public boolean hasMittens() {
|
||||
return !mittens.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this selection has earplugs.
|
||||
*/
|
||||
public boolean hasEarplugs() {
|
||||
return !earplugs.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this selection has a blindfold.
|
||||
*/
|
||||
public boolean hasBlindfold() {
|
||||
return !blindfold.isEmpty();
|
||||
}
|
||||
public boolean hasGag() { return !gag.isEmpty(); }
|
||||
public boolean hasMittens() { return !mittens.isEmpty(); }
|
||||
public boolean hasEarplugs() { return !earplugs.isEmpty(); }
|
||||
public boolean hasBlindfold() { return !blindfold.isEmpty(); }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,16 +108,6 @@ public class KidnapperItemSelector {
|
||||
return selectItems(false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate adjusted probability based on kidnapper type.
|
||||
*
|
||||
* @param baseProb Base probability for the item
|
||||
* @param eliteBonus Bonus probability for elite kidnappers
|
||||
* @param archerPenalty Penalty probability for archer kidnappers
|
||||
* @param isElite Whether the kidnapper is elite
|
||||
* @param isArcher Whether the kidnapper is an archer
|
||||
* @return Adjusted probability
|
||||
*/
|
||||
private static double getAdjustedProbability(
|
||||
double baseProb,
|
||||
double eliteBonus,
|
||||
@@ -151,9 +121,6 @@ public class KidnapperItemSelector {
|
||||
return prob;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal item selection logic.
|
||||
*/
|
||||
private static SelectionResult selectItems(
|
||||
boolean isElite,
|
||||
boolean isArcher
|
||||
@@ -162,126 +129,64 @@ public class KidnapperItemSelector {
|
||||
KidnapperTheme theme = KidnapperTheme.getRandomWeighted();
|
||||
|
||||
// 2. Select color (if theme supports it)
|
||||
// Filter out colors that don't have textures for this theme's bind
|
||||
ItemColor color = theme.supportsColor()
|
||||
? getValidColorForBind(theme.getBind())
|
||||
? ItemColor.getRandomStandard()
|
||||
: null;
|
||||
|
||||
// 3. Create bind (always)
|
||||
ItemStack bind = createBind(theme.getBind(), color);
|
||||
ItemStack bind = createItemById(theme.getBindId(), color);
|
||||
|
||||
// 4. Roll for gag (randomly selected from theme's compatible gags)
|
||||
// 4. Roll for gag
|
||||
ItemStack gag = ItemStack.EMPTY;
|
||||
double gagProb = getAdjustedProbability(
|
||||
PROB_GAG,
|
||||
ELITE_GAG_BONUS,
|
||||
ARCHER_GAG_PENALTY,
|
||||
isElite,
|
||||
isArcher
|
||||
PROB_GAG, ELITE_GAG_BONUS, ARCHER_GAG_PENALTY, isElite, isArcher
|
||||
);
|
||||
if (RANDOM.nextDouble() < gagProb) {
|
||||
gag = createGag(theme.getRandomGag(), color);
|
||||
gag = createItemById(theme.getRandomGagId(), color);
|
||||
}
|
||||
|
||||
// 5. Roll for mittens (same for all themes)
|
||||
// 5. Roll for mittens
|
||||
ItemStack mittens = ItemStack.EMPTY;
|
||||
double mittensProb = getAdjustedProbability(
|
||||
PROB_MITTENS,
|
||||
ELITE_MITTENS_BONUS,
|
||||
ARCHER_MITTENS_PENALTY,
|
||||
isElite,
|
||||
isArcher
|
||||
PROB_MITTENS, ELITE_MITTENS_BONUS, ARCHER_MITTENS_PENALTY, isElite, isArcher
|
||||
);
|
||||
if (RANDOM.nextDouble() < mittensProb) {
|
||||
mittens = createMittens();
|
||||
}
|
||||
|
||||
// 6. Roll for earplugs (same for all themes)
|
||||
// 6. Roll for earplugs
|
||||
ItemStack earplugs = ItemStack.EMPTY;
|
||||
double earplugsProb = getAdjustedProbability(
|
||||
PROB_EARPLUGS,
|
||||
ELITE_EARPLUGS_BONUS,
|
||||
ARCHER_EARPLUGS_PENALTY,
|
||||
isElite,
|
||||
isArcher
|
||||
PROB_EARPLUGS, ELITE_EARPLUGS_BONUS, ARCHER_EARPLUGS_PENALTY, isElite, isArcher
|
||||
);
|
||||
if (RANDOM.nextDouble() < earplugsProb) {
|
||||
earplugs = createEarplugs();
|
||||
}
|
||||
|
||||
// 7. Roll for blindfold (last, most restrictive - randomly selected)
|
||||
// 7. Roll for blindfold
|
||||
ItemStack blindfold = ItemStack.EMPTY;
|
||||
double blindfoldProb = getAdjustedProbability(
|
||||
PROB_BLINDFOLD,
|
||||
ELITE_BLINDFOLD_BONUS,
|
||||
ARCHER_BLINDFOLD_PENALTY,
|
||||
isElite,
|
||||
isArcher
|
||||
PROB_BLINDFOLD, ELITE_BLINDFOLD_BONUS, ARCHER_BLINDFOLD_PENALTY, isElite, isArcher
|
||||
);
|
||||
if (theme.hasBlindfolds() && RANDOM.nextDouble() < blindfoldProb) {
|
||||
blindfold = createBlindfold(theme.getRandomBlindfold(), color);
|
||||
blindfold = createItemById(theme.getRandomBlindfoldId(), color);
|
||||
}
|
||||
|
||||
return new SelectionResult(
|
||||
theme,
|
||||
color,
|
||||
bind,
|
||||
gag,
|
||||
mittens,
|
||||
earplugs,
|
||||
blindfold
|
||||
theme, color, bind, gag, mittens, earplugs, blindfold
|
||||
);
|
||||
}
|
||||
|
||||
// ITEM CREATION METHODS
|
||||
|
||||
/**
|
||||
* Create a bind ItemStack with optional color.
|
||||
* Create a data-driven bondage item by registry name, with optional color.
|
||||
*/
|
||||
public static ItemStack createBind(
|
||||
BindVariant variant,
|
||||
@Nullable ItemColor color
|
||||
) {
|
||||
ItemStack stack = new ItemStack(ModItems.getBind(variant));
|
||||
if (color != null && variant.supportsColor()) {
|
||||
applyColor(stack, color);
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a gag ItemStack with optional color.
|
||||
* Validates that the color has a texture for this gag variant.
|
||||
*/
|
||||
public static ItemStack createGag(
|
||||
GagVariant variant,
|
||||
@Nullable ItemColor color
|
||||
) {
|
||||
ItemStack stack = new ItemStack(ModItems.getGag(variant));
|
||||
if (
|
||||
color != null &&
|
||||
variant.supportsColor() &&
|
||||
isColorValidForGag(color, variant)
|
||||
) {
|
||||
applyColor(stack, color);
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a blindfold ItemStack with optional color.
|
||||
* Validates that the color has a texture for this blindfold variant.
|
||||
*/
|
||||
public static ItemStack createBlindfold(
|
||||
BlindfoldVariant variant,
|
||||
@Nullable ItemColor color
|
||||
) {
|
||||
ItemStack stack = new ItemStack(ModItems.getBlindfold(variant));
|
||||
if (
|
||||
color != null &&
|
||||
variant.supportsColor() &&
|
||||
isColorValidForBlindfold(color, variant)
|
||||
) {
|
||||
public static ItemStack createItemById(String id, @Nullable ItemColor color) {
|
||||
ItemStack stack = DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", id)
|
||||
);
|
||||
if (color != null) {
|
||||
applyColor(stack, color);
|
||||
}
|
||||
return stack;
|
||||
@@ -289,18 +194,20 @@ public class KidnapperItemSelector {
|
||||
|
||||
/**
|
||||
* Create mittens ItemStack.
|
||||
* Mittens don't have color variants.
|
||||
*/
|
||||
public static ItemStack createMittens() {
|
||||
return new ItemStack(ModItems.getMittens(MittensVariant.LEATHER));
|
||||
return DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "leather_mittens")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create earplugs ItemStack.
|
||||
* Earplugs don't have color variants.
|
||||
*/
|
||||
public static ItemStack createEarplugs() {
|
||||
return new ItemStack(ModItems.getEarplugs(EarplugsVariant.CLASSIC));
|
||||
return DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "classic_earplugs")
|
||||
);
|
||||
}
|
||||
|
||||
// COLOR METHODS
|
||||
@@ -310,7 +217,6 @@ public class KidnapperItemSelector {
|
||||
|
||||
/**
|
||||
* Apply color NBT to an ItemStack.
|
||||
* Sets both the ItemColor name and CustomModelData for model selection.
|
||||
*/
|
||||
public static void applyColor(ItemStack stack, ItemColor color) {
|
||||
if (stack.isEmpty() || color == null) return;
|
||||
@@ -340,141 +246,9 @@ public class KidnapperItemSelector {
|
||||
|
||||
/**
|
||||
* Get the texture suffix for an item's color.
|
||||
* Example: "ropes" + "_red" = "ropes_red"
|
||||
* @return The color suffix (e.g., "_red"), or empty string if no color
|
||||
*/
|
||||
public static String getColorSuffix(ItemStack stack) {
|
||||
ItemColor color = getColor(stack);
|
||||
return color != null ? "_" + color.getName() : "";
|
||||
}
|
||||
|
||||
// COLOR VALIDATION
|
||||
|
||||
/**
|
||||
* Get a random color that has a texture for the given bind variant.
|
||||
* Excludes colors that don't have textures for specific variants.
|
||||
*/
|
||||
public static ItemColor getValidColorForBind(BindVariant variant) {
|
||||
ItemColor color;
|
||||
int attempts = 0;
|
||||
do {
|
||||
color = ItemColor.getRandomStandard();
|
||||
attempts++;
|
||||
// Prevent infinite loop
|
||||
if (attempts > 50) break;
|
||||
} while (!isColorValidForBind(color, variant));
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a color has a texture for the given bind variant.
|
||||
* Returns false for colors without textures.
|
||||
*/
|
||||
public static boolean isColorValidForBind(
|
||||
ItemColor color,
|
||||
BindVariant variant
|
||||
) {
|
||||
if (color == null || variant == null) return true;
|
||||
|
||||
// BROWN doesn't have textures for ROPES and SHIBARI
|
||||
if (
|
||||
color == ItemColor.BROWN &&
|
||||
(variant == BindVariant.ROPES || variant == BindVariant.SHIBARI)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// GRAY doesn't have texture for DUCT_TAPE
|
||||
if (color == ItemColor.GRAY && variant == BindVariant.DUCT_TAPE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a color has a texture for the given gag variant.
|
||||
*/
|
||||
public static boolean isColorValidForGag(
|
||||
ItemColor color,
|
||||
GagVariant variant
|
||||
) {
|
||||
if (color == null || variant == null) return true;
|
||||
|
||||
// GRAY doesn't have texture for TAPE_GAG
|
||||
if (color == ItemColor.GRAY && variant == GagVariant.TAPE_GAG) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// WHITE doesn't have texture for CLOTH_GAG and CLEAVE_GAG
|
||||
if (
|
||||
color == ItemColor.WHITE &&
|
||||
(variant == GagVariant.CLOTH_GAG ||
|
||||
variant == GagVariant.CLEAVE_GAG)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// RED doesn't have texture for BALL_GAG and BALL_GAG_STRAP
|
||||
if (
|
||||
color == ItemColor.RED &&
|
||||
(variant == GagVariant.BALL_GAG ||
|
||||
variant == GagVariant.BALL_GAG_STRAP)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a color has a texture for the given blindfold variant.
|
||||
*/
|
||||
public static boolean isColorValidForBlindfold(
|
||||
ItemColor color,
|
||||
BlindfoldVariant variant
|
||||
) {
|
||||
if (color == null || variant == null) return true;
|
||||
|
||||
// BLACK doesn't have texture for CLASSIC or MASK blindfolds
|
||||
if (
|
||||
color == ItemColor.BLACK &&
|
||||
(variant == BlindfoldVariant.CLASSIC ||
|
||||
variant == BlindfoldVariant.MASK)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random color that has a texture for the given gag variant.
|
||||
*/
|
||||
public static ItemColor getValidColorForGag(GagVariant variant) {
|
||||
ItemColor color;
|
||||
int attempts = 0;
|
||||
do {
|
||||
color = ItemColor.getRandomStandard();
|
||||
attempts++;
|
||||
if (attempts > 50) break;
|
||||
} while (!isColorValidForGag(color, variant));
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random color that has a texture for the given blindfold variant.
|
||||
*/
|
||||
public static ItemColor getValidColorForBlindfold(
|
||||
BlindfoldVariant variant
|
||||
) {
|
||||
ItemColor color;
|
||||
int attempts = 0;
|
||||
do {
|
||||
color = ItemColor.getRandomStandard();
|
||||
attempts++;
|
||||
if (attempts > 50) break;
|
||||
} while (!isColorValidForBlindfold(color, variant));
|
||||
return color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.tiedup.remake.entities;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.state.CollarRegistry;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.util.tasks.ItemTask;
|
||||
@@ -161,16 +161,17 @@ public class KidnapperJobManager {
|
||||
);
|
||||
|
||||
// Put a shock collar on the worker AFTER untie/free
|
||||
ItemStack shockCollar = new ItemStack(ModItems.SHOCK_COLLAR_AUTO.get());
|
||||
if (shockCollar.getItem() instanceof ItemCollar collarItem) {
|
||||
// Add kidnapper as owner so the collar is linked
|
||||
collarItem.addOwner(
|
||||
shockCollar,
|
||||
kidnapper.getUUID(),
|
||||
kidnapper.getNpcName()
|
||||
);
|
||||
// Lock the collar so they can't remove it
|
||||
shockCollar = collarItem.setLocked(shockCollar, true);
|
||||
ItemStack shockCollar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(
|
||||
new net.minecraft.resources.ResourceLocation("tiedup", "shock_collar_auto"));
|
||||
// Add kidnapper as owner so the collar is linked
|
||||
CollarHelper.addOwner(
|
||||
shockCollar,
|
||||
kidnapper.getUUID(),
|
||||
kidnapper.getNpcName()
|
||||
);
|
||||
// Lock the collar so they can't remove it
|
||||
if (shockCollar.getItem() instanceof com.tiedup.remake.items.base.ILockable lockable) {
|
||||
shockCollar = lockable.setLocked(shockCollar, true);
|
||||
}
|
||||
captive.equip(BodyRegionV2.NECK, shockCollar);
|
||||
|
||||
|
||||
@@ -1,42 +1,33 @@
|
||||
package com.tiedup.remake.entities;
|
||||
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.items.base.BlindfoldVariant;
|
||||
import com.tiedup.remake.items.base.GagVariant;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Defines themed item sets for kidnappers.
|
||||
* Each theme groups compatible binds, gags, and blindfolds.
|
||||
* Each theme groups compatible binds, gags, and blindfolds by registry name.
|
||||
*
|
||||
* Themes are selected randomly with weighted probabilities.
|
||||
* Higher weight = more common theme.
|
||||
*
|
||||
* Note: Natural themes (SLIME, VINE, WEB) are reserved for monsters.
|
||||
*
|
||||
* Registry names correspond to data-driven bondage item IDs in DataDrivenItemRegistry.
|
||||
*/
|
||||
public enum KidnapperTheme {
|
||||
// === ROPE THEMES (most common) ===
|
||||
|
||||
ROPE(
|
||||
BindVariant.ROPES,
|
||||
new GagVariant[] {
|
||||
GagVariant.ROPES_GAG,
|
||||
GagVariant.CLOTH_GAG,
|
||||
GagVariant.CLEAVE_GAG,
|
||||
},
|
||||
new BlindfoldVariant[] { BlindfoldVariant.CLASSIC },
|
||||
"ropes",
|
||||
new String[] { "ropes_gag", "cloth_gag", "cleave_gag" },
|
||||
new String[] { "classic_blindfold" },
|
||||
true, // supportsColor
|
||||
30 // weight (spawn probability)
|
||||
),
|
||||
|
||||
SHIBARI(
|
||||
BindVariant.SHIBARI,
|
||||
new GagVariant[] {
|
||||
GagVariant.ROPES_GAG,
|
||||
GagVariant.CLOTH_GAG,
|
||||
GagVariant.RIBBON_GAG,
|
||||
},
|
||||
new BlindfoldVariant[] { BlindfoldVariant.CLASSIC },
|
||||
"shibari",
|
||||
new String[] { "ropes_gag", "cloth_gag", "ribbon_gag" },
|
||||
new String[] { "classic_blindfold" },
|
||||
true,
|
||||
15
|
||||
),
|
||||
@@ -44,9 +35,9 @@ public enum KidnapperTheme {
|
||||
// === TAPE THEME ===
|
||||
|
||||
TAPE(
|
||||
BindVariant.DUCT_TAPE,
|
||||
new GagVariant[] { GagVariant.TAPE_GAG, GagVariant.WRAP_GAG },
|
||||
new BlindfoldVariant[] { BlindfoldVariant.MASK },
|
||||
"duct_tape",
|
||||
new String[] { "tape_gag", "wrap_gag" },
|
||||
new String[] { "blindfold_mask" },
|
||||
true,
|
||||
20
|
||||
),
|
||||
@@ -54,13 +45,9 @@ public enum KidnapperTheme {
|
||||
// === LEATHER/BDSM THEME ===
|
||||
|
||||
LEATHER(
|
||||
BindVariant.LEATHER_STRAPS,
|
||||
new GagVariant[] {
|
||||
GagVariant.BALL_GAG,
|
||||
GagVariant.BALL_GAG_STRAP,
|
||||
GagVariant.PANEL_GAG,
|
||||
},
|
||||
new BlindfoldVariant[] { BlindfoldVariant.MASK },
|
||||
"leather_straps",
|
||||
new String[] { "ball_gag", "ball_gag_strap", "panel_gag" },
|
||||
new String[] { "blindfold_mask" },
|
||||
false,
|
||||
15
|
||||
),
|
||||
@@ -68,12 +55,9 @@ public enum KidnapperTheme {
|
||||
// === CHAIN THEME ===
|
||||
|
||||
CHAIN(
|
||||
BindVariant.CHAIN,
|
||||
new GagVariant[] {
|
||||
GagVariant.CHAIN_PANEL_GAG,
|
||||
GagVariant.BALL_GAG_STRAP,
|
||||
},
|
||||
new BlindfoldVariant[] { BlindfoldVariant.MASK },
|
||||
"chain",
|
||||
new String[] { "chain_panel_gag", "ball_gag_strap" },
|
||||
new String[] { "blindfold_mask" },
|
||||
false,
|
||||
10
|
||||
),
|
||||
@@ -81,13 +65,9 @@ public enum KidnapperTheme {
|
||||
// === MEDICAL THEME ===
|
||||
|
||||
MEDICAL(
|
||||
BindVariant.MEDICAL_STRAPS,
|
||||
new GagVariant[] {
|
||||
GagVariant.TUBE_GAG,
|
||||
GagVariant.SPONGE_GAG,
|
||||
GagVariant.BALL_GAG,
|
||||
},
|
||||
new BlindfoldVariant[] { BlindfoldVariant.MASK },
|
||||
"medical_straps",
|
||||
new String[] { "tube_gag", "sponge_gag", "ball_gag" },
|
||||
new String[] { "blindfold_mask" },
|
||||
false,
|
||||
8
|
||||
),
|
||||
@@ -95,9 +75,9 @@ public enum KidnapperTheme {
|
||||
// === SCI-FI/BEAM THEME ===
|
||||
|
||||
BEAM(
|
||||
BindVariant.BEAM_CUFFS,
|
||||
new GagVariant[] { GagVariant.BEAM_PANEL_GAG, GagVariant.LATEX_GAG },
|
||||
new BlindfoldVariant[] { BlindfoldVariant.MASK },
|
||||
"beam_cuffs",
|
||||
new String[] { "beam_panel_gag", "latex_gag" },
|
||||
new String[] { "blindfold_mask" },
|
||||
false,
|
||||
5
|
||||
),
|
||||
@@ -105,9 +85,9 @@ public enum KidnapperTheme {
|
||||
// === LATEX THEME (rare) ===
|
||||
|
||||
LATEX(
|
||||
BindVariant.LATEX_SACK,
|
||||
new GagVariant[] { GagVariant.LATEX_GAG, GagVariant.TUBE_GAG },
|
||||
new BlindfoldVariant[] { BlindfoldVariant.MASK },
|
||||
"latex_sack",
|
||||
new String[] { "latex_gag", "tube_gag" },
|
||||
new String[] { "blindfold_mask" },
|
||||
false,
|
||||
3
|
||||
),
|
||||
@@ -115,13 +95,9 @@ public enum KidnapperTheme {
|
||||
// === ASYLUM THEME (rare) ===
|
||||
|
||||
ASYLUM(
|
||||
BindVariant.STRAITJACKET,
|
||||
new GagVariant[] {
|
||||
GagVariant.BITE_GAG,
|
||||
GagVariant.SPONGE_GAG,
|
||||
GagVariant.BALL_GAG,
|
||||
},
|
||||
new BlindfoldVariant[] { BlindfoldVariant.MASK },
|
||||
"straitjacket",
|
||||
new String[] { "bite_gag", "sponge_gag", "ball_gag" },
|
||||
new String[] { "blindfold_mask" },
|
||||
false,
|
||||
5
|
||||
),
|
||||
@@ -129,9 +105,9 @@ public enum KidnapperTheme {
|
||||
// === RIBBON THEME (cute/playful) ===
|
||||
|
||||
RIBBON(
|
||||
BindVariant.RIBBON,
|
||||
new GagVariant[] { GagVariant.RIBBON_GAG, GagVariant.CLOTH_GAG },
|
||||
new BlindfoldVariant[] { BlindfoldVariant.CLASSIC },
|
||||
"ribbon",
|
||||
new String[] { "ribbon_gag", "cloth_gag" },
|
||||
new String[] { "classic_blindfold" },
|
||||
false,
|
||||
8
|
||||
),
|
||||
@@ -139,54 +115,54 @@ public enum KidnapperTheme {
|
||||
// === WRAP THEME ===
|
||||
|
||||
WRAP(
|
||||
BindVariant.WRAP,
|
||||
new GagVariant[] { GagVariant.WRAP_GAG, GagVariant.TAPE_GAG },
|
||||
new BlindfoldVariant[] { BlindfoldVariant.MASK },
|
||||
"wrap",
|
||||
new String[] { "wrap_gag", "tape_gag" },
|
||||
new String[] { "blindfold_mask" },
|
||||
false,
|
||||
5
|
||||
);
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private final BindVariant bind;
|
||||
private final GagVariant[] gags;
|
||||
private final BlindfoldVariant[] blindfolds;
|
||||
private final String bindId;
|
||||
private final String[] gagIds;
|
||||
private final String[] blindfoldIds;
|
||||
private final boolean supportsColor;
|
||||
private final int weight;
|
||||
|
||||
KidnapperTheme(
|
||||
BindVariant bind,
|
||||
GagVariant[] gags,
|
||||
BlindfoldVariant[] blindfolds,
|
||||
String bindId,
|
||||
String[] gagIds,
|
||||
String[] blindfoldIds,
|
||||
boolean supportsColor,
|
||||
int weight
|
||||
) {
|
||||
this.bind = bind;
|
||||
this.gags = gags;
|
||||
this.blindfolds = blindfolds;
|
||||
this.bindId = bindId;
|
||||
this.gagIds = gagIds;
|
||||
this.blindfoldIds = blindfoldIds;
|
||||
this.supportsColor = supportsColor;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary bind for this theme.
|
||||
* Get the primary bind registry name for this theme.
|
||||
*/
|
||||
public BindVariant getBind() {
|
||||
return bind;
|
||||
public String getBindId() {
|
||||
return bindId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get compatible gags for this theme (ordered by preference).
|
||||
* Get compatible gag IDs for this theme (ordered by preference).
|
||||
*/
|
||||
public GagVariant[] getGags() {
|
||||
return gags;
|
||||
public String[] getGagIds() {
|
||||
return gagIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get compatible blindfolds for this theme.
|
||||
* Get compatible blindfold IDs for this theme.
|
||||
*/
|
||||
public BlindfoldVariant[] getBlindfolds() {
|
||||
return blindfolds;
|
||||
public String[] getBlindfoldIds() {
|
||||
return blindfoldIds;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,41 +182,40 @@ public enum KidnapperTheme {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get primary gag (first in list).
|
||||
* Used when only one gag is selected.
|
||||
* Get primary gag ID (first in list).
|
||||
*/
|
||||
public GagVariant getPrimaryGag() {
|
||||
return gags.length > 0 ? gags[0] : GagVariant.BALL_GAG;
|
||||
public String getPrimaryGagId() {
|
||||
return gagIds.length > 0 ? gagIds[0] : "ball_gag";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random gag from this theme's compatible list.
|
||||
* Get a random gag ID from this theme's compatible list.
|
||||
*/
|
||||
public GagVariant getRandomGag() {
|
||||
if (gags.length == 0) return GagVariant.BALL_GAG;
|
||||
return gags[RANDOM.nextInt(gags.length)];
|
||||
public String getRandomGagId() {
|
||||
if (gagIds.length == 0) return "ball_gag";
|
||||
return gagIds[RANDOM.nextInt(gagIds.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get primary blindfold (first in list).
|
||||
* Get primary blindfold ID (first in list).
|
||||
*/
|
||||
public BlindfoldVariant getPrimaryBlindfold() {
|
||||
return blindfolds.length > 0 ? blindfolds[0] : BlindfoldVariant.CLASSIC;
|
||||
public String getPrimaryBlindfoldId() {
|
||||
return blindfoldIds.length > 0 ? blindfoldIds[0] : "classic_blindfold";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random blindfold from this theme's compatible list.
|
||||
* Get a random blindfold ID from this theme's compatible list.
|
||||
*/
|
||||
public BlindfoldVariant getRandomBlindfold() {
|
||||
if (blindfolds.length == 0) return BlindfoldVariant.CLASSIC;
|
||||
return blindfolds[RANDOM.nextInt(blindfolds.length)];
|
||||
public String getRandomBlindfoldId() {
|
||||
if (blindfoldIds.length == 0) return "classic_blindfold";
|
||||
return blindfoldIds[RANDOM.nextInt(blindfoldIds.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this theme has any blindfolds.
|
||||
*/
|
||||
public boolean hasBlindfolds() {
|
||||
return blindfolds.length > 0;
|
||||
return blindfoldIds.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.tiedup.remake.entities;
|
||||
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -108,7 +107,7 @@ public final class LeashProxyEntity extends Turtle {
|
||||
ItemStack bind = state.getEquipment(BodyRegionV2.ARMS);
|
||||
if (
|
||||
!bind.isEmpty() &&
|
||||
bind.getItem() == ModItems.getBind(BindVariant.DOGBINDER)
|
||||
PoseTypeHelper.getPoseType(bind) == com.tiedup.remake.items.base.PoseType.DOG
|
||||
) {
|
||||
yOffset = 0.35D; // Lower for 4-legged dogwalk pose (back/hip level)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.tiedup.remake.entities.EntityKidnapper;
|
||||
import com.tiedup.remake.entities.EntityKidnapper.CaptivePriority;
|
||||
import com.tiedup.remake.entities.ai.StuckDetector;
|
||||
import com.tiedup.remake.entities.ai.WaypointNavigator;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.prison.PrisonerManager;
|
||||
import com.tiedup.remake.prison.PrisonerRecord;
|
||||
import com.tiedup.remake.prison.PrisonerState;
|
||||
@@ -260,7 +260,7 @@ public class KidnapperBringToCellGoal extends Goal {
|
||||
);
|
||||
IRestrainable captive = this.kidnapper.getCaptive();
|
||||
if (captive != null) {
|
||||
ItemCollar.runWithSuppressedAlert(() ->
|
||||
CollarHelper.runWithSuppressedAlert(() ->
|
||||
captive.free(false)
|
||||
);
|
||||
this.kidnapper.removeCaptive(captive, false);
|
||||
@@ -1079,9 +1079,9 @@ public class KidnapperBringToCellGoal extends Goal {
|
||||
ItemStack collar = captive.getEquipment(BodyRegionV2.NECK);
|
||||
if (
|
||||
!collar.isEmpty() &&
|
||||
collar.getItem() instanceof ItemCollar collarItem
|
||||
CollarHelper.isCollar(collar)
|
||||
) {
|
||||
collarItem.setCellId(collar, this.targetCell.getId());
|
||||
CollarHelper.setCellId(collar, this.targetCell.getId());
|
||||
}
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.tiedup.remake.entities.EntityKidnapper;
|
||||
import com.tiedup.remake.entities.ai.StuckDetector;
|
||||
import com.tiedup.remake.items.ItemKey;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.network.action.PacketTying;
|
||||
import com.tiedup.remake.prison.PrisonerManager;
|
||||
@@ -849,11 +849,8 @@ public class KidnapperCaptureGoal extends Goal {
|
||||
// If already has collar, check ownership
|
||||
if (state.hasCollar()) {
|
||||
ItemStack existingCollar = state.getEquipment(BodyRegionV2.NECK);
|
||||
if (
|
||||
existingCollar.getItem() instanceof
|
||||
com.tiedup.remake.items.base.ItemCollar collarItem
|
||||
) {
|
||||
java.util.List<java.util.UUID> owners = collarItem.getOwners(
|
||||
if (CollarHelper.isCollar(existingCollar)) {
|
||||
java.util.List<java.util.UUID> owners = CollarHelper.getOwners(
|
||||
existingCollar
|
||||
);
|
||||
if (!owners.contains(this.kidnapper.getUUID())) {
|
||||
@@ -885,9 +882,9 @@ public class KidnapperCaptureGoal extends Goal {
|
||||
for (java.util.UUID oldOwner : new java.util.ArrayList<>(
|
||||
owners
|
||||
)) {
|
||||
collarItem.removeOwner(existingCollar, oldOwner);
|
||||
CollarHelper.removeOwner(existingCollar, oldOwner);
|
||||
}
|
||||
collarItem.addOwner(
|
||||
CollarHelper.addOwner(
|
||||
existingCollar,
|
||||
this.kidnapper.getUUID(),
|
||||
this.kidnapper.getNpcName()
|
||||
@@ -929,9 +926,9 @@ public class KidnapperCaptureGoal extends Goal {
|
||||
if (this.captureProgress >= COLLAR_APPLY_TIME) {
|
||||
// Create a copy of the collar and configure it
|
||||
ItemStack collarCopy = collar.copy();
|
||||
if (collarCopy.getItem() instanceof ItemCollar collarItem) {
|
||||
if (CollarHelper.isCollar(collarCopy)) {
|
||||
// Add kidnapper as owner
|
||||
collarItem.addOwner(
|
||||
CollarHelper.addOwner(
|
||||
collarCopy,
|
||||
this.kidnapper.getUUID(),
|
||||
this.kidnapper.getNpcName()
|
||||
@@ -941,12 +938,17 @@ public class KidnapperCaptureGoal extends Goal {
|
||||
ItemStack keyStack = new ItemStack(ModItems.COLLAR_KEY.get());
|
||||
if (keyStack.getItem() instanceof ItemKey keyItem) {
|
||||
UUID keyUUID = keyItem.getKeyUUID(keyStack);
|
||||
collarItem.setLockedByKeyUUID(collarCopy, keyUUID);
|
||||
// Lock via ILockable interface (collar must implement it)
|
||||
if (collarCopy.getItem() instanceof com.tiedup.remake.items.base.ILockable lockable) {
|
||||
lockable.setLockedByKeyUUID(collarCopy, keyUUID);
|
||||
}
|
||||
// Store the key on the kidnapper for potential drop on death
|
||||
this.kidnapper.addCollarKey(keyStack);
|
||||
} else {
|
||||
// Fallback: just lock without a key
|
||||
collarItem.setLocked(collarCopy, true);
|
||||
if (collarCopy.getItem() instanceof com.tiedup.remake.items.base.ILockable lockable) {
|
||||
lockable.setLocked(collarCopy, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import com.tiedup.remake.entities.AbstractTiedUpNpc;
|
||||
import com.tiedup.remake.entities.EntityKidnapper;
|
||||
import com.tiedup.remake.entities.ai.StuckDetector;
|
||||
import com.tiedup.remake.entities.ai.WaypointNavigator;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import com.tiedup.remake.util.KidnapperAIHelper;
|
||||
@@ -547,9 +547,7 @@ public class KidnapperWalkPrisonerGoal extends Goal {
|
||||
);
|
||||
|
||||
// 3. Change bind to DOGBINDER
|
||||
ItemStack dogBinder = new ItemStack(
|
||||
ModItems.getBind(BindVariant.DOGBINDER)
|
||||
);
|
||||
ItemStack dogBinder = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "dogbinder"));
|
||||
this.walkedPrisoner.equip(BodyRegionV2.ARMS, dogBinder);
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
@@ -738,7 +736,7 @@ public class KidnapperWalkPrisonerGoal extends Goal {
|
||||
if (currentBind.isEmpty() || !prisoner.isTiedUp()) {
|
||||
// They freed themselves - put dogbinder back on
|
||||
ItemStack dogBinder = new ItemStack(
|
||||
ModItems.getBind(BindVariant.DOGBINDER)
|
||||
DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "dogbinder")).getItem()
|
||||
);
|
||||
prisoner.equip(BodyRegionV2.ARMS, dogBinder);
|
||||
}
|
||||
|
||||
@@ -229,20 +229,21 @@ public class MaidDeliverCaptiveGoal extends Goal {
|
||||
kidnappedState.getEquipment(BodyRegionV2.NECK);
|
||||
if (
|
||||
!collar.isEmpty() &&
|
||||
collar.getItem() instanceof
|
||||
com.tiedup.remake.items.base.ItemCollar collarItem
|
||||
com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collar)
|
||||
) {
|
||||
for (java.util.UUID ownerId : new java.util.ArrayList<>(
|
||||
collarItem.getOwners(collar)
|
||||
com.tiedup.remake.v2.bondage.CollarHelper.getOwners(collar)
|
||||
)) {
|
||||
collarItem.removeOwner(collar, ownerId);
|
||||
com.tiedup.remake.v2.bondage.CollarHelper.removeOwner(collar, ownerId);
|
||||
}
|
||||
collarItem.addOwner(
|
||||
com.tiedup.remake.v2.bondage.CollarHelper.addOwner(
|
||||
collar,
|
||||
buyerEntity.getUUID(),
|
||||
buyerEntity.getName().getString()
|
||||
);
|
||||
collarItem.setLocked(collar, false);
|
||||
if (collar.getItem() instanceof com.tiedup.remake.items.base.ILockable lockable) {
|
||||
lockable.setLocked(collar, false);
|
||||
}
|
||||
kidnappedState.equip(BodyRegionV2.NECK, collar);
|
||||
|
||||
if (
|
||||
|
||||
@@ -338,10 +338,8 @@ public class MaidReturnGoal extends Goal {
|
||||
// Fallback: use basic rope if no snapshot
|
||||
cap.equip(
|
||||
BodyRegionV2.ARMS,
|
||||
new ItemStack(
|
||||
com.tiedup.remake.items.ModItems.getBind(
|
||||
com.tiedup.remake.items.base.BindVariant.ROPES
|
||||
)
|
||||
com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(
|
||||
new net.minecraft.resources.ResourceLocation("tiedup", "ropes")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.tiedup.remake.entities.ai.master;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.dialogue.DialogueBridge;
|
||||
import com.tiedup.remake.entities.EntityMaster;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.state.HumanChairHelper;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -394,8 +394,8 @@ public class MasterHumanChairGoal extends Goal {
|
||||
|
||||
// Apply invisible dogbind for the pose animation
|
||||
if (!bindState.isTiedUp()) {
|
||||
ItemStack dogbind = new ItemStack(
|
||||
ModItems.getBind(BindVariant.DOGBINDER)
|
||||
ItemStack dogbind = DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "dogbinder")
|
||||
);
|
||||
CompoundTag tag = dogbind.getOrCreateTag();
|
||||
tag.putBoolean(NBT_HUMAN_CHAIR_BIND, true);
|
||||
|
||||
@@ -3,12 +3,9 @@ package com.tiedup.remake.entities.ai.master;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.dialogue.DialogueBridge;
|
||||
import com.tiedup.remake.entities.EntityMaster;
|
||||
import com.tiedup.remake.items.ItemChokeCollar;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.items.base.BlindfoldVariant;
|
||||
import com.tiedup.remake.items.base.GagVariant;
|
||||
import com.tiedup.remake.items.base.MittensVariant;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.IV2BondageEquipment;
|
||||
@@ -261,7 +258,7 @@ public class MasterPunishGoal extends Goal {
|
||||
// CHOKE: only if pet has choke collar
|
||||
if (bindState != null && bindState.hasCollar()) {
|
||||
ItemStack collar = bindState.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemChokeCollar) {
|
||||
if (CollarHelper.isChokeCollar(collar)) {
|
||||
available.add(PunishmentType.CHOKE_COLLAR);
|
||||
}
|
||||
}
|
||||
@@ -393,8 +390,8 @@ public class MasterPunishGoal extends Goal {
|
||||
PlayerBindState bindState = PlayerBindState.getInstance(pet);
|
||||
if (bindState != null && bindState.hasCollar()) {
|
||||
ItemStack collar = bindState.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemChokeCollar chokeCollar) {
|
||||
chokeCollar.setChoking(collar, true);
|
||||
if (CollarHelper.isChokeCollar(collar)) {
|
||||
CollarHelper.setChoking(collar, true);
|
||||
this.activeChokeCollar = collar;
|
||||
this.chokeActiveTimer = 1;
|
||||
|
||||
@@ -457,12 +454,14 @@ public class MasterPunishGoal extends Goal {
|
||||
*/
|
||||
private ItemStack createAccessory(BodyRegionV2 region) {
|
||||
return switch (region) {
|
||||
case EYES -> new ItemStack(
|
||||
ModItems.getBlindfold(BlindfoldVariant.CLASSIC)
|
||||
case EYES -> DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "classic_blindfold")
|
||||
);
|
||||
case MOUTH -> new ItemStack(ModItems.getGag(GagVariant.BALL_GAG));
|
||||
case HANDS -> new ItemStack(
|
||||
ModItems.getMittens(MittensVariant.LEATHER)
|
||||
case MOUTH -> DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "ball_gag")
|
||||
);
|
||||
case HANDS -> DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "leather_mittens")
|
||||
);
|
||||
default -> ItemStack.EMPTY;
|
||||
};
|
||||
@@ -472,8 +471,8 @@ public class MasterPunishGoal extends Goal {
|
||||
* Apply armbinder as punishment.
|
||||
*/
|
||||
private void applyTighten(ServerPlayer pet) {
|
||||
ItemStack armbinder = new ItemStack(
|
||||
ModItems.getBind(BindVariant.ARMBINDER)
|
||||
ItemStack armbinder = DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "armbinder")
|
||||
);
|
||||
|
||||
// Mark as temporary
|
||||
@@ -545,9 +544,9 @@ public class MasterPunishGoal extends Goal {
|
||||
private void deactivateChoke() {
|
||||
if (
|
||||
!activeChokeCollar.isEmpty() &&
|
||||
activeChokeCollar.getItem() instanceof ItemChokeCollar chokeCollar
|
||||
CollarHelper.isChokeCollar(activeChokeCollar)
|
||||
) {
|
||||
chokeCollar.setChoking(activeChokeCollar, false);
|
||||
CollarHelper.setChoking(activeChokeCollar, false);
|
||||
}
|
||||
this.activeChokeCollar = ItemStack.EMPTY;
|
||||
this.chokeActiveTimer = 0;
|
||||
|
||||
@@ -3,11 +3,8 @@ package com.tiedup.remake.entities.ai.master;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.dialogue.DialogueBridge;
|
||||
import com.tiedup.remake.entities.EntityMaster;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.items.base.BlindfoldVariant;
|
||||
import com.tiedup.remake.items.base.GagVariant;
|
||||
import com.tiedup.remake.items.base.MittensVariant;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.IV2BondageEquipment;
|
||||
@@ -17,7 +14,6 @@ import java.util.List;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
@@ -249,15 +245,18 @@ public class MasterRandomEventGoal extends Goal {
|
||||
* Create a random accessory item for the given body region.
|
||||
*/
|
||||
private ItemStack createRandomAccessory(BodyRegionV2 region) {
|
||||
Item item = switch (region) {
|
||||
case EYES -> ModItems.getBlindfold(BlindfoldVariant.CLASSIC);
|
||||
case MOUTH -> ModItems.getGag(GagVariant.BALL_GAG);
|
||||
case HANDS -> ModItems.getMittens(MittensVariant.LEATHER);
|
||||
default -> null;
|
||||
return switch (region) {
|
||||
case EYES -> DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "classic_blindfold")
|
||||
);
|
||||
case MOUTH -> DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "ball_gag")
|
||||
);
|
||||
case HANDS -> DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "leather_mittens")
|
||||
);
|
||||
default -> ItemStack.EMPTY;
|
||||
};
|
||||
|
||||
if (item == null) return ItemStack.EMPTY;
|
||||
return new ItemStack(item);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,8 +303,8 @@ public class MasterRandomEventGoal extends Goal {
|
||||
// Put pet in dogbind if not already tied
|
||||
PlayerBindState bindState = PlayerBindState.getInstance(pet);
|
||||
if (bindState != null && !bindState.isTiedUp()) {
|
||||
ItemStack dogbind = new ItemStack(
|
||||
ModItems.getBind(BindVariant.DOGBINDER)
|
||||
ItemStack dogbind = DataDrivenBondageItem.createStack(
|
||||
new ResourceLocation("tiedup", "dogbinder")
|
||||
);
|
||||
bindState.equip(BodyRegionV2.ARMS, dogbind);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.tiedup.remake.entities.ai.master;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.dialogue.DialogueBridge;
|
||||
import com.tiedup.remake.entities.EntityMaster;
|
||||
import com.tiedup.remake.items.ItemChokeCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.util.MessageDispatcher;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -441,8 +441,8 @@ public class MasterTaskWatchGoal extends Goal {
|
||||
if (bindState != null && bindState.hasCollar()) {
|
||||
ItemStack collar = bindState.getEquipment(BodyRegionV2.NECK);
|
||||
|
||||
if (collar.getItem() instanceof ItemChokeCollar chokeCollar) {
|
||||
chokeCollar.setChoking(collar, true);
|
||||
if (CollarHelper.isChokeCollar(collar)) {
|
||||
CollarHelper.setChoking(collar, true);
|
||||
this.activeChokeCollar = collar;
|
||||
this.chokeTimer = CHOKE_DURATION;
|
||||
|
||||
@@ -475,9 +475,9 @@ public class MasterTaskWatchGoal extends Goal {
|
||||
private void deactivateChoke() {
|
||||
if (
|
||||
!activeChokeCollar.isEmpty() &&
|
||||
activeChokeCollar.getItem() instanceof ItemChokeCollar chokeCollar
|
||||
CollarHelper.isChokeCollar(activeChokeCollar)
|
||||
) {
|
||||
chokeCollar.setChoking(activeChokeCollar, false);
|
||||
CollarHelper.setChoking(activeChokeCollar, false);
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[MasterTaskWatchGoal] {} deactivated choke",
|
||||
master.getNpcName()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.tiedup.remake.entities.ai.personality;
|
||||
|
||||
import com.tiedup.remake.entities.EntityDamsel;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.personality.NpcCommand;
|
||||
import com.tiedup.remake.personality.ToolMode;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -506,7 +506,7 @@ public class NpcFollowCommandGoal extends Goal {
|
||||
if (dist <= ATTACK_RANGE) {
|
||||
// Try to capture using bind item
|
||||
ItemStack bindItem = npc.getMainHandItem();
|
||||
if (bindItem.getItem() instanceof ItemBind) {
|
||||
if (BindModeHelper.isBindItem(bindItem)) {
|
||||
// Apply bind to target
|
||||
captureTarget.equip(BodyRegionV2.ARMS, bindItem.copy());
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.tiedup.remake.entities.ai.personality;
|
||||
|
||||
import com.tiedup.remake.entities.EntityDamsel;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.personality.NpcCommand;
|
||||
import com.tiedup.remake.personality.PersonalityState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -426,7 +426,7 @@ public class NpcGuardCommandGoal extends Goal {
|
||||
NonNullList<ItemStack> inventory = npc.getNpcInventory();
|
||||
for (int i = 0; i < inventory.size(); i++) {
|
||||
ItemStack stack = inventory.get(i);
|
||||
if (stack.getItem() instanceof ItemBind) {
|
||||
if (BindModeHelper.isBindItem(stack)) {
|
||||
// Apply bind to slave
|
||||
slave.equip(BodyRegionV2.ARMS, stack.copy());
|
||||
stack.shrink(1);
|
||||
@@ -486,8 +486,8 @@ public class NpcGuardCommandGoal extends Goal {
|
||||
private UUID getCollarOwnerUUID(EntityDamsel slave) {
|
||||
if (!slave.hasCollar()) return null;
|
||||
ItemStack collar = slave.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
java.util.List<UUID> owners = collarItem.getOwners(collar);
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
java.util.List<UUID> owners = CollarHelper.getOwners(collar);
|
||||
if (!owners.isEmpty()) {
|
||||
return owners.get(0);
|
||||
}
|
||||
|
||||
@@ -305,11 +305,8 @@ public class NpcStruggleGoal extends Goal {
|
||||
ItemStack collar = npc.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.isEmpty()) return false;
|
||||
|
||||
if (
|
||||
collar.getItem() instanceof
|
||||
com.tiedup.remake.items.base.ItemCollar collarItem
|
||||
) {
|
||||
List<UUID> ownerUUIDs = collarItem.getOwners(collar);
|
||||
if (com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collar)) {
|
||||
List<UUID> ownerUUIDs = com.tiedup.remake.v2.bondage.CollarHelper.getOwners(collar);
|
||||
if (!ownerUUIDs.isEmpty()) {
|
||||
// Check if any owner is nearby
|
||||
List<Player> players = npc
|
||||
@@ -338,11 +335,8 @@ public class NpcStruggleGoal extends Goal {
|
||||
ItemStack collar = npc.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.isEmpty()) return null;
|
||||
|
||||
if (
|
||||
collar.getItem() instanceof
|
||||
com.tiedup.remake.items.base.ItemCollar collarItem
|
||||
) {
|
||||
List<UUID> ownerUUIDs = collarItem.getOwners(collar);
|
||||
if (com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collar)) {
|
||||
List<UUID> ownerUUIDs = com.tiedup.remake.v2.bondage.CollarHelper.getOwners(collar);
|
||||
if (!ownerUUIDs.isEmpty()) {
|
||||
return ownerUUIDs.get(0);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.tiedup.remake.entities.armorstand;
|
||||
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import com.tiedup.remake.v2.bondage.PoseTypeHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import java.util.Map;
|
||||
import net.minecraft.core.Rotations;
|
||||
@@ -185,11 +185,8 @@ public class ArmorStandBondageHelper {
|
||||
// Save original pose if not already saved
|
||||
saveOriginalPose(stand);
|
||||
|
||||
// Get pose type from bind
|
||||
PoseType poseType = PoseType.STANDARD;
|
||||
if (bindStack.getItem() instanceof ItemBind bind) {
|
||||
poseType = bind.getPoseType();
|
||||
}
|
||||
// Get pose type from bind (V2 data-driven)
|
||||
PoseType poseType = PoseTypeHelper.getPoseType(bindStack);
|
||||
|
||||
// Apply pose
|
||||
applyBondagePose(stand, poseType);
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.tiedup.remake.entities.damsel.components;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.AbstractTiedUpNpc;
|
||||
import com.tiedup.remake.entities.BondageServiceHandler;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.state.ICaptor;
|
||||
import com.tiedup.remake.state.IRestrainable;
|
||||
import com.tiedup.remake.state.IRestrainableEntity;
|
||||
@@ -565,9 +565,9 @@ public class DamselBondageManager implements IRestrainable {
|
||||
ItemStack collar = getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.isEmpty()) return false;
|
||||
|
||||
if (!(collar.getItem() instanceof ItemCollar itemCollar)) return false;
|
||||
if (!CollarHelper.isCollar(collar)) return false;
|
||||
|
||||
UUID cellId = itemCollar.getCellId(collar);
|
||||
UUID cellId = CollarHelper.getCellId(collar);
|
||||
if (cellId == null) return false;
|
||||
|
||||
// Get cell position from registry
|
||||
|
||||
@@ -2,7 +2,7 @@ package com.tiedup.remake.entities.damsel.components;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.dialogue.EntityDialogueManager;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.personality.*;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import java.util.*;
|
||||
@@ -180,8 +180,8 @@ public class DamselPersonalitySystem {
|
||||
UUID masterUUID = null;
|
||||
if (context.hasCollar()) {
|
||||
ItemStack collar = context.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
List<UUID> owners = collarItem.getOwners(collar);
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
List<UUID> owners = CollarHelper.getOwners(collar);
|
||||
if (!owners.isEmpty()) {
|
||||
masterUUID = owners.get(0);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,11 @@ import com.tiedup.remake.core.ModConfig;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.AbstractTiedUpNpc;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
||||
import com.tiedup.remake.v2.bondage.component.BlindingComponent;
|
||||
import com.tiedup.remake.state.IRestrainableEntity;
|
||||
import com.tiedup.remake.util.RestraintEffectUtils;
|
||||
import com.tiedup.remake.util.TiedUpSounds;
|
||||
@@ -112,19 +116,13 @@ public class NpcEquipmentManager {
|
||||
public boolean hasGaggingEffect() {
|
||||
ItemStack gag = getCurrentGag();
|
||||
if (gag.isEmpty()) return false;
|
||||
return (
|
||||
gag.getItem() instanceof
|
||||
com.tiedup.remake.items.base.IHasGaggingEffect
|
||||
);
|
||||
return DataDrivenBondageItem.getComponent(gag, ComponentType.GAGGING, GaggingComponent.class) != null;
|
||||
}
|
||||
|
||||
public boolean hasBlindingEffect() {
|
||||
ItemStack blindfold = getCurrentBlindfold();
|
||||
if (blindfold.isEmpty()) return false;
|
||||
return (
|
||||
blindfold.getItem() instanceof
|
||||
com.tiedup.remake.items.base.IHasBlindingEffect
|
||||
);
|
||||
return DataDrivenBondageItem.getComponent(blindfold, ComponentType.BLINDING, BlindingComponent.class) != null;
|
||||
}
|
||||
|
||||
public boolean hasKnives() {
|
||||
@@ -768,8 +766,8 @@ public class NpcEquipmentManager {
|
||||
public boolean isCollarOwner(Player player) {
|
||||
if (!hasCollar()) return true;
|
||||
ItemStack collar = getCurrentCollar();
|
||||
if (!(collar.getItem() instanceof ItemCollar collarItem)) return true;
|
||||
return collarItem.isOwner(collar, player);
|
||||
if (!CollarHelper.isCollar(collar)) return true;
|
||||
return CollarHelper.isOwner(collar, player);
|
||||
}
|
||||
|
||||
// COERCION
|
||||
|
||||
@@ -361,19 +361,19 @@ public class KidnapperAppearance {
|
||||
this.itemSelection = new KidnapperItemSelector.SelectionResult(
|
||||
this.currentTheme,
|
||||
this.themeColor,
|
||||
KidnapperItemSelector.createBind(
|
||||
this.currentTheme.getBind(),
|
||||
KidnapperItemSelector.createItemById(
|
||||
this.currentTheme.getBindId(),
|
||||
this.themeColor
|
||||
),
|
||||
KidnapperItemSelector.createGag(
|
||||
this.currentTheme.getPrimaryGag(),
|
||||
KidnapperItemSelector.createItemById(
|
||||
this.currentTheme.getPrimaryGagId(),
|
||||
this.themeColor
|
||||
),
|
||||
KidnapperItemSelector.createMittens(),
|
||||
KidnapperItemSelector.createEarplugs(),
|
||||
this.currentTheme.hasBlindfolds()
|
||||
? KidnapperItemSelector.createBlindfold(
|
||||
this.currentTheme.getPrimaryBlindfold(),
|
||||
? KidnapperItemSelector.createItemById(
|
||||
this.currentTheme.getPrimaryBlindfoldId(),
|
||||
this.themeColor
|
||||
)
|
||||
: ItemStack.EMPTY
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.dialogue.EntityDialogueManager;
|
||||
import com.tiedup.remake.entities.EntityKidnapper;
|
||||
import com.tiedup.remake.entities.ai.kidnapper.KidnapperState;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.prison.PrisonerManager;
|
||||
import com.tiedup.remake.prison.PrisonerRecord;
|
||||
import com.tiedup.remake.prison.PrisonerState;
|
||||
@@ -189,9 +189,9 @@ public class KidnapperCaptiveManager {
|
||||
// If target has collar, verify we own it
|
||||
if (target.hasCollar()) {
|
||||
ItemStack collar = target.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
// Check if THIS kidnapper is owner
|
||||
if (!collarItem.getOwners(collar).contains(host.getUUID())) {
|
||||
if (!CollarHelper.getOwners(collar).contains(host.getUUID())) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[KidnapperCaptiveManager] {} can't capture {} - collar owned by someone else",
|
||||
host.getNpcName(),
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.tiedup.remake.entities.EntityKidnapper;
|
||||
import com.tiedup.remake.entities.EntityKidnapperElite;
|
||||
import com.tiedup.remake.entities.ai.kidnapper.KidnapperState;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.prison.PrisonerManager;
|
||||
import com.tiedup.remake.prison.PrisonerRecord;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
@@ -206,8 +206,8 @@ public class KidnapperTargetSelector {
|
||||
// Self-collared entities (exploit) are treated as uncolllared — kidnappers ignore self-collars
|
||||
if (state.hasCollar()) {
|
||||
ItemStack collar = state.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
java.util.List<UUID> owners = collarItem.getOwners(collar);
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
java.util.List<UUID> owners = CollarHelper.getOwners(collar);
|
||||
// Filter out self-collar (owner == wearer = exploit)
|
||||
java.util.List<UUID> realOwners = owners
|
||||
.stream()
|
||||
@@ -312,8 +312,8 @@ public class KidnapperTargetSelector {
|
||||
// Other kidnappers' collars are fair game — kidnapper can steal from kidnapper
|
||||
if (state != null && state.hasCollar()) {
|
||||
ItemStack collar = state.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
java.util.List<UUID> owners = collarItem.getOwners(collar);
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
java.util.List<UUID> owners = CollarHelper.getOwners(collar);
|
||||
if (!owners.isEmpty() && !owners.contains(host.getUUID())) {
|
||||
// Check if any owner is a DIFFERENT player (not self-collared, not a kidnapper)
|
||||
if (host.level() instanceof ServerLevel sl) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.EntityMaster;
|
||||
import com.tiedup.remake.entities.ai.master.MasterRandomEventGoal;
|
||||
import com.tiedup.remake.entities.ai.master.MasterState;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.minigame.StruggleSessionManager;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
@@ -119,29 +119,34 @@ public class MasterPetManager {
|
||||
if (state == null) return;
|
||||
|
||||
// Create a choke collar for pet play
|
||||
ItemStack chokeCollar = new ItemStack(
|
||||
com.tiedup.remake.items.ModItems.CHOKE_COLLAR.get()
|
||||
ItemStack chokeCollar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(
|
||||
new net.minecraft.resources.ResourceLocation("tiedup", "choke_collar")
|
||||
);
|
||||
|
||||
// Configure for pet play BEFORE equipping
|
||||
if (
|
||||
chokeCollar.getItem() instanceof
|
||||
com.tiedup.remake.items.ItemChokeCollar collar
|
||||
com.tiedup.remake.items.base.IHasResistance resistable
|
||||
) {
|
||||
collar.setCurrentResistance(chokeCollar, PET_COLLAR_RESISTANCE);
|
||||
collar.setLocked(chokeCollar, true);
|
||||
collar.setLockable(chokeCollar, false); // Cannot be lockpicked
|
||||
collar.setCanBeStruggledOut(chokeCollar, true); // Can struggle, but very hard
|
||||
|
||||
// Add this Master as owner
|
||||
collar.addOwner(chokeCollar, master.getUUID(), master.getNpcName());
|
||||
|
||||
// Set NBT flag for pet play mode
|
||||
chokeCollar.getOrCreateTag().putBoolean("petPlayMode", true);
|
||||
chokeCollar
|
||||
.getOrCreateTag()
|
||||
.putUUID("masterUUID", master.getUUID());
|
||||
resistable.setCurrentResistance(chokeCollar, PET_COLLAR_RESISTANCE);
|
||||
resistable.setCanBeStruggledOut(chokeCollar, true); // Can struggle, but very hard
|
||||
}
|
||||
if (
|
||||
chokeCollar.getItem() instanceof
|
||||
com.tiedup.remake.items.base.ILockable lockable
|
||||
) {
|
||||
lockable.setLocked(chokeCollar, true);
|
||||
lockable.setLockable(chokeCollar, false); // Cannot be lockpicked
|
||||
}
|
||||
|
||||
// Add this Master as owner
|
||||
CollarHelper.addOwner(chokeCollar, master.getUUID(), master.getNpcName());
|
||||
|
||||
// Set NBT flag for pet play mode
|
||||
CollarHelper.setPetPlayMode(chokeCollar, true);
|
||||
chokeCollar
|
||||
.getOrCreateTag()
|
||||
.putUUID("masterUUID", master.getUUID());
|
||||
|
||||
// Replace any existing collar (force removal) with the choke collar
|
||||
state.replaceEquipment(BodyRegionV2.NECK, chokeCollar, true);
|
||||
@@ -186,9 +191,9 @@ public class MasterPetManager {
|
||||
}
|
||||
|
||||
// Unlock the collar
|
||||
if (collarStack.getItem() instanceof ItemCollar collar) {
|
||||
collar.setLocked(collarStack, false);
|
||||
collar.setLockable(collarStack, true);
|
||||
if (collarStack.getItem() instanceof com.tiedup.remake.items.base.ILockable lockable) {
|
||||
lockable.setLocked(collarStack, false);
|
||||
lockable.setLockable(collarStack, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,8 +247,8 @@ public class MasterPetManager {
|
||||
);
|
||||
if (collarStack.isEmpty()) return;
|
||||
|
||||
if (collarStack.getItem() instanceof ItemCollar collar) {
|
||||
collar.setCurrentResistance(collarStack, PET_COLLAR_RESISTANCE);
|
||||
if (collarStack.getItem() instanceof com.tiedup.remake.items.base.IHasResistance resistable) {
|
||||
resistable.setCurrentResistance(collarStack, PET_COLLAR_RESISTANCE);
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[MasterPetManager] Reset collar resistance for {}",
|
||||
pet.getName().getString()
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.tiedup.remake.cells.CampLifecycleManager;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.EntityMaid;
|
||||
import com.tiedup.remake.entities.EntitySlaveTrader;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -40,7 +40,7 @@ public class CampNpcProtectionHandler {
|
||||
|
||||
// Check if player is holding restraint item
|
||||
ItemStack heldItem = player.getItemInHand(event.getHand());
|
||||
if (!(heldItem.getItem() instanceof ItemBind)) return;
|
||||
if (!BindModeHelper.isBindItem(heldItem)) return;
|
||||
|
||||
// Check if target is trader or maid with active camp
|
||||
UUID campId = null;
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.tiedup.remake.events.captivity;
|
||||
import com.tiedup.remake.core.SettingsAccessor;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.LeashProxyEntity;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.state.IPlayerLeashAccess;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
@@ -146,9 +146,9 @@ public class PlayerEnslavementHandler {
|
||||
ItemStack collar = slaveKidnappedState.getEquipment(
|
||||
BodyRegionV2.NECK
|
||||
);
|
||||
if (collar.getItem() instanceof ItemCollar collarItem) {
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
if (
|
||||
collarItem
|
||||
CollarHelper
|
||||
.getOwners(collar)
|
||||
.contains(master.getUUID())
|
||||
) {
|
||||
|
||||
@@ -4,8 +4,8 @@ import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.EntityKidnapper;
|
||||
import com.tiedup.remake.entities.EntityMaid;
|
||||
import com.tiedup.remake.entities.EntitySlaveTrader;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.prison.LaborRecord;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.prison.PrisonerManager;
|
||||
import com.tiedup.remake.prison.PrisonerRecord;
|
||||
import com.tiedup.remake.prison.PrisonerState;
|
||||
@@ -145,7 +145,7 @@ public class LaborAttackPunishmentHandler {
|
||||
|
||||
// Check if player is holding a restraint item (rope, chain, etc.)
|
||||
ItemStack heldItem = serverPlayer.getItemInHand(hand);
|
||||
if (!(heldItem.getItem() instanceof ItemBind)) {
|
||||
if (!BindModeHelper.isBindItem(heldItem)) {
|
||||
return; // Not a restraint item
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ package com.tiedup.remake.events.restriction;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.EntityMaster;
|
||||
import com.tiedup.remake.items.ItemChokeCollar;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.blocks.PetBedManager;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -284,8 +284,8 @@ public class PetPlayRestrictionHandler {
|
||||
if (bindState == null || !bindState.hasCollar()) return;
|
||||
|
||||
ItemStack collar = bindState.getEquipment(BodyRegionV2.NECK);
|
||||
if (collar.getItem() instanceof ItemChokeCollar chokeCollar) {
|
||||
if (chokeCollar.isChoking(collar)) {
|
||||
if (CollarHelper.isChokeCollar(collar)) {
|
||||
if (CollarHelper.isChoking(collar)) {
|
||||
// Apply ChokeEffect (short duration, re-applied each active tick)
|
||||
if (
|
||||
!player.hasEffect(
|
||||
|
||||
@@ -3,8 +3,8 @@ package com.tiedup.remake.events.restriction;
|
||||
import com.tiedup.remake.core.SettingsAccessor;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.EntityKidnapper;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.minigame.StruggleSessionManager;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.network.personality.PacketSlaveBeingFreed;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
@@ -642,14 +642,11 @@ public class RestraintTaskTickHandler {
|
||||
if (!(slave.level() instanceof ServerLevel serverLevel)) return;
|
||||
|
||||
ItemStack collar = slave.getEquipment(BodyRegionV2.NECK);
|
||||
if (
|
||||
collar.isEmpty() ||
|
||||
!(collar.getItem() instanceof ItemCollar collarItem)
|
||||
) {
|
||||
if (collar.isEmpty() || !CollarHelper.isCollar(collar)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<UUID> owners = collarItem.getOwners(collar);
|
||||
List<UUID> owners = CollarHelper.getOwners(collar);
|
||||
if (owners.isEmpty()) return;
|
||||
|
||||
// Create alert packet
|
||||
|
||||
@@ -40,7 +40,11 @@ public class AnvilEventHandler {
|
||||
|
||||
// Check if item can have a padlock attached (tape, slime, vine, web cannot)
|
||||
if (!lockable.canAttachPadlock()) {
|
||||
return; // Item type cannot have padlock
|
||||
return; // Item type cannot have padlock (V1)
|
||||
}
|
||||
// V2 data-driven items: check definition's can_attach_padlock field
|
||||
if (!com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.canAttachPadlockTo(left)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Item must not already have a padlock attached
|
||||
|
||||
@@ -4,9 +4,11 @@ import com.tiedup.remake.core.SettingsAccessor;
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.dialogue.GagTalkManager;
|
||||
import com.tiedup.remake.items.base.ItemGag;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.util.GagMaterial;
|
||||
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import com.tiedup.remake.util.TiedUpUtils;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -54,12 +56,19 @@ public class ChatEventHandler {
|
||||
BodyRegionV2.MOUTH
|
||||
);
|
||||
|
||||
if (
|
||||
!gagStack.isEmpty() &&
|
||||
gagStack.getItem() instanceof ItemGag gagItem
|
||||
) {
|
||||
// V2: check gagging component
|
||||
GaggingComponent gaggingComp = DataDrivenBondageItem.getComponent(
|
||||
gagStack, ComponentType.GAGGING, GaggingComponent.class);
|
||||
boolean isGagItem = gaggingComp != null;
|
||||
|
||||
if (!gagStack.isEmpty() && isGagItem) {
|
||||
String originalMessage = event.getRawText();
|
||||
GagMaterial material = gagItem.getGagMaterial();
|
||||
// V2: get material from component, V1 fallback: from ItemGag
|
||||
GagMaterial material = null;
|
||||
if (gaggingComp != null) {
|
||||
material = gaggingComp.getMaterial();
|
||||
}
|
||||
// material stays null if no component; GagTalkManager handles null → CLOTH fallback
|
||||
|
||||
// 1. Process the message through our GagTalkManager V2
|
||||
Component muffledMessage = GagTalkManager.processGagMessage(
|
||||
@@ -83,7 +92,9 @@ public class ChatEventHandler {
|
||||
.append("> ")
|
||||
.append(muffledMessage);
|
||||
|
||||
double range = material.getTalkRange();
|
||||
double range = material != null
|
||||
? material.getTalkRange()
|
||||
: (gaggingComp != null ? gaggingComp.getRange() : 10.0);
|
||||
|
||||
List<ServerPlayer> nearbyPlayers =
|
||||
TiedUpUtils.getPlayersAround(
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.items.base.BindVariant;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.PoseType;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
/**
|
||||
* Generic bind item created from BindVariant enum.
|
||||
* Replaces individual bind classes (ItemRopes, ItemChain, ItemStraitjacket, etc.)
|
||||
*
|
||||
* Factory pattern: All bind variants are created using this single class.
|
||||
*/
|
||||
public class GenericBind extends ItemBind {
|
||||
|
||||
private final BindVariant variant;
|
||||
|
||||
public GenericBind(BindVariant variant) {
|
||||
super(new Item.Properties().stacksTo(16));
|
||||
this.variant = variant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemName() {
|
||||
return variant.getItemName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PoseType getPoseType() {
|
||||
return variant.getPoseType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variant this bind was created from.
|
||||
*/
|
||||
public BindVariant getVariant() {
|
||||
return variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default resistance value for this bind variant.
|
||||
* Note: Actual resistance is managed by GameRules, this is just the configured default.
|
||||
*/
|
||||
public int getDefaultResistance() {
|
||||
return variant.getResistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this bind can have a padlock attached via anvil.
|
||||
* Adhesive (tape) and organic (slime, vine, web) binds cannot have padlocks.
|
||||
*/
|
||||
@Override
|
||||
public boolean canAttachPadlock() {
|
||||
return switch (variant) {
|
||||
case DUCT_TAPE, SLIME, VINE_SEED, WEB_BIND -> false;
|
||||
default -> true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this bind variant.
|
||||
* Issue #12 fix: Eliminates string checks in renderers.
|
||||
*/
|
||||
@Override
|
||||
public String getTextureSubfolder() {
|
||||
return variant.getTextureSubfolder();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.items.base.BlindfoldVariant;
|
||||
import com.tiedup.remake.items.base.ItemBlindfold;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
/**
|
||||
* Generic blindfold item created from BlindfoldVariant enum.
|
||||
* Replaces individual blindfold classes (ItemClassicBlindfold, ItemBlindfoldMask).
|
||||
*
|
||||
* Factory pattern: All blindfold variants are created using this single class.
|
||||
*/
|
||||
public class GenericBlindfold extends ItemBlindfold {
|
||||
|
||||
private final BlindfoldVariant variant;
|
||||
|
||||
public GenericBlindfold(BlindfoldVariant variant) {
|
||||
super(new Item.Properties().stacksTo(16));
|
||||
this.variant = variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variant this blindfold was created from.
|
||||
*/
|
||||
public BlindfoldVariant getVariant() {
|
||||
return variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this blindfold variant.
|
||||
* Issue #12 fix: Eliminates string checks in renderers.
|
||||
*/
|
||||
@Override
|
||||
public String getTextureSubfolder() {
|
||||
return variant.getTextureSubfolder();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.items.base.EarplugsVariant;
|
||||
import com.tiedup.remake.items.base.ItemEarplugs;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
/**
|
||||
* Generic earplugs item created from EarplugsVariant enum.
|
||||
* Replaces individual earplugs classes (ItemClassicEarplugs).
|
||||
*
|
||||
* Factory pattern: All earplugs variants are created using this single class.
|
||||
*/
|
||||
public class GenericEarplugs extends ItemEarplugs {
|
||||
|
||||
private final EarplugsVariant variant;
|
||||
|
||||
public GenericEarplugs(EarplugsVariant variant) {
|
||||
super(new Item.Properties().stacksTo(16));
|
||||
this.variant = variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variant this earplugs was created from.
|
||||
*/
|
||||
public EarplugsVariant getVariant() {
|
||||
return variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this earplugs variant.
|
||||
* Issue #12 fix: Eliminates string checks in renderers.
|
||||
*/
|
||||
@Override
|
||||
public String getTextureSubfolder() {
|
||||
return variant.getTextureSubfolder();
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.items.base.GagVariant;
|
||||
import com.tiedup.remake.items.base.ItemGag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Generic gag item created from GagVariant enum.
|
||||
* Replaces individual gag classes (ItemBallGag, ItemTapeGag, etc.)
|
||||
*
|
||||
* Factory pattern: All gag variants are created using this single class.
|
||||
*
|
||||
* Note: ItemMedicalGag is NOT handled by this class because it implements
|
||||
* IHasBlindingEffect (combo item with special behavior).
|
||||
*/
|
||||
public class GenericGag extends ItemGag {
|
||||
|
||||
private final GagVariant variant;
|
||||
|
||||
public GenericGag(GagVariant variant) {
|
||||
super(new Item.Properties().stacksTo(16), variant.getMaterial());
|
||||
this.variant = variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variant this gag was created from.
|
||||
*/
|
||||
public GagVariant getVariant() {
|
||||
return variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this gag can have a padlock attached via anvil.
|
||||
* Adhesive (tape) and organic (slime, vine, web) gags cannot have padlocks.
|
||||
*/
|
||||
@Override
|
||||
public boolean canAttachPadlock() {
|
||||
return switch (variant) {
|
||||
case TAPE_GAG, SLIME_GAG, VINE_GAG, WEB_GAG -> false;
|
||||
default -> true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this gag variant.
|
||||
* Issue #12 fix: Eliminates string checks in renderers.
|
||||
*/
|
||||
@Override
|
||||
public String getTextureSubfolder() {
|
||||
return variant.getTextureSubfolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this gag uses a 3D OBJ model.
|
||||
*/
|
||||
@Override
|
||||
public boolean uses3DModel() {
|
||||
return variant.uses3DModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the 3D model location for this gag.
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public ResourceLocation get3DModelLocation() {
|
||||
String path = variant.getModelPath();
|
||||
return path != null ? ResourceLocation.tryParse(path) : null;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package com.tiedup.remake.items;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.base.IKnife;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.items.base.ItemBind;
|
||||
import com.tiedup.remake.items.base.KnifeVariant;
|
||||
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
||||
import com.tiedup.remake.network.sync.SyncManager;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
@@ -306,10 +306,7 @@ public class GenericKnife extends Item implements IKnife {
|
||||
player,
|
||||
BodyRegionV2.ARMS
|
||||
);
|
||||
if (
|
||||
bindStack.isEmpty() ||
|
||||
!(bindStack.getItem() instanceof ItemBind bind)
|
||||
) {
|
||||
if (bindStack.isEmpty() || !BindModeHelper.isBindItem(bindStack)) {
|
||||
player.stopUsingItem();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.items.base.ItemMittens;
|
||||
import com.tiedup.remake.items.base.MittensVariant;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
/**
|
||||
* Generic mittens item created from MittensVariant enum.
|
||||
*
|
||||
* Factory pattern: All mittens variants are created using this single class.
|
||||
*
|
||||
*/
|
||||
public class GenericMittens extends ItemMittens {
|
||||
|
||||
private final MittensVariant variant;
|
||||
|
||||
public GenericMittens(MittensVariant variant) {
|
||||
super(new Item.Properties().stacksTo(16));
|
||||
this.variant = variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variant this mittens was created from.
|
||||
*/
|
||||
public MittensVariant getVariant() {
|
||||
return variant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this mittens variant.
|
||||
* Issue #12 fix: Eliminates string checks in renderers.
|
||||
*/
|
||||
@Override
|
||||
public String getTextureSubfolder() {
|
||||
return variant.getTextureSubfolder();
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.items.bondage3d.IHas3DModelConfig;
|
||||
import com.tiedup.remake.items.bondage3d.Model3DConfig;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Choke Collar - Pet play collar used by Masters.
|
||||
*
|
||||
* <p>Special feature: Can be put in "choke mode" which applies a drowning effect.</p>
|
||||
* <p>Used by Masters for punishment. The effect simulates choking by reducing air supply,
|
||||
* which triggers drowning damage if left active for too long.</p>
|
||||
*
|
||||
* <p><b>Mechanics:</b></p>
|
||||
* <ul>
|
||||
* <li>When choking is active, the wearer's air supply decreases rapidly</li>
|
||||
* <li>This creates the drowning effect (damage and bubble particles)</li>
|
||||
* <li>Masters should deactivate the choke before the pet dies</li>
|
||||
* <li>The choke is controlled by the Master's punishment system</li>
|
||||
* </ul>
|
||||
*
|
||||
* @see com.tiedup.remake.entities.ai.master.MasterPunishGoal
|
||||
* @see com.tiedup.remake.events.restriction.PetPlayRestrictionHandler
|
||||
*/
|
||||
public class ItemChokeCollar extends ItemCollar implements IHas3DModelConfig {
|
||||
|
||||
private static final String NBT_CHOKING = "choking";
|
||||
|
||||
private static final Model3DConfig CONFIG = new Model3DConfig(
|
||||
"tiedup:models/obj/choke_collar_leather/model.obj",
|
||||
"tiedup:models/obj/choke_collar_leather/texture.png",
|
||||
0.0f,
|
||||
1.47f,
|
||||
0.0f, // Collar band centered at neck level
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
180.0f, // Flip Y to match rendering convention
|
||||
Set.of()
|
||||
);
|
||||
|
||||
public ItemChokeCollar() {
|
||||
super(new Item.Properties());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getItemName() {
|
||||
return "choke_collar";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if choke mode is active.
|
||||
*
|
||||
* @param stack The collar ItemStack
|
||||
* @return true if choking is active
|
||||
*/
|
||||
public boolean isChoking(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
return tag != null && tag.getBoolean(NBT_CHOKING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set choke mode on/off.
|
||||
* When active, applies drowning effect to wearer (handled by PetPlayRestrictionHandler).
|
||||
*
|
||||
* @param stack The collar ItemStack
|
||||
* @param choking true to activate choking, false to deactivate
|
||||
*/
|
||||
public void setChoking(ItemStack stack, boolean choking) {
|
||||
stack.getOrCreateTag().putBoolean(NBT_CHOKING, choking);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if collar is in pet play mode (from Master).
|
||||
*
|
||||
* @param stack The collar ItemStack
|
||||
* @return true if this is a pet play collar
|
||||
*/
|
||||
public boolean isPetPlayMode(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
return tag != null && tag.getBoolean("petPlayMode");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(
|
||||
ItemStack stack,
|
||||
@Nullable Level level,
|
||||
List<Component> tooltip,
|
||||
TooltipFlag flag
|
||||
) {
|
||||
super.appendHoverText(stack, level, tooltip, flag);
|
||||
|
||||
// Show choke status
|
||||
if (isChoking(stack)) {
|
||||
tooltip.add(
|
||||
Component.literal("CHOKING ACTIVE!")
|
||||
.withStyle(ChatFormatting.DARK_RED)
|
||||
.withStyle(ChatFormatting.BOLD)
|
||||
);
|
||||
}
|
||||
|
||||
// Show pet play mode status
|
||||
if (isPetPlayMode(stack)) {
|
||||
tooltip.add(
|
||||
Component.literal("Pet Play Mode").withStyle(
|
||||
ChatFormatting.LIGHT_PURPLE
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Description
|
||||
tooltip.add(
|
||||
Component.literal("A special collar used for pet play punishment")
|
||||
.withStyle(ChatFormatting.DARK_GRAY)
|
||||
.withStyle(ChatFormatting.ITALIC)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Choke collar cannot shock like shock collar.
|
||||
*/
|
||||
@Override
|
||||
public boolean canShock() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3D Model Support
|
||||
|
||||
@Override
|
||||
public boolean uses3DModel() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation get3DModelLocation() {
|
||||
return ResourceLocation.tryParse(CONFIG.objPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Model3DConfig getModelConfig() {
|
||||
return CONFIG;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
/**
|
||||
* Classic Collar - Basic collar item
|
||||
* Standard collar for marking ownership.
|
||||
*
|
||||
* Based on original ItemCollar from 1.12.2
|
||||
* Note: Collars have maxStackSize of 1 (unique items)
|
||||
*/
|
||||
public class ItemClassicCollar extends ItemCollar {
|
||||
|
||||
public ItemClassicCollar() {
|
||||
super(
|
||||
new Item.Properties()
|
||||
// stacksTo(1) is set by ItemCollar base class
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import com.tiedup.remake.cells.CellRegistryV2;
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.EntityDamsel;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.network.personality.PacketOpenCommandWandScreen;
|
||||
import com.tiedup.remake.personality.HomeType;
|
||||
import com.tiedup.remake.personality.JobExperience;
|
||||
@@ -349,11 +349,11 @@ public class ItemCommandWand extends Item {
|
||||
|
||||
// Get collar and verify ownership
|
||||
ItemStack collar = damsel.getEquipment(BodyRegionV2.NECK);
|
||||
if (!(collar.getItem() instanceof ItemCollar collarItem)) {
|
||||
if (!CollarHelper.isCollar(collar)) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
if (!collarItem.getOwners(collar).contains(player.getUUID())) {
|
||||
if (!CollarHelper.isOwner(collar, player)) {
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
SystemMessageManager.MessageCategory.ERROR,
|
||||
|
||||
@@ -1,369 +0,0 @@
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
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;
|
||||
@@ -91,13 +91,10 @@ public class ItemGpsLocator extends ItemOwnerTarget {
|
||||
ItemStack collarStack = targetState.getEquipment(
|
||||
BodyRegionV2.NECK
|
||||
);
|
||||
if (
|
||||
collarStack.getItem() instanceof
|
||||
ItemGpsCollar collarItem
|
||||
) {
|
||||
if (CollarHelper.hasGPS(collarStack)) {
|
||||
if (
|
||||
collarItem.isOwner(collarStack, player) ||
|
||||
collarItem.hasPublicTracking(collarStack)
|
||||
CollarHelper.isOwner(collarStack, player) ||
|
||||
CollarHelper.hasPublicTracking(collarStack)
|
||||
) {
|
||||
// Check if same dimension
|
||||
boolean sameDimension = player
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.items.base.IHasGaggingEffect;
|
||||
import com.tiedup.remake.items.base.ItemBlindfold;
|
||||
import com.tiedup.remake.util.GagMaterial;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
/**
|
||||
* Hood - Covers the head completely
|
||||
* Combines blindfold effect with gagging effect.
|
||||
*
|
||||
* Extends ItemBlindfold for slot behavior, implements IHasGaggingEffect for speech muffling.
|
||||
*/
|
||||
public class ItemHood extends ItemBlindfold implements IHasGaggingEffect {
|
||||
|
||||
private final GagMaterial gagMaterial;
|
||||
|
||||
public ItemHood() {
|
||||
super(new Item.Properties().stacksTo(16));
|
||||
this.gagMaterial = GagMaterial.STUFFED; // Hoods muffle speech like stuffed gags
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gag material type for speech conversion.
|
||||
* @return The gag material (STUFFED for hoods)
|
||||
*/
|
||||
public GagMaterial getGagMaterial() {
|
||||
return gagMaterial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextureSubfolder() {
|
||||
return "hoods";
|
||||
}
|
||||
}
|
||||
@@ -259,13 +259,10 @@ public class ItemKey extends ItemOwnerTarget {
|
||||
ItemStack collarStack = targetState.getEquipment(BodyRegionV2.NECK);
|
||||
if (collarStack.isEmpty()) return;
|
||||
|
||||
if (
|
||||
collarStack.getItem() instanceof
|
||||
com.tiedup.remake.items.base.ItemCollar collar
|
||||
) {
|
||||
if (com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collarStack)) {
|
||||
// Add player as owner to the collar (if not already)
|
||||
if (!collar.getOwners(collarStack).contains(player.getUUID())) {
|
||||
collar.addOwner(collarStack, player);
|
||||
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);
|
||||
|
||||
@@ -332,15 +332,12 @@ public class ItemLockpick extends Item {
|
||||
);
|
||||
if (collar.isEmpty()) return;
|
||||
|
||||
if (
|
||||
collar.getItem() instanceof
|
||||
com.tiedup.remake.items.ItemShockCollar shockCollar
|
||||
) {
|
||||
if (com.tiedup.remake.v2.bondage.CollarHelper.canShock(collar)) {
|
||||
// Shock the player
|
||||
state.shockKidnapped(" (Failed lockpick attempt)", 2.0f);
|
||||
|
||||
// Notify owners
|
||||
notifyOwnersLockpickAttempt(player, collar, shockCollar);
|
||||
notifyOwnersLockpickAttempt(player, collar);
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[LOCKPICK] {} was shocked for failed lockpick attempt",
|
||||
@@ -354,8 +351,7 @@ public class ItemLockpick extends Item {
|
||||
*/
|
||||
private static void notifyOwnersLockpickAttempt(
|
||||
Player player,
|
||||
ItemStack collar,
|
||||
com.tiedup.remake.items.ItemShockCollar shockCollar
|
||||
ItemStack collar
|
||||
) {
|
||||
if (player.getServer() == null) return;
|
||||
|
||||
@@ -367,7 +363,7 @@ public class ItemLockpick extends Item {
|
||||
).withStyle(ChatFormatting.GOLD)
|
||||
);
|
||||
|
||||
List<UUID> owners = shockCollar.getOwners(collar);
|
||||
List<UUID> owners = com.tiedup.remake.v2.bondage.CollarHelper.getOwners(collar);
|
||||
for (UUID ownerId : owners) {
|
||||
ServerPlayer owner = player
|
||||
.getServer()
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.items.base.IHasBlindingEffect;
|
||||
import com.tiedup.remake.items.base.ItemGag;
|
||||
import com.tiedup.remake.util.GagMaterial;
|
||||
import net.minecraft.world.item.Item;
|
||||
|
||||
/**
|
||||
* Medical Gag - Full face medical restraint
|
||||
* Combines gag effect with blinding effect.
|
||||
*
|
||||
* Extends ItemGag for slot behavior, implements IHasBlindingEffect for vision obstruction.
|
||||
*/
|
||||
public class ItemMedicalGag extends ItemGag implements IHasBlindingEffect {
|
||||
|
||||
public ItemMedicalGag() {
|
||||
super(new Item.Properties().stacksTo(16), GagMaterial.PANEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTextureSubfolder() {
|
||||
return "straps";
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import java.util.List;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Shock Collar - Advanced collar that can be remotely triggered.
|
||||
*
|
||||
* <p>Mechanics:</p>
|
||||
* <ul>
|
||||
* <li><b>Remote Shocking:</b> Can be triggered by anyone holding a linked Shocker Controller.</li>
|
||||
* <li><b>Struggle Penalty:</b> If locked, has a chance to shock the wearer during struggle attempts, interrupting them.</li>
|
||||
* <li><b>Public Mode:</b> Can be set to public mode, allowing anyone to shock the wearer even if they aren't the owner.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class ItemShockCollar extends ItemCollar {
|
||||
|
||||
private static final String NBT_PUBLIC_MODE = "public_mode";
|
||||
|
||||
public ItemShockCollar() {
|
||||
super(new Item.Properties());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShock() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows current mode (PUBLIC/PRIVATE) and usage instructions in 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("Shock Feature: ")
|
||||
.withStyle(ChatFormatting.YELLOW)
|
||||
.append(
|
||||
Component.literal(
|
||||
isPublic(stack) ? "PUBLIC" : "PRIVATE"
|
||||
).withStyle(
|
||||
isPublic(stack)
|
||||
? ChatFormatting.GREEN
|
||||
: ChatFormatting.RED
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
tooltip.add(
|
||||
Component.literal("Shift + Right-click to toggle public mode")
|
||||
.withStyle(ChatFormatting.DARK_GRAY)
|
||||
.withStyle(ChatFormatting.ITALIC)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles Public mode when shift-right-clicking in air.
|
||||
*/
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(
|
||||
Level level,
|
||||
Player player,
|
||||
InteractionHand hand
|
||||
) {
|
||||
ItemStack stack = player.getItemInHand(hand);
|
||||
|
||||
if (player.isShiftKeyDown()) {
|
||||
if (!level.isClientSide) {
|
||||
boolean newState = !isPublic(stack);
|
||||
setPublic(stack, newState);
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
SystemMessageManager.MessageCategory.SHOCKER_MODE_SET,
|
||||
(newState ? "PUBLIC" : "PRIVATE")
|
||||
);
|
||||
}
|
||||
return InteractionResultHolder.sidedSuccess(
|
||||
stack,
|
||||
level.isClientSide()
|
||||
);
|
||||
}
|
||||
|
||||
return super.use(level, player, hand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the risk of shocking the wearer during a struggle attempt.
|
||||
*
|
||||
* NOTE: For the new continuous struggle mini-game, shock logic is handled
|
||||
* directly in MiniGameSessionManager.tickContinuousSessions(). This method
|
||||
* is now a no-op that always returns true, kept for API compatibility.
|
||||
*
|
||||
* @param entity The wearer of the collar
|
||||
* @param stack The collar instance
|
||||
* @return Always true (shock logic moved to MiniGameSessionManager)
|
||||
*/
|
||||
public boolean notifyStruggle(LivingEntity entity, ItemStack stack) {
|
||||
// Shock collar checks during continuous struggle are now handled by
|
||||
// MiniGameSessionManager.shouldTriggerShock() with 10% chance every 5 seconds.
|
||||
// This method is kept for backwards compatibility but no longer performs the check.
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isPublic(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
return tag != null && tag.getBoolean(NBT_PUBLIC_MODE);
|
||||
}
|
||||
|
||||
public void setPublic(ItemStack stack, boolean publicMode) {
|
||||
stack.getOrCreateTag().putBoolean(NBT_PUBLIC_MODE, publicMode);
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.state.IRestrainable;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Automatic Shock Collar - Shocks the wearer at regular intervals.
|
||||
*
|
||||
*
|
||||
* <p>Mechanics:</p>
|
||||
* <ul>
|
||||
* <li><b>Self-Triggering:</b> Has an internal timer stored in NBT that shocks the entity when it reaches 0.</li>
|
||||
* <li><b>Unstruggable:</b> By default, cannot be escaped via struggle mechanics (requires key).</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class ItemShockCollarAuto extends ItemShockCollar {
|
||||
|
||||
private final int interval;
|
||||
|
||||
/**
|
||||
* @param interval Frequency of shocks in TICKS (20 ticks = 1 second).
|
||||
*/
|
||||
public ItemShockCollarAuto() {
|
||||
this(600); // 30 seconds default
|
||||
}
|
||||
|
||||
public ItemShockCollarAuto(int interval) {
|
||||
super();
|
||||
this.interval = interval;
|
||||
}
|
||||
|
||||
public int getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the internal shock timer is cleaned up when the item is removed.
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void onUnequipped(ItemStack stack, LivingEntity entity) {
|
||||
IRestrainable state = KidnappedHelper.getKidnappedState(entity);
|
||||
if (state != null) {
|
||||
state.resetAutoShockTimer();
|
||||
}
|
||||
super.onUnequipped(stack, entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents escaping through struggle mechanics for this specific collar type.
|
||||
*/
|
||||
@Override
|
||||
public boolean canBeStruggledOut(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package com.tiedup.remake.items;
|
||||
import com.tiedup.remake.core.ModSounds;
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.items.base.ItemOwnerTarget;
|
||||
import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.state.IRestrainable;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
@@ -77,12 +77,11 @@ public class ItemShockerController extends ItemOwnerTarget {
|
||||
BodyRegionV2.NECK
|
||||
);
|
||||
if (
|
||||
collar.getItem() instanceof
|
||||
ItemCollar collarItem &&
|
||||
collarItem.hasNickname(collar)
|
||||
CollarHelper.isCollar(collar) &&
|
||||
CollarHelper.hasNickname(collar)
|
||||
) {
|
||||
displayName =
|
||||
collarItem.getNickname(collar) +
|
||||
CollarHelper.getNickname(collar) +
|
||||
" (" +
|
||||
displayName +
|
||||
")";
|
||||
@@ -330,12 +329,10 @@ public class ItemShockerController extends ItemOwnerTarget {
|
||||
IRestrainable state = KidnappedHelper.getKidnappedState(entity);
|
||||
if (state != null && state.hasCollar()) {
|
||||
ItemStack collarStack = state.getEquipment(BodyRegionV2.NECK);
|
||||
if (
|
||||
collarStack.getItem() instanceof ItemShockCollar collarItem
|
||||
) {
|
||||
if (CollarHelper.canShock(collarStack)) {
|
||||
if (
|
||||
collarItem.getOwners(collarStack).contains(ownerId) ||
|
||||
collarItem.isPublic(collarStack)
|
||||
CollarHelper.isOwner(collarStack, ownerId) ||
|
||||
CollarHelper.isPublicShock(collarStack)
|
||||
) {
|
||||
targets.add(entity);
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@ package com.tiedup.remake.items;
|
||||
|
||||
import com.tiedup.remake.blocks.ModBlocks;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.KidnapperItemSelector;
|
||||
import com.tiedup.remake.items.base.*;
|
||||
import com.tiedup.remake.v2.V2Items;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
@@ -16,7 +18,7 @@ import net.minecraftforge.registries.RegistryObject;
|
||||
* Creative Mode Tabs Registration
|
||||
* Defines the creative inventory tabs where TiedUp items will appear.
|
||||
*
|
||||
* Updated to use factory pattern with enum-based item registration.
|
||||
* All bondage items are now sourced from DataDrivenItemRegistry.
|
||||
*/
|
||||
@SuppressWarnings("null") // Minecraft API guarantees non-null returns
|
||||
public class ModCreativeTabs {
|
||||
@@ -28,126 +30,27 @@ public class ModCreativeTabs {
|
||||
CREATIVE_MODE_TABS.register("tiedup_tab", () ->
|
||||
CreativeModeTab.builder()
|
||||
.title(Component.translatable("itemGroup.tiedup"))
|
||||
.icon(() -> new ItemStack(ModItems.getBind(BindVariant.ROPES)))
|
||||
.icon(() -> {
|
||||
// Use first data-driven item as icon, or fallback to whip
|
||||
var allDefs = DataDrivenItemRegistry.getAll();
|
||||
if (!allDefs.isEmpty()) {
|
||||
return DataDrivenBondageItem.createStack(allDefs.iterator().next().id());
|
||||
}
|
||||
return new ItemStack(ModItems.WHIP.get());
|
||||
})
|
||||
.displayItems((parameters, output) -> {
|
||||
// ========== BINDS (from enum) ==========
|
||||
for (BindVariant variant : BindVariant.values()) {
|
||||
// Add base item
|
||||
output.accept(ModItems.getBind(variant));
|
||||
|
||||
// Add colored variants if supported
|
||||
if (variant.supportsColor()) {
|
||||
for (ItemColor color : ItemColor.values()) {
|
||||
// Skip special colors (caution, clear) except for duct tape
|
||||
if (
|
||||
color.isSpecial() &&
|
||||
variant != BindVariant.DUCT_TAPE
|
||||
) continue;
|
||||
// Use validation method to check if color has texture
|
||||
if (
|
||||
KidnapperItemSelector.isColorValidForBind(
|
||||
color,
|
||||
variant
|
||||
)
|
||||
) {
|
||||
output.accept(
|
||||
KidnapperItemSelector.createBind(
|
||||
variant,
|
||||
color
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// ========== DATA-DRIVEN BONDAGE ITEMS ==========
|
||||
// All binds, gags, blindfolds, earplugs, mittens, collars,
|
||||
// hood, medical gag, etc. are now data-driven
|
||||
for (DataDrivenItemDefinition def : DataDrivenItemRegistry.getAll()) {
|
||||
output.accept(
|
||||
DataDrivenBondageItem.createStack(def.id())
|
||||
);
|
||||
}
|
||||
|
||||
// ========== GAGS (from enum) ==========
|
||||
for (GagVariant variant : GagVariant.values()) {
|
||||
// Add base item
|
||||
output.accept(ModItems.getGag(variant));
|
||||
|
||||
// Add colored variants if supported
|
||||
if (variant.supportsColor()) {
|
||||
for (ItemColor color : ItemColor.values()) {
|
||||
// Skip special colors (caution, clear) except for tape gag
|
||||
if (
|
||||
color.isSpecial() &&
|
||||
variant != GagVariant.TAPE_GAG
|
||||
) continue;
|
||||
// Use validation method to check if color has texture
|
||||
if (
|
||||
KidnapperItemSelector.isColorValidForGag(
|
||||
color,
|
||||
variant
|
||||
)
|
||||
) {
|
||||
output.accept(
|
||||
KidnapperItemSelector.createGag(
|
||||
variant,
|
||||
color
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== BLINDFOLDS (from enum) ==========
|
||||
for (BlindfoldVariant variant : BlindfoldVariant.values()) {
|
||||
// Add base item
|
||||
output.accept(ModItems.getBlindfold(variant));
|
||||
|
||||
// Add colored variants if supported
|
||||
if (variant.supportsColor()) {
|
||||
for (ItemColor color : ItemColor.values()) {
|
||||
// Skip special colors for blindfolds
|
||||
if (color.isSpecial()) continue;
|
||||
// Use validation method to check if color has texture
|
||||
if (
|
||||
KidnapperItemSelector.isColorValidForBlindfold(
|
||||
color,
|
||||
variant
|
||||
)
|
||||
) {
|
||||
output.accept(
|
||||
KidnapperItemSelector.createBlindfold(
|
||||
variant,
|
||||
color
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hood (combo item, not in enum)
|
||||
output.accept(ModItems.HOOD.get());
|
||||
|
||||
// ========== 3D ITEMS ==========
|
||||
output.accept(ModItems.BALL_GAG_3D.get());
|
||||
|
||||
// ========== COMBO ITEMS ==========
|
||||
output.accept(ModItems.MEDICAL_GAG.get());
|
||||
|
||||
// ========== CLOTHES ==========
|
||||
output.accept(ModItems.CLOTHES.get());
|
||||
|
||||
// ========== COLLARS ==========
|
||||
output.accept(ModItems.CLASSIC_COLLAR.get());
|
||||
output.accept(ModItems.CHOKE_COLLAR.get());
|
||||
output.accept(ModItems.SHOCK_COLLAR.get());
|
||||
output.accept(ModItems.GPS_COLLAR.get());
|
||||
|
||||
// ========== EARPLUGS (from enum) ==========
|
||||
for (EarplugsVariant variant : EarplugsVariant.values()) {
|
||||
output.accept(ModItems.getEarplugs(variant));
|
||||
}
|
||||
|
||||
// ========== MITTENS (from enum) ==========
|
||||
for (MittensVariant variant : MittensVariant.values()) {
|
||||
output.accept(ModItems.getMittens(variant));
|
||||
}
|
||||
|
||||
// ========== KNIVES (from enum) ==========
|
||||
for (KnifeVariant variant : KnifeVariant.values()) {
|
||||
output.accept(ModItems.getKnife(variant));
|
||||
@@ -196,15 +99,6 @@ public class ModCreativeTabs {
|
||||
com.tiedup.remake.v2.bondage.V2BondageItems.V2_HANDCUFFS.get()
|
||||
);
|
||||
|
||||
// ========== DATA-DRIVEN BONDAGE ITEMS ==========
|
||||
for (com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition def : com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry.getAll()) {
|
||||
output.accept(
|
||||
com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(
|
||||
def.id()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// ========== FURNITURE PLACER ITEMS ==========
|
||||
for (com.tiedup.remake.v2.furniture.FurnitureDefinition def : com.tiedup.remake.v2.furniture.FurnitureRegistry.getAll()) {
|
||||
output.accept(
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.tiedup.remake.items;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.entities.ModEntities;
|
||||
import com.tiedup.remake.items.base.*;
|
||||
import com.tiedup.remake.items.bondage3d.gags.ItemBallGag3D;
|
||||
import com.tiedup.remake.items.clothes.GenericClothes;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
@@ -17,14 +16,9 @@ import net.minecraftforge.registries.RegistryObject;
|
||||
* Mod Items Registration
|
||||
* Handles registration of all TiedUp items using DeferredRegister.
|
||||
*
|
||||
* Refactored with Factory Pattern:
|
||||
* - Binds, Gags, Blindfolds, Earplugs, Knives use EnumMaps and factory methods
|
||||
* - Complex items (collars, whip, chloroform, etc.) remain individual registrations
|
||||
*
|
||||
* Usage:
|
||||
* - ModItems.getBind(BindVariant.ROPES) - Get a specific bind item
|
||||
* - ModItems.getGag(GagVariant.BALL_GAG) - Get a specific gag item
|
||||
* - ModItems.WHIP.get() - Get complex items directly
|
||||
* V1 bondage items (binds, gags, blindfolds, earplugs, mittens, collars, hood, medical gag)
|
||||
* have been removed. All bondage items are now data-driven via DataDrivenItemRegistry.
|
||||
* Only non-bondage items and knives remain here.
|
||||
*/
|
||||
public class ModItems {
|
||||
|
||||
@@ -34,42 +28,7 @@ public class ModItems {
|
||||
TiedUpMod.MOD_ID
|
||||
);
|
||||
|
||||
// ========== FACTORY-BASED ITEMS ==========
|
||||
|
||||
/**
|
||||
* All bind items (15 variants via BindVariant enum)
|
||||
*/
|
||||
public static final Map<BindVariant, RegistryObject<Item>> BINDS =
|
||||
registerAllBinds();
|
||||
|
||||
/**
|
||||
* All gag items (via GagVariant enum)
|
||||
* Note: ItemMedicalGag is registered separately as it has special behavior
|
||||
* Note: BALL_GAG_3D is a separate 3D item (not in enum)
|
||||
*/
|
||||
public static final Map<GagVariant, RegistryObject<Item>> GAGS =
|
||||
registerAllGags();
|
||||
|
||||
/**
|
||||
* Ball Gag 3D - Uses 3D OBJ model rendering via dedicated class.
|
||||
* This is a separate item from BALL_GAG (which uses 2D textures).
|
||||
*/
|
||||
public static final RegistryObject<Item> BALL_GAG_3D = ITEMS.register(
|
||||
"ball_gag_3d",
|
||||
ItemBallGag3D::new
|
||||
);
|
||||
|
||||
/**
|
||||
* All blindfold items (2 variants via BlindfoldVariant enum)
|
||||
*/
|
||||
public static final Map<BlindfoldVariant, RegistryObject<Item>> BLINDFOLDS =
|
||||
registerAllBlindfolds();
|
||||
|
||||
/**
|
||||
* All earplugs items (1 variant via EarplugsVariant enum)
|
||||
*/
|
||||
public static final Map<EarplugsVariant, RegistryObject<Item>> EARPLUGS =
|
||||
registerAllEarplugs();
|
||||
// ========== KNIVES (still V1 — not bondage items) ==========
|
||||
|
||||
/**
|
||||
* All knife items (3 variants via KnifeVariant enum)
|
||||
@@ -77,12 +36,6 @@ public class ModItems {
|
||||
public static final Map<KnifeVariant, RegistryObject<Item>> KNIVES =
|
||||
registerAllKnives();
|
||||
|
||||
/**
|
||||
* All mittens items (1 variant via MittensVariant enum)
|
||||
*/
|
||||
public static final Map<MittensVariant, RegistryObject<Item>> MITTENS =
|
||||
registerAllMittens();
|
||||
|
||||
/**
|
||||
* Clothes item - uses dynamic textures from URLs.
|
||||
* Users can create presets via anvil naming.
|
||||
@@ -92,48 +45,8 @@ public class ModItems {
|
||||
GenericClothes::new
|
||||
);
|
||||
|
||||
// ========== COMPLEX ITEMS (individual registrations) ==========
|
||||
// ========== TOOLS ==========
|
||||
|
||||
// Medical gag - combo item with IHasBlindingEffect
|
||||
public static final RegistryObject<Item> MEDICAL_GAG = ITEMS.register(
|
||||
"medical_gag",
|
||||
ItemMedicalGag::new
|
||||
);
|
||||
|
||||
// Hood - combo item
|
||||
public static final RegistryObject<Item> HOOD = ITEMS.register(
|
||||
"hood",
|
||||
ItemHood::new
|
||||
);
|
||||
|
||||
// Collars - complex logic
|
||||
public static final RegistryObject<Item> CLASSIC_COLLAR = ITEMS.register(
|
||||
"classic_collar",
|
||||
ItemClassicCollar::new
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> SHOCK_COLLAR = ITEMS.register(
|
||||
"shock_collar",
|
||||
ItemShockCollar::new
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> SHOCK_COLLAR_AUTO = ITEMS.register(
|
||||
"shock_collar_auto",
|
||||
ItemShockCollarAuto::new
|
||||
);
|
||||
|
||||
public static final RegistryObject<Item> GPS_COLLAR = ITEMS.register(
|
||||
"gps_collar",
|
||||
ItemGpsCollar::new
|
||||
);
|
||||
|
||||
// Choke Collar - Pet play collar used by Masters
|
||||
public static final RegistryObject<Item> CHOKE_COLLAR = ITEMS.register(
|
||||
"choke_collar",
|
||||
ItemChokeCollar::new
|
||||
);
|
||||
|
||||
// Tools with complex behavior
|
||||
public static final RegistryObject<Item> WHIP = ITEMS.register(
|
||||
"whip",
|
||||
ItemWhip::new
|
||||
@@ -213,13 +126,11 @@ public class ModItems {
|
||||
|
||||
// ========== CELL SYSTEM ITEMS ==========
|
||||
|
||||
// Admin Wand - Structure marker placement and Cell Core management
|
||||
public static final RegistryObject<Item> ADMIN_WAND = ITEMS.register(
|
||||
"admin_wand",
|
||||
ItemAdminWand::new
|
||||
);
|
||||
|
||||
// Cell Key - Universal key for iron bar doors
|
||||
public static final RegistryObject<Item> CELL_KEY = ITEMS.register(
|
||||
"cell_key",
|
||||
ItemCellKey::new
|
||||
@@ -227,7 +138,6 @@ public class ModItems {
|
||||
|
||||
// ========== SLAVE TRADER SYSTEM ==========
|
||||
|
||||
// Token - Access pass for kidnapper camps
|
||||
public static final RegistryObject<Item> TOKEN = ITEMS.register(
|
||||
"token",
|
||||
ItemToken::new
|
||||
@@ -252,72 +162,6 @@ public class ModItems {
|
||||
|
||||
// ========== FACTORY METHODS ==========
|
||||
|
||||
private static Map<BindVariant, RegistryObject<Item>> registerAllBinds() {
|
||||
Map<BindVariant, RegistryObject<Item>> map = new EnumMap<>(
|
||||
BindVariant.class
|
||||
);
|
||||
for (BindVariant variant : BindVariant.values()) {
|
||||
map.put(
|
||||
variant,
|
||||
ITEMS.register(variant.getRegistryName(), () ->
|
||||
new GenericBind(variant)
|
||||
)
|
||||
);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<GagVariant, RegistryObject<Item>> registerAllGags() {
|
||||
Map<GagVariant, RegistryObject<Item>> map = new EnumMap<>(
|
||||
GagVariant.class
|
||||
);
|
||||
for (GagVariant variant : GagVariant.values()) {
|
||||
map.put(
|
||||
variant,
|
||||
ITEMS.register(variant.getRegistryName(), () ->
|
||||
new GenericGag(variant)
|
||||
)
|
||||
);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<
|
||||
BlindfoldVariant,
|
||||
RegistryObject<Item>
|
||||
> registerAllBlindfolds() {
|
||||
Map<BlindfoldVariant, RegistryObject<Item>> map = new EnumMap<>(
|
||||
BlindfoldVariant.class
|
||||
);
|
||||
for (BlindfoldVariant variant : BlindfoldVariant.values()) {
|
||||
map.put(
|
||||
variant,
|
||||
ITEMS.register(variant.getRegistryName(), () ->
|
||||
new GenericBlindfold(variant)
|
||||
)
|
||||
);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<
|
||||
EarplugsVariant,
|
||||
RegistryObject<Item>
|
||||
> registerAllEarplugs() {
|
||||
Map<EarplugsVariant, RegistryObject<Item>> map = new EnumMap<>(
|
||||
EarplugsVariant.class
|
||||
);
|
||||
for (EarplugsVariant variant : EarplugsVariant.values()) {
|
||||
map.put(
|
||||
variant,
|
||||
ITEMS.register(variant.getRegistryName(), () ->
|
||||
new GenericEarplugs(variant)
|
||||
)
|
||||
);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<KnifeVariant, RegistryObject<Item>> registerAllKnives() {
|
||||
Map<KnifeVariant, RegistryObject<Item>> map = new EnumMap<>(
|
||||
KnifeVariant.class
|
||||
@@ -333,62 +177,8 @@ public class ModItems {
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<
|
||||
MittensVariant,
|
||||
RegistryObject<Item>
|
||||
> registerAllMittens() {
|
||||
Map<MittensVariant, RegistryObject<Item>> map = new EnumMap<>(
|
||||
MittensVariant.class
|
||||
);
|
||||
for (MittensVariant variant : MittensVariant.values()) {
|
||||
map.put(
|
||||
variant,
|
||||
ITEMS.register(variant.getRegistryName(), () ->
|
||||
new GenericMittens(variant)
|
||||
)
|
||||
);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// ========== HELPER ACCESSORS ==========
|
||||
|
||||
/**
|
||||
* Get a bind item by variant.
|
||||
* @param variant The bind variant
|
||||
* @return The bind item
|
||||
*/
|
||||
public static Item getBind(BindVariant variant) {
|
||||
return BINDS.get(variant).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a gag item by variant.
|
||||
* @param variant The gag variant
|
||||
* @return The gag item
|
||||
*/
|
||||
public static Item getGag(GagVariant variant) {
|
||||
return GAGS.get(variant).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a blindfold item by variant.
|
||||
* @param variant The blindfold variant
|
||||
* @return The blindfold item
|
||||
*/
|
||||
public static Item getBlindfold(BlindfoldVariant variant) {
|
||||
return BLINDFOLDS.get(variant).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an earplugs item by variant.
|
||||
* @param variant The earplugs variant
|
||||
* @return The earplugs item
|
||||
*/
|
||||
public static Item getEarplugs(EarplugsVariant variant) {
|
||||
return EARPLUGS.get(variant).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a knife item by variant.
|
||||
* @param variant The knife variant
|
||||
@@ -397,13 +187,4 @@ public class ModItems {
|
||||
public static Item getKnife(KnifeVariant variant) {
|
||||
return KNIVES.get(variant).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a mittens item by variant.
|
||||
* @param variant The mittens variant
|
||||
* @return The mittens item
|
||||
*/
|
||||
public static Item getMittens(MittensVariant variant) {
|
||||
return MITTENS.get(variant).get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
import com.tiedup.remake.v2.bondage.component.AdjustableComponent;
|
||||
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -56,9 +59,12 @@ public class AdjustmentHelper {
|
||||
return tag.getFloat(NBT_ADJUSTMENT_Y);
|
||||
}
|
||||
|
||||
// Fallback to item's default adjustment
|
||||
if (stack.getItem() instanceof IAdjustable adj) {
|
||||
return adj.getDefaultAdjustment();
|
||||
// Fallback to item's default adjustment from V2 AdjustableComponent
|
||||
AdjustableComponent comp = DataDrivenBondageItem.getComponent(
|
||||
stack, ComponentType.ADJUSTABLE, AdjustableComponent.class
|
||||
);
|
||||
if (comp != null) {
|
||||
return comp.getDefaultValue();
|
||||
}
|
||||
|
||||
return DEFAULT_VALUE;
|
||||
@@ -127,16 +133,15 @@ public class AdjustmentHelper {
|
||||
* Check if an ItemStack's item supports adjustment.
|
||||
*
|
||||
* @param stack The ItemStack to check
|
||||
* @return true if the item implements IAdjustable and canBeAdjusted() returns true
|
||||
* @return true if the item has an AdjustableComponent
|
||||
*/
|
||||
public static boolean isAdjustable(ItemStack stack) {
|
||||
if (stack.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (stack.getItem() instanceof IAdjustable adj) {
|
||||
return adj.canBeAdjusted();
|
||||
}
|
||||
return false;
|
||||
return DataDrivenBondageItem.getComponent(
|
||||
stack, ComponentType.ADJUSTABLE, AdjustableComponent.class
|
||||
) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
/**
|
||||
* Enum defining all bind variants with their properties.
|
||||
* Used by GenericBind to create bind items via factory pattern.
|
||||
*
|
||||
* <p><b>Issue #12 fix:</b> Added textureSubfolder to eliminate 40+ string checks in renderers.
|
||||
*/
|
||||
public enum BindVariant {
|
||||
// Standard binds (PoseType.STANDARD)
|
||||
ROPES("ropes", PoseType.STANDARD, true, "ropes"),
|
||||
ARMBINDER("armbinder", PoseType.STANDARD, false, "armbinder"),
|
||||
DOGBINDER("dogbinder", PoseType.DOG, false, "armbinder"),
|
||||
CHAIN("chain", PoseType.STANDARD, false, "chain"),
|
||||
RIBBON("ribbon", PoseType.STANDARD, false, "ribbon"),
|
||||
SLIME("slime", PoseType.STANDARD, false, "slime"),
|
||||
VINE_SEED("vine_seed", PoseType.STANDARD, false, "vine"),
|
||||
WEB_BIND("web_bind", PoseType.STANDARD, false, "web"),
|
||||
SHIBARI("shibari", PoseType.STANDARD, true, "shibari"),
|
||||
LEATHER_STRAPS("leather_straps", PoseType.STANDARD, false, "straps"),
|
||||
MEDICAL_STRAPS("medical_straps", PoseType.STANDARD, false, "straps"),
|
||||
BEAM_CUFFS("beam_cuffs", PoseType.STANDARD, false, "beam"),
|
||||
DUCT_TAPE("duct_tape", PoseType.STANDARD, true, "tape"),
|
||||
|
||||
// Pose items (special PoseType)
|
||||
STRAITJACKET("straitjacket", PoseType.STRAITJACKET, false, "straitjacket"),
|
||||
WRAP("wrap", PoseType.WRAP, false, "wrap"),
|
||||
LATEX_SACK("latex_sack", PoseType.LATEX_SACK, false, "latex");
|
||||
|
||||
private final String registryName;
|
||||
private final PoseType poseType;
|
||||
private final boolean supportsColor;
|
||||
private final String textureSubfolder;
|
||||
|
||||
BindVariant(
|
||||
String registryName,
|
||||
PoseType poseType,
|
||||
boolean supportsColor,
|
||||
String textureSubfolder
|
||||
) {
|
||||
this.registryName = registryName;
|
||||
this.poseType = poseType;
|
||||
this.supportsColor = supportsColor;
|
||||
this.textureSubfolder = textureSubfolder;
|
||||
}
|
||||
|
||||
public String getRegistryName() {
|
||||
return registryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured resistance for this bind variant.
|
||||
* Delegates to {@link com.tiedup.remake.core.SettingsAccessor#getBindResistance(String)}.
|
||||
*/
|
||||
public int getResistance() {
|
||||
return com.tiedup.remake.core.SettingsAccessor.getBindResistance(
|
||||
registryName
|
||||
);
|
||||
}
|
||||
|
||||
public PoseType getPoseType() {
|
||||
return poseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this bind variant supports color variations.
|
||||
* Items with colors: ropes, shibari, duct_tape
|
||||
*/
|
||||
public boolean supportsColor() {
|
||||
return supportsColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this bind variant.
|
||||
* Used by renderers to locate texture files.
|
||||
*
|
||||
* @return Subfolder path under textures/entity/bondage/ (e.g., "ropes", "straps")
|
||||
*/
|
||||
public String getTextureSubfolder() {
|
||||
return textureSubfolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item name used for textures and translations.
|
||||
* For most variants this is the same as registryName.
|
||||
*/
|
||||
public String getItemName() {
|
||||
return registryName;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
/**
|
||||
* Enum defining all blindfold variants.
|
||||
* Used by GenericBlindfold to create blindfold items via factory pattern.
|
||||
*
|
||||
* <p><b>Issue #12 fix:</b> Added textureSubfolder to eliminate string checks in renderers.
|
||||
*/
|
||||
public enum BlindfoldVariant {
|
||||
CLASSIC("classic_blindfold", true, "blindfolds"),
|
||||
MASK("blindfold_mask", true, "blindfolds/mask");
|
||||
|
||||
private final String registryName;
|
||||
private final boolean supportsColor;
|
||||
private final String textureSubfolder;
|
||||
|
||||
BlindfoldVariant(
|
||||
String registryName,
|
||||
boolean supportsColor,
|
||||
String textureSubfolder
|
||||
) {
|
||||
this.registryName = registryName;
|
||||
this.supportsColor = supportsColor;
|
||||
this.textureSubfolder = textureSubfolder;
|
||||
}
|
||||
|
||||
public String getRegistryName() {
|
||||
return registryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this blindfold variant supports color variations.
|
||||
* Both variants support colors in the original mod.
|
||||
*/
|
||||
public boolean supportsColor() {
|
||||
return supportsColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this blindfold variant.
|
||||
* Used by renderers to locate texture files.
|
||||
*
|
||||
* @return Subfolder path under textures/entity/bondage/ (e.g., "blindfolds", "blindfolds/mask")
|
||||
*/
|
||||
public String getTextureSubfolder() {
|
||||
return textureSubfolder;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
/**
|
||||
* Enum defining all earplugs variants.
|
||||
* Used by GenericEarplugs to create earplugs items via factory pattern.
|
||||
*
|
||||
* <p><b>Issue #12 fix:</b> Added textureSubfolder to eliminate string checks in renderers.
|
||||
*/
|
||||
public enum EarplugsVariant {
|
||||
CLASSIC("classic_earplugs", "earplugs");
|
||||
|
||||
private final String registryName;
|
||||
private final String textureSubfolder;
|
||||
|
||||
EarplugsVariant(String registryName, String textureSubfolder) {
|
||||
this.registryName = registryName;
|
||||
this.textureSubfolder = textureSubfolder;
|
||||
}
|
||||
|
||||
public String getRegistryName() {
|
||||
return registryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this earplugs variant.
|
||||
* Used by renderers to locate texture files.
|
||||
*
|
||||
* @return Subfolder path under textures/entity/bondage/ (e.g., "earplugs")
|
||||
*/
|
||||
public String getTextureSubfolder() {
|
||||
return textureSubfolder;
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
import com.tiedup.remake.util.GagMaterial;
|
||||
|
||||
/**
|
||||
* Enum defining all gag variants with their properties.
|
||||
* Used by GenericGag to create gag items via factory pattern.
|
||||
*
|
||||
* <p>Note: ItemMedicalGag is NOT included here because it implements
|
||||
* IHasBlindingEffect (combo item with special behavior).
|
||||
*
|
||||
* <p><b>Issue #12 fix:</b> Added textureSubfolder to eliminate 40+ string checks in renderers.
|
||||
*/
|
||||
public enum GagVariant {
|
||||
// Cloth-based gags
|
||||
CLOTH_GAG("cloth_gag", GagMaterial.CLOTH, true, "cloth", false, null),
|
||||
ROPES_GAG("ropes_gag", GagMaterial.CLOTH, true, "shibari", false, null),
|
||||
CLEAVE_GAG("cleave_gag", GagMaterial.CLOTH, true, "cleave", false, null),
|
||||
RIBBON_GAG("ribbon_gag", GagMaterial.CLOTH, false, "ribbon", false, null),
|
||||
|
||||
// Ball gags - standard 2D texture rendering
|
||||
BALL_GAG(
|
||||
"ball_gag",
|
||||
GagMaterial.BALL,
|
||||
true,
|
||||
"ballgags/normal",
|
||||
false,
|
||||
null
|
||||
),
|
||||
BALL_GAG_STRAP(
|
||||
"ball_gag_strap",
|
||||
GagMaterial.BALL,
|
||||
true,
|
||||
"ballgags/harness",
|
||||
false,
|
||||
null
|
||||
),
|
||||
|
||||
// Tape gags
|
||||
TAPE_GAG("tape_gag", GagMaterial.TAPE, true, "tape", false, null),
|
||||
|
||||
// Stuffed/filling gags (no colors)
|
||||
WRAP_GAG("wrap_gag", GagMaterial.STUFFED, false, "wrap", false, null),
|
||||
SLIME_GAG("slime_gag", GagMaterial.STUFFED, false, "slime", false, null),
|
||||
VINE_GAG("vine_gag", GagMaterial.STUFFED, false, "vine", false, null),
|
||||
WEB_GAG("web_gag", GagMaterial.STUFFED, false, "web", false, null),
|
||||
|
||||
// Panel gags (no colors)
|
||||
PANEL_GAG(
|
||||
"panel_gag",
|
||||
GagMaterial.PANEL,
|
||||
false,
|
||||
"straitjacket",
|
||||
false,
|
||||
null
|
||||
),
|
||||
BEAM_PANEL_GAG(
|
||||
"beam_panel_gag",
|
||||
GagMaterial.PANEL,
|
||||
false,
|
||||
"beam",
|
||||
false,
|
||||
null
|
||||
),
|
||||
CHAIN_PANEL_GAG(
|
||||
"chain_panel_gag",
|
||||
GagMaterial.PANEL,
|
||||
false,
|
||||
"chain",
|
||||
false,
|
||||
null
|
||||
),
|
||||
|
||||
// Latex gags (no colors)
|
||||
LATEX_GAG("latex_gag", GagMaterial.LATEX, false, "latex", false, null),
|
||||
|
||||
// Ring/tube gags (no colors)
|
||||
TUBE_GAG("tube_gag", GagMaterial.RING, false, "tube", false, null),
|
||||
|
||||
// Bite gags (no colors)
|
||||
BITE_GAG("bite_gag", GagMaterial.BITE, false, "armbinder", false, null),
|
||||
|
||||
// Sponge gags (no colors)
|
||||
SPONGE_GAG("sponge_gag", GagMaterial.SPONGE, false, "sponge", false, null),
|
||||
|
||||
// Baguette gags (no colors)
|
||||
BAGUETTE_GAG(
|
||||
"baguette_gag",
|
||||
GagMaterial.BAGUETTE,
|
||||
false,
|
||||
"baguette",
|
||||
false,
|
||||
null
|
||||
);
|
||||
|
||||
private final String registryName;
|
||||
private final GagMaterial material;
|
||||
private final boolean supportsColor;
|
||||
private final String textureSubfolder;
|
||||
private final boolean uses3DModel;
|
||||
private final String modelPath;
|
||||
|
||||
GagVariant(
|
||||
String registryName,
|
||||
GagMaterial material,
|
||||
boolean supportsColor,
|
||||
String textureSubfolder,
|
||||
boolean uses3DModel,
|
||||
String modelPath
|
||||
) {
|
||||
this.registryName = registryName;
|
||||
this.material = material;
|
||||
this.supportsColor = supportsColor;
|
||||
this.textureSubfolder = textureSubfolder;
|
||||
this.uses3DModel = uses3DModel;
|
||||
this.modelPath = modelPath;
|
||||
}
|
||||
|
||||
public String getRegistryName() {
|
||||
return registryName;
|
||||
}
|
||||
|
||||
public GagMaterial getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this gag variant supports color variations.
|
||||
* Items with colors: cloth_gag, ropes_gag, cleave_gag, ribbon_gag,
|
||||
* ball_gag, ball_gag_strap, tape_gag
|
||||
*/
|
||||
public boolean supportsColor() {
|
||||
return supportsColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this gag variant.
|
||||
* Used by renderers to locate texture files.
|
||||
*
|
||||
* @return Subfolder path under textures/entity/bondage/ (e.g., "cloth", "ballgags/normal")
|
||||
*/
|
||||
public String getTextureSubfolder() {
|
||||
return textureSubfolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this gag variant uses a 3D OBJ model.
|
||||
*
|
||||
* @return true if this variant uses a 3D model
|
||||
*/
|
||||
public boolean uses3DModel() {
|
||||
return uses3DModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model path for 3D rendering.
|
||||
*
|
||||
* @return ResourceLocation string path (e.g., "tiedup:models/obj/ball_gag.obj"), or null if no 3D model
|
||||
*/
|
||||
public String getModelPath() {
|
||||
return modelPath;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
/**
|
||||
* Interface for items that can have their render position adjusted.
|
||||
* Typically gags and blindfolds that render on the player's head.
|
||||
*
|
||||
* Players can adjust the Y position of these items to better fit their skin.
|
||||
* Adjustment values are stored in the ItemStack's NBT via AdjustmentHelper.
|
||||
*/
|
||||
public interface IAdjustable {
|
||||
/**
|
||||
* Whether this item supports position adjustment.
|
||||
* @return true if adjustable
|
||||
*/
|
||||
boolean canBeAdjusted();
|
||||
|
||||
/**
|
||||
* Default Y offset for this item type (in pixels, 1 pixel = 1/16 block).
|
||||
* Override for items that need a non-zero default position.
|
||||
* @return default adjustment value
|
||||
*/
|
||||
default float getDefaultAdjustment() {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum allowed adjustment value (pixels).
|
||||
* @return minimum value (typically -4.0)
|
||||
*/
|
||||
default float getMinAdjustment() {
|
||||
return -4.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum allowed adjustment value (pixels).
|
||||
* @return maximum value (typically +4.0)
|
||||
*/
|
||||
default float getMaxAdjustment() {
|
||||
return 4.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step size for GUI slider (smaller = more precise).
|
||||
* @return step size (typically 0.25)
|
||||
*/
|
||||
default float getAdjustmentStep() {
|
||||
return 0.25f;
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Interface for all bondage equipment items.
|
||||
* Defines the core behavior for items that can be equipped in custom bondage slots.
|
||||
*
|
||||
* Based on original IExtraBondageItem from 1.12.2
|
||||
*/
|
||||
public interface IBondageItem {
|
||||
/**
|
||||
* Get the body region this item occupies when equipped.
|
||||
* @return The body region
|
||||
*/
|
||||
BodyRegionV2 getBodyRegion();
|
||||
|
||||
/**
|
||||
* Called every tick while this item is equipped on an entity.
|
||||
* @param stack The equipped item stack
|
||||
* @param entity The entity wearing the item
|
||||
*/
|
||||
default void onWornTick(ItemStack stack, LivingEntity entity) {
|
||||
// Default: do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this item is equipped on an entity.
|
||||
* @param stack The equipped item stack
|
||||
* @param entity The entity wearing the item
|
||||
*/
|
||||
default void onEquipped(ItemStack stack, LivingEntity entity) {
|
||||
// Default: do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this item is unequipped from an entity.
|
||||
* @param stack The unequipped item stack
|
||||
* @param entity The entity that was wearing the item
|
||||
*/
|
||||
default void onUnequipped(ItemStack stack, LivingEntity entity) {
|
||||
// Default: do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this item can be equipped on the given entity.
|
||||
* @param stack The item stack to equip
|
||||
* @param entity The target entity
|
||||
* @return true if the item can be equipped, false otherwise
|
||||
*/
|
||||
default boolean canEquip(ItemStack stack, LivingEntity entity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this item can be unequipped from the given entity.
|
||||
* @param stack The equipped item stack
|
||||
* @param entity The entity wearing the item
|
||||
* @return true if the item can be unequipped, false otherwise
|
||||
*/
|
||||
default boolean canUnequip(ItemStack stack, LivingEntity entity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this bondage item.
|
||||
* Used by renderers to locate texture files.
|
||||
*
|
||||
* <p><b>Issue #12 fix:</b> Eliminates 40+ string checks in renderers by letting
|
||||
* each item type declare its own texture subfolder.
|
||||
*
|
||||
* @return Subfolder path under textures/entity/bondage/ (e.g., "ropes", "ballgags/normal")
|
||||
*/
|
||||
default String getTextureSubfolder() {
|
||||
return "misc"; // Fallback for items without explicit subfolder
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this bondage item uses a 3D OBJ model instead of a flat texture.
|
||||
* Items with 3D models will be rendered using ObjModelRenderer.
|
||||
*
|
||||
* @return true if this item uses a 3D model, false for standard texture rendering
|
||||
*/
|
||||
default boolean uses3DModel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ResourceLocation of the 3D model for this item.
|
||||
* Only called if uses3DModel() returns true.
|
||||
*
|
||||
* @return ResourceLocation pointing to the .obj file, or null if no 3D model
|
||||
*/
|
||||
@Nullable
|
||||
default ResourceLocation get3DModelLocation() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
/**
|
||||
* Marker interface for items that have a blinding visual effect.
|
||||
*
|
||||
* <p>Items implementing this interface will:
|
||||
* <ul>
|
||||
* <li>Apply a screen overlay when worn (client-side)</li>
|
||||
* <li>Reduce the player's visibility</li>
|
||||
* <li>Potentially disable certain UI elements</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Usage</h2>
|
||||
* <pre>{@code
|
||||
* if (blindfold.getItem() instanceof IHasBlindingEffect) {
|
||||
* // Apply blinding overlay
|
||||
* renderBlindingOverlay();
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Implementations</h2>
|
||||
* <ul>
|
||||
* <li>{@link ItemBlindfold} - All blindfold items</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Based on original IHasBlindingEffect.java from 1.12.2
|
||||
*
|
||||
* @see ItemBlindfold
|
||||
*/
|
||||
public interface IHasBlindingEffect {
|
||||
// Marker interface - no methods required
|
||||
// Presence of this interface indicates the item has a blinding effect
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
/**
|
||||
* Marker interface for items that have a gagging (speech muffling) effect.
|
||||
*
|
||||
* <p>Items implementing this interface will:
|
||||
* <ul>
|
||||
* <li>Convert chat messages to "mmpphh" sounds</li>
|
||||
* <li>Play gagged speech sounds</li>
|
||||
* <li>Potentially block certain chat commands</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Usage</h2>
|
||||
* <pre>{@code
|
||||
* if (gag.getItem() instanceof IHasGaggingEffect) {
|
||||
* // Convert chat message to gagged speech
|
||||
* message = GagTalkConverter.convert(message);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Implementations</h2>
|
||||
* <ul>
|
||||
* <li>{@link ItemGag} - Ball gags, tape gags, cloth gags, etc.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Based on original ItemGaggingEffect.java from 1.12.2
|
||||
*
|
||||
* @see ItemGag
|
||||
*/
|
||||
public interface IHasGaggingEffect {
|
||||
// Marker interface - no methods required
|
||||
// Presence of this interface indicates the item has a gagging effect
|
||||
}
|
||||
@@ -22,10 +22,8 @@ import net.minecraft.world.item.ItemStack;
|
||||
*
|
||||
* <h2>Implementations</h2>
|
||||
* <ul>
|
||||
* <li>{@link ItemBind} - Ropes, chains, straitjackets</li>
|
||||
* <li>{@link ItemGag} - Ball gags, tape gags, cloth gags</li>
|
||||
* <li>{@link ItemBlindfold} - Blindfolds</li>
|
||||
* <li>{@link ItemCollar} - Collars (special: may not be struggleable)</li>
|
||||
* <li>{@link com.tiedup.remake.v2.bondage.items.AbstractV2BondageItem} - All V2 bondage items</li>
|
||||
* <li>{@link com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem} - Data-driven items (resistance via ResistanceComponent)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Based on original IHasResistance.java from 1.12.2
|
||||
|
||||
@@ -24,11 +24,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
*
|
||||
* <h2>Implementations</h2>
|
||||
* <ul>
|
||||
* <li>{@link ItemGag} - Gags can be locked</li>
|
||||
* <li>{@link ItemBlindfold} - Blindfolds can be locked</li>
|
||||
* <li>{@link ItemCollar} - Collars can be locked</li>
|
||||
* <li>{@link ItemEarplugs} - Earplugs can be locked</li>
|
||||
* <li>ItemBind - Binds can be locked (future)</li>
|
||||
* <li>{@link com.tiedup.remake.v2.bondage.items.AbstractV2BondageItem} - All V2 bondage items</li>
|
||||
* <li>{@link com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem} - Data-driven items (via AbstractV2BondageItem)</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>NBT Storage</h2>
|
||||
|
||||
@@ -1,637 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
import com.tiedup.remake.core.SettingsAccessor;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.network.action.PacketTying;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.tasks.TyingPlayerTask;
|
||||
import com.tiedup.remake.tasks.TyingTask;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import com.tiedup.remake.util.RestraintEffectUtils;
|
||||
import com.tiedup.remake.util.TiedUpSounds;
|
||||
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.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
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.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Base class for binding/restraint items (ropes, chains, straitjacket, etc.)
|
||||
* These items restrain a player's movement and actions when equipped.
|
||||
*
|
||||
* <p>Implements {@link IHasResistance} for the struggle/escape system.
|
||||
* <p>Implements {@link ILockable} for the padlock system.
|
||||
*
|
||||
* Based on original ItemBind from 1.12.2
|
||||
*
|
||||
*/
|
||||
public abstract class ItemBind
|
||||
extends Item
|
||||
implements IBondageItem, IHasResistance, ILockable
|
||||
{
|
||||
|
||||
// ========== Leg Binding: Bind Mode NBT Key ==========
|
||||
private static final String NBT_BIND_MODE = "bindMode";
|
||||
|
||||
public ItemBind(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyRegionV2 getBodyRegion() {
|
||||
return BodyRegionV2.ARMS;
|
||||
}
|
||||
|
||||
// ========== Leg Binding: Bind Mode Methods ==========
|
||||
|
||||
// String constants matching NBT values
|
||||
public static final String BIND_MODE_FULL = "full";
|
||||
private static final String MODE_FULL = BIND_MODE_FULL;
|
||||
private static final String MODE_ARMS = "arms";
|
||||
private static final String MODE_LEGS = "legs";
|
||||
private static final String[] MODE_CYCLE = {
|
||||
MODE_FULL,
|
||||
MODE_ARMS,
|
||||
MODE_LEGS,
|
||||
};
|
||||
private static final java.util.Map<String, String> MODE_TRANSLATION_KEYS =
|
||||
java.util.Map.of(
|
||||
MODE_FULL,
|
||||
"tiedup.bindmode.full",
|
||||
MODE_ARMS,
|
||||
"tiedup.bindmode.arms",
|
||||
MODE_LEGS,
|
||||
"tiedup.bindmode.legs"
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the bind mode ID string from the stack's NBT.
|
||||
* @param stack The bind ItemStack
|
||||
* @return "full", "arms", or "legs" (defaults to "full" if absent)
|
||||
*/
|
||||
public static String getBindModeId(ItemStack stack) {
|
||||
if (stack.isEmpty()) return MODE_FULL;
|
||||
CompoundTag tag = stack.getTag();
|
||||
if (tag == null || !tag.contains(NBT_BIND_MODE)) return MODE_FULL;
|
||||
String value = tag.getString(NBT_BIND_MODE);
|
||||
if (
|
||||
MODE_FULL.equals(value) ||
|
||||
MODE_ARMS.equals(value) ||
|
||||
MODE_LEGS.equals(value)
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
return MODE_FULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if arms are bound (mode is "arms" or "full").
|
||||
* @param stack The bind ItemStack
|
||||
* @return true if arms are restrained
|
||||
*/
|
||||
public static boolean hasArmsBound(ItemStack stack) {
|
||||
String mode = getBindModeId(stack);
|
||||
return MODE_ARMS.equals(mode) || MODE_FULL.equals(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if legs are bound (mode is "legs" or "full").
|
||||
* @param stack The bind ItemStack
|
||||
* @return true if legs are restrained
|
||||
*/
|
||||
public static boolean hasLegsBound(ItemStack stack) {
|
||||
String mode = getBindModeId(stack);
|
||||
return MODE_LEGS.equals(mode) || MODE_FULL.equals(mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle bind mode: full -> arms -> legs -> full.
|
||||
* @param stack The bind ItemStack
|
||||
* @return the new mode ID string
|
||||
*/
|
||||
public static String cycleBindModeId(ItemStack stack) {
|
||||
String current = getBindModeId(stack);
|
||||
String next = MODE_FULL;
|
||||
for (int i = 0; i < MODE_CYCLE.length; i++) {
|
||||
if (MODE_CYCLE[i].equals(current)) {
|
||||
next = MODE_CYCLE[(i + 1) % MODE_CYCLE.length];
|
||||
break;
|
||||
}
|
||||
}
|
||||
stack.getOrCreateTag().putString(NBT_BIND_MODE, next);
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the translation key for the current bind mode.
|
||||
* @param stack The bind ItemStack
|
||||
* @return the i18n key for the mode
|
||||
*/
|
||||
public static String getBindModeTranslationKey(ItemStack stack) {
|
||||
return MODE_TRANSLATION_KEYS.getOrDefault(
|
||||
getBindModeId(stack),
|
||||
"tiedup.bindmode.full"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when player right-clicks in air with bind item.
|
||||
* Sneak+click cycles the bind mode.
|
||||
*/
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(
|
||||
Level level,
|
||||
Player player,
|
||||
InteractionHand hand
|
||||
) {
|
||||
ItemStack stack = player.getItemInHand(hand);
|
||||
|
||||
// Sneak+click in air cycles bind mode
|
||||
if (player.isShiftKeyDown()) {
|
||||
if (!level.isClientSide) {
|
||||
String newModeId = cycleBindModeId(stack);
|
||||
|
||||
// Play feedback sound
|
||||
player.playSound(SoundEvents.CHAIN_STEP, 0.5f, 1.2f);
|
||||
|
||||
// Show action bar message
|
||||
player.displayClientMessage(
|
||||
Component.translatable(
|
||||
"tiedup.message.bindmode_changed",
|
||||
Component.translatable(getBindModeTranslationKey(stack))
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[ItemBind] {} cycled bind mode to {}",
|
||||
player.getName().getString(),
|
||||
newModeId
|
||||
);
|
||||
}
|
||||
return InteractionResultHolder.sidedSuccess(
|
||||
stack,
|
||||
level.isClientSide
|
||||
);
|
||||
}
|
||||
|
||||
return super.use(level, player, hand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(
|
||||
ItemStack stack,
|
||||
@Nullable Level level,
|
||||
List<Component> tooltip,
|
||||
TooltipFlag flag
|
||||
) {
|
||||
super.appendHoverText(stack, level, tooltip, flag);
|
||||
|
||||
// Show bind mode
|
||||
tooltip.add(
|
||||
Component.translatable(
|
||||
"item.tiedup.tooltip.bindmode",
|
||||
Component.translatable(getBindModeTranslationKey(stack))
|
||||
).withStyle(ChatFormatting.GRAY)
|
||||
);
|
||||
|
||||
// Show lock status
|
||||
if (isLockable(stack)) {
|
||||
if (isLocked(stack)) {
|
||||
tooltip.add(
|
||||
Component.translatable(
|
||||
"item.tiedup.tooltip.locked"
|
||||
).withStyle(ChatFormatting.RED)
|
||||
);
|
||||
} else {
|
||||
tooltip.add(
|
||||
Component.translatable(
|
||||
"item.tiedup.tooltip.lockable"
|
||||
).withStyle(ChatFormatting.GOLD)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the bind is equipped on an entity.
|
||||
* Applies movement speed reduction only if legs are bound.
|
||||
*
|
||||
* Leg Binding: Speed reduction conditional on mode
|
||||
* Based on original ItemBind.onEquipped() (1.12.2)
|
||||
*/
|
||||
@Override
|
||||
public void onEquipped(ItemStack stack, LivingEntity entity) {
|
||||
String modeId = getBindModeId(stack);
|
||||
|
||||
// Only apply speed reduction if legs are bound
|
||||
if (hasLegsBound(stack)) {
|
||||
// H6 fix: For players, speed is handled exclusively by MovementStyleManager
|
||||
// (V2 tick-based system) via MovementStyleResolver V1 fallback.
|
||||
// Applying V1 RestraintEffectUtils here would cause double stacking (different
|
||||
// UUIDs, ADDITION vs MULTIPLY_BASE) leading to quasi-immobility.
|
||||
if (entity instanceof Player) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[ItemBind] Applied bind (mode={}, pose={}) to player {} - speed delegated to MovementStyleManager",
|
||||
modeId,
|
||||
getPoseType().getAnimationId(),
|
||||
entity.getName().getString()
|
||||
);
|
||||
} else {
|
||||
// NPCs: MovementStyleManager only handles ServerPlayer, so NPCs
|
||||
// still need the legacy RestraintEffectUtils speed modifier.
|
||||
PoseType poseType = getPoseType();
|
||||
boolean fullImmobilization =
|
||||
poseType == PoseType.WRAP ||
|
||||
poseType == PoseType.LATEX_SACK;
|
||||
|
||||
RestraintEffectUtils.applyBindSpeedReduction(
|
||||
entity,
|
||||
fullImmobilization
|
||||
);
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[ItemBind] Applied bind (mode={}, pose={}) to NPC {} - speed reduced (full={})",
|
||||
modeId,
|
||||
poseType.getAnimationId(),
|
||||
entity.getName().getString(),
|
||||
fullImmobilization
|
||||
);
|
||||
}
|
||||
} else {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[ItemBind] Applied bind (mode={}) to {} - no speed reduction",
|
||||
modeId,
|
||||
entity.getName().getString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the bind is unequipped from an entity.
|
||||
* Restores normal movement speed for all entities.
|
||||
*
|
||||
* Based on original ItemBind.onUnequipped() (1.12.2)
|
||||
*/
|
||||
@Override
|
||||
public void onUnequipped(ItemStack stack, LivingEntity entity) {
|
||||
// H6 fix: For players, speed cleanup is handled by MovementStyleManager
|
||||
// (V2 tick-based system). On the next tick, the resolver will see the item
|
||||
// is gone, deactivate the style, and remove the modifier automatically.
|
||||
// NPCs still need the legacy RestraintEffectUtils cleanup.
|
||||
if (!(entity instanceof Player)) {
|
||||
RestraintEffectUtils.removeBindSpeedReduction(entity);
|
||||
}
|
||||
|
||||
IHasResistance.super.resetCurrentResistance(stack);
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[ItemBind] Removed bind from {} - speed {} resistance reset",
|
||||
entity.getName().getString(),
|
||||
entity instanceof Player
|
||||
? "delegated to MovementStyleManager,"
|
||||
: "restored,"
|
||||
);
|
||||
}
|
||||
|
||||
// ========== Tying Interaction ==========
|
||||
|
||||
/**
|
||||
* Called when player right-clicks another entity with this bind item.
|
||||
* Starts or continues a tying task to tie up the target entity.
|
||||
*
|
||||
* - Players: Uses tying task with progress bar
|
||||
* - NPCs: Instant bind (no tying mini-game)
|
||||
*
|
||||
* Based on original ItemBind.itemInteractionForEntity() (1.12.2)
|
||||
*
|
||||
* @param stack The item stack
|
||||
* @param player The player using the item (kidnapper)
|
||||
* @param target The entity being interacted with
|
||||
* @param hand The hand holding the item
|
||||
* @return SUCCESS if tying started/continued, PASS otherwise
|
||||
*/
|
||||
@Override
|
||||
public InteractionResult interactLivingEntity(
|
||||
ItemStack stack,
|
||||
Player player,
|
||||
LivingEntity target,
|
||||
InteractionHand hand
|
||||
) {
|
||||
// Only run on server side
|
||||
if (player.level().isClientSide) {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
IBondageState targetState = KidnappedHelper.getKidnappedState(target);
|
||||
if (targetState == null) {
|
||||
return InteractionResult.PASS; // Target cannot be restrained
|
||||
}
|
||||
|
||||
// Get kidnapper state (player using the item)
|
||||
IBondageState kidnapperState = KidnappedHelper.getKidnappedState(
|
||||
player
|
||||
);
|
||||
if (kidnapperState == null) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
// Already tied - try to swap binds (if not locked)
|
||||
// Check stack.isEmpty() first to prevent accidental unbinding when
|
||||
// the original stack was consumed (e.g., rapid clicks after tying completes)
|
||||
if (targetState.isTiedUp()) {
|
||||
if (stack.isEmpty()) {
|
||||
// No bind in hand - can't swap, just pass
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
ItemStack oldBind = targetState.replaceEquipment(
|
||||
BodyRegionV2.ARMS,
|
||||
stack.copy(),
|
||||
false
|
||||
);
|
||||
if (!oldBind.isEmpty()) {
|
||||
stack.shrink(1);
|
||||
targetState.kidnappedDropItem(oldBind);
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[ItemBind] Swapped bind on {} - dropped old bind",
|
||||
target.getName().getString()
|
||||
);
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
// Locked or failed - can't swap
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
if (kidnapperState.isTiedUp()) {
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[ItemBind] {} tried to tie but is tied themselves",
|
||||
player.getName().getString()
|
||||
);
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
// SECURITY: Distance and line-of-sight validation (skip for self-tying)
|
||||
boolean isSelfTying = player.equals(target);
|
||||
if (!isSelfTying) {
|
||||
double maxTieDistance = 4.0; // Max distance to tie (blocks)
|
||||
double distance = player.distanceTo(target);
|
||||
if (distance > maxTieDistance) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[ItemBind] {} tried to tie {} from too far away ({} blocks)",
|
||||
player.getName().getString(),
|
||||
target.getName().getString(),
|
||||
String.format("%.1f", distance)
|
||||
);
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
// Check line-of-sight (must be able to see target)
|
||||
if (!player.hasLineOfSight(target)) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[ItemBind] {} tried to tie {} without line of sight",
|
||||
player.getName().getString(),
|
||||
target.getName().getString()
|
||||
);
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
}
|
||||
|
||||
return handleTying(stack, player, target, targetState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle tying any target entity (Player or NPC).
|
||||
*
|
||||
* Uses progress-based system:
|
||||
* - update() marks the tick as active
|
||||
* - tick() in RestraintTaskTickHandler.onPlayerTick() handles progress increment/decrement
|
||||
*/
|
||||
private InteractionResult handleTying(
|
||||
ItemStack stack,
|
||||
Player player,
|
||||
LivingEntity target,
|
||||
IBondageState targetState
|
||||
) {
|
||||
// Get kidnapper's state to track the tying task
|
||||
PlayerBindState kidnapperState = PlayerBindState.getInstance(player);
|
||||
if (kidnapperState == null) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
// Get tying duration from GameRule (default: 5 seconds)
|
||||
int tyingSeconds = getTyingDuration(player);
|
||||
|
||||
// Get current tying task (if any)
|
||||
TyingTask currentTask = kidnapperState.getCurrentTyingTask();
|
||||
|
||||
// Check if we should start a new task or continue existing one
|
||||
if (
|
||||
currentTask == null ||
|
||||
!currentTask.isSameTarget(target) ||
|
||||
currentTask.isStopped() ||
|
||||
!ItemStack.matches(currentTask.getBind(), stack)
|
||||
) {
|
||||
// Create new tying task (works for both Players and NPCs)
|
||||
TyingPlayerTask newTask = new TyingPlayerTask(
|
||||
stack.copy(),
|
||||
targetState,
|
||||
target,
|
||||
tyingSeconds,
|
||||
player.level(),
|
||||
player // Pass kidnapper for SystemMessage
|
||||
);
|
||||
|
||||
// FIX: Store the inventory slot for consumption when task completes
|
||||
// This prevents duplication AND allows refund if task is cancelled
|
||||
int sourceSlot = player.getInventory().selected;
|
||||
newTask.setSourceSlot(sourceSlot);
|
||||
newTask.setSourcePlayer(player);
|
||||
|
||||
// Start new task
|
||||
kidnapperState.setCurrentTyingTask(newTask);
|
||||
newTask.setUpTargetState(); // Initialize target's restraint state (only for players)
|
||||
newTask.start();
|
||||
currentTask = newTask;
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[ItemBind] {} started tying {} ({} seconds, slot={})",
|
||||
player.getName().getString(),
|
||||
target.getName().getString(),
|
||||
tyingSeconds,
|
||||
sourceSlot
|
||||
);
|
||||
} else {
|
||||
// Continue existing task - ensure kidnapper is set
|
||||
if (currentTask instanceof TyingPlayerTask playerTask) {
|
||||
playerTask.setKidnapper(player);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark this tick as active (progress will increase in onPlayerTick)
|
||||
// The tick() method in RestraintTaskTickHandler.onPlayerTick handles progress increment/decrement
|
||||
currentTask.update();
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when player right-clicks with the bind item (not targeting an entity).
|
||||
* Cancels any ongoing tying task.
|
||||
*
|
||||
* Based on original ItemBind.onItemRightClick() (1.12.2)
|
||||
*
|
||||
* @param context The use context
|
||||
* @return FAIL to cancel the action
|
||||
*/
|
||||
@Override
|
||||
public InteractionResult useOn(UseOnContext context) {
|
||||
// Only run on server side
|
||||
if (context.getLevel().isClientSide) {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
Player player = context.getPlayer();
|
||||
if (player == null) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
// Cancel any ongoing tying task
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
if (state == null) {
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
// Check for active tying task (unified for both players and NPCs)
|
||||
TyingTask task = state.getCurrentTyingTask();
|
||||
if (task != null) {
|
||||
task.stop();
|
||||
state.setCurrentTyingTask(null);
|
||||
|
||||
LivingEntity target = task.getTargetEntity();
|
||||
String targetName =
|
||||
target != null ? target.getName().getString() : "???";
|
||||
String kidnapperName = player.getName().getString();
|
||||
|
||||
// Send cancellation packet to kidnapper
|
||||
if (player instanceof ServerPlayer serverPlayer) {
|
||||
PacketTying packet = new PacketTying(
|
||||
-1,
|
||||
task.getMaxSeconds(),
|
||||
true,
|
||||
targetName
|
||||
);
|
||||
ModNetwork.sendToPlayer(packet, serverPlayer);
|
||||
}
|
||||
|
||||
// Send cancellation packet to target (if it's a player)
|
||||
if (target instanceof ServerPlayer serverTarget) {
|
||||
PacketTying packet = new PacketTying(
|
||||
-1,
|
||||
task.getMaxSeconds(),
|
||||
false,
|
||||
kidnapperName
|
||||
);
|
||||
ModNetwork.sendToPlayer(packet, serverTarget);
|
||||
}
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[ItemBind] {} cancelled tying task",
|
||||
player.getName().getString()
|
||||
);
|
||||
}
|
||||
|
||||
return InteractionResult.FAIL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tying duration in seconds from GameRule.
|
||||
*
|
||||
* @param player The player (for accessing world/GameRules)
|
||||
* @return Duration in seconds (default: 5)
|
||||
*/
|
||||
private int getTyingDuration(Player player) {
|
||||
return SettingsAccessor.getTyingPlayerTime(
|
||||
player.level().getGameRules()
|
||||
);
|
||||
}
|
||||
|
||||
// ========== Resistance System (via IHasResistance) ==========
|
||||
|
||||
/**
|
||||
* Get the item name for GameRule lookup.
|
||||
* Each subclass must implement this to return its identifier (e.g., "rope", "chain", etc.)
|
||||
*
|
||||
* @return Item name for resistance GameRule lookup
|
||||
*/
|
||||
public abstract String getItemName();
|
||||
|
||||
// ========== Pose System ==========
|
||||
|
||||
/**
|
||||
* Get the pose type for this bind item.
|
||||
* Determines which animation/pose is applied when this item is equipped.
|
||||
*
|
||||
* Override in subclasses for special poses (straitjacket, wrap, latex_sack).
|
||||
*
|
||||
* @return PoseType for this bind (default: STANDARD)
|
||||
*/
|
||||
public PoseType getPoseType() {
|
||||
return PoseType.STANDARD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of IHasResistance.getResistanceId().
|
||||
* Delegates to getItemName() for backward compatibility with subclasses.
|
||||
*
|
||||
* @return Item identifier for resistance lookup
|
||||
*/
|
||||
@Override
|
||||
public String getResistanceId() {
|
||||
return getItemName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the entity struggles against this bind.
|
||||
* Plays struggle sound and shows message.
|
||||
*
|
||||
* Based on original ItemBind struggle notification (1.12.2)
|
||||
*
|
||||
* @param entity The entity struggling
|
||||
*/
|
||||
@Override
|
||||
public void notifyStruggle(LivingEntity entity) {
|
||||
// Play struggle sound
|
||||
TiedUpSounds.playStruggleSound(entity);
|
||||
|
||||
// Log the struggle attempt
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[ItemBind] {} is struggling against bind",
|
||||
entity.getName().getString()
|
||||
);
|
||||
|
||||
// Notify nearby players if the entity is a player
|
||||
if (entity instanceof ServerPlayer serverPlayer) {
|
||||
serverPlayer.displayClientMessage(
|
||||
Component.translatable("tiedup.message.struggling"),
|
||||
true // Action bar
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ILockable implementation inherited from interface default methods
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.util.EquipmentInteractionHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import java.util.List;
|
||||
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.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Base class for blindfold items (classic blindfold, mask, hood, etc.)
|
||||
* These items obstruct a player's vision when equipped.
|
||||
*
|
||||
* Based on original ItemBlindfold from 1.12.2
|
||||
*
|
||||
*/
|
||||
public abstract class ItemBlindfold
|
||||
extends Item
|
||||
implements IBondageItem, IHasBlindingEffect, IAdjustable, ILockable
|
||||
{
|
||||
|
||||
public ItemBlindfold(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyRegionV2 getBodyRegion() {
|
||||
return BodyRegionV2.EYES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(
|
||||
ItemStack stack,
|
||||
@Nullable Level level,
|
||||
List<Component> tooltip,
|
||||
TooltipFlag flag
|
||||
) {
|
||||
super.appendHoverText(stack, level, tooltip, flag);
|
||||
appendLockTooltip(stack, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* All blindfolds can be adjusted to better fit player skins.
|
||||
* @return true - blindfolds support position adjustment
|
||||
*/
|
||||
@Override
|
||||
public boolean canBeAdjusted() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when player right-clicks another entity with this blindfold.
|
||||
* Allows putting blindfold on tied-up entities (Players and NPCs).
|
||||
*/
|
||||
@Override
|
||||
public InteractionResult interactLivingEntity(
|
||||
ItemStack stack,
|
||||
Player player,
|
||||
LivingEntity target,
|
||||
InteractionHand hand
|
||||
) {
|
||||
return EquipmentInteractionHelper.equipOnTarget(
|
||||
stack,
|
||||
player,
|
||||
target,
|
||||
state -> state.isBlindfolded(),
|
||||
(state, item) -> state.equip(BodyRegionV2.EYES, item),
|
||||
(state, item) ->
|
||||
state.replaceEquipment(BodyRegionV2.EYES, item, false),
|
||||
(p, t) ->
|
||||
SystemMessageManager.sendToTarget(
|
||||
p,
|
||||
t,
|
||||
SystemMessageManager.MessageCategory.BLINDFOLDED
|
||||
),
|
||||
"ItemBlindfold"
|
||||
);
|
||||
}
|
||||
|
||||
// ILockable implementation inherited from interface default methods
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user