feat(D-01/D): V1 cleanup — delete 28 files, ~5400 lines removed
D1: ThreadLocal alert suppression moved from ItemCollar to CollarHelper.
onCollarRemoved() logic (kidnapper alert) moved to CollarHelper.
D2+D3: Deleted 17 V1 item classes + 4 V1-only interfaces:
ItemBind, ItemGag, ItemBlindfold, ItemCollar, ItemEarplugs, ItemMittens,
ItemColor, ItemClassicCollar, ItemShockCollar, ItemShockCollarAuto,
ItemGpsCollar, ItemChokeCollar, ItemHood, ItemMedicalGag,
IBondageItem, IHasGaggingEffect, IHasBlindingEffect, IAdjustable
D4: KidnapperTheme/KidnapperItemSelector/DispenserBehaviors migrated
from variant enums to string-based DataDrivenItemRegistry IDs.
D5: Deleted 11 variant enums + Generic* factories + ItemBallGag3D:
BindVariant, GagVariant, BlindfoldVariant, EarplugsVariant, MittensVariant,
GenericBind, GenericGag, GenericBlindfold, GenericEarplugs, GenericMittens
D6: ModItems cleaned — all V1 bondage registrations removed.
D7: ModCreativeTabs rewritten — iterates DataDrivenItemRegistry.
D8+D9: All V2 helpers cleaned (V1 fallbacks removed), orphan imports removed.
Zero V1 bondage code references remain (only Javadoc comments).
All bondage items are now data-driven via 47 JSON definitions.
This commit is contained in:
@@ -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
|
||||
}
|
||||
@@ -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
@@ -1,90 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.util.EquipmentInteractionHelper;
|
||||
import com.tiedup.remake.util.TiedUpSounds;
|
||||
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 earplug items.
|
||||
* These items block or reduce sounds when equipped.
|
||||
*
|
||||
* Based on original ItemEarplugs from 1.12.2
|
||||
*
|
||||
* Phase future: Sound blocking effect
|
||||
*/
|
||||
public abstract class ItemEarplugs
|
||||
extends Item
|
||||
implements IBondageItem, ILockable
|
||||
{
|
||||
|
||||
public ItemEarplugs(Properties properties) {
|
||||
super(properties.stacksTo(16)); // Earplugs can stack to 16
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyRegionV2 getBodyRegion() {
|
||||
return BodyRegionV2.EARS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(
|
||||
ItemStack stack,
|
||||
@Nullable Level level,
|
||||
List<Component> tooltip,
|
||||
TooltipFlag flag
|
||||
) {
|
||||
super.appendHoverText(stack, level, tooltip, flag);
|
||||
appendLockTooltip(stack, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when player right-clicks another entity with earplugs.
|
||||
* Allows putting earplugs 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.hasEarplugs(),
|
||||
(state, item) -> state.equip(BodyRegionV2.EARS, item),
|
||||
(state, item) ->
|
||||
state.replaceEquipment(BodyRegionV2.EARS, item, false),
|
||||
(p, t) ->
|
||||
SystemMessageManager.sendToTarget(
|
||||
p,
|
||||
t,
|
||||
SystemMessageManager.MessageCategory.EARPLUGS_ON
|
||||
),
|
||||
"ItemEarplugs",
|
||||
null, // No pre-equip hook
|
||||
(s, p, t, state) -> TiedUpSounds.playEarplugsEquipSound(t), // Post-equip: play sound
|
||||
null // No replace check
|
||||
);
|
||||
}
|
||||
|
||||
// Sound blocking implemented in:
|
||||
// - client/events/EarplugSoundHandler.java (event interception)
|
||||
// - client/MuffledSoundInstance.java (volume/pitch wrapper)
|
||||
// - Configurable via ModConfig.CLIENT.earplugVolumeMultiplier
|
||||
|
||||
// ILockable implementation inherited from interface default methods
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.util.EquipmentInteractionHelper;
|
||||
import com.tiedup.remake.util.GagMaterial;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import java.util.List;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.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 gag items (ball gag, cloth gag, tape, etc.)
|
||||
* These items prevent or muffle a player's speech when equipped.
|
||||
*
|
||||
* Based on original ItemGag from 1.12.2
|
||||
*
|
||||
*/
|
||||
public abstract class ItemGag
|
||||
extends Item
|
||||
implements IBondageItem, IHasGaggingEffect, IAdjustable, ILockable
|
||||
{
|
||||
|
||||
private final GagMaterial material;
|
||||
|
||||
public ItemGag(Properties properties, GagMaterial material) {
|
||||
super(properties);
|
||||
this.material = material;
|
||||
}
|
||||
|
||||
public GagMaterial getGagMaterial() {
|
||||
return this.material;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyRegionV2 getBodyRegion() {
|
||||
return BodyRegionV2.MOUTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(
|
||||
ItemStack stack,
|
||||
@Nullable Level level,
|
||||
List<Component> tooltip,
|
||||
TooltipFlag flag
|
||||
) {
|
||||
super.appendHoverText(stack, level, tooltip, flag);
|
||||
appendLockTooltip(stack, tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* All gags can be adjusted to better fit player skins.
|
||||
* @return true - gags support position adjustment
|
||||
*/
|
||||
@Override
|
||||
public boolean canBeAdjusted() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when player right-clicks another entity with this gag.
|
||||
* Allows putting gag 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.isGagged(),
|
||||
(state, item) -> state.equip(BodyRegionV2.MOUTH, item),
|
||||
(state, item) ->
|
||||
state.replaceEquipment(BodyRegionV2.MOUTH, item, false),
|
||||
SystemMessageManager::sendGagged,
|
||||
"ItemGag"
|
||||
);
|
||||
}
|
||||
|
||||
// ILockable implementation inherited from interface default methods
|
||||
}
|
||||
@@ -1,72 +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 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;
|
||||
|
||||
/**
|
||||
* Base class for mittens items.
|
||||
* These items block hand interactions (mining, placing, using items) when equipped.
|
||||
*
|
||||
*
|
||||
* Restrictions when wearing mittens:
|
||||
* - Cannot mine/break blocks
|
||||
* - Cannot place blocks
|
||||
* - Cannot use items
|
||||
* - Cannot attack (0 damage punch allowed)
|
||||
*
|
||||
* Allowed:
|
||||
* - Push buttons/levers
|
||||
* - Open doors
|
||||
*/
|
||||
public abstract class ItemMittens
|
||||
extends Item
|
||||
implements IBondageItem, ILockable
|
||||
{
|
||||
|
||||
public ItemMittens(Properties properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyRegionV2 getBodyRegion() {
|
||||
return BodyRegionV2.HANDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when player right-clicks another entity with these mittens.
|
||||
* Allows putting mittens 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.hasMittens(),
|
||||
(state, item) -> state.equip(BodyRegionV2.HANDS, item),
|
||||
(state, item) ->
|
||||
state.replaceEquipment(BodyRegionV2.HANDS, item, false),
|
||||
(p, t) ->
|
||||
SystemMessageManager.sendToTarget(
|
||||
p,
|
||||
t,
|
||||
SystemMessageManager.MessageCategory.MITTENS_ON
|
||||
),
|
||||
"ItemMittens"
|
||||
);
|
||||
}
|
||||
|
||||
// ILockable implementation inherited from interface default methods
|
||||
}
|
||||
@@ -195,11 +195,11 @@ public abstract class ItemOwnerTarget extends Item {
|
||||
BodyRegionV2.NECK
|
||||
);
|
||||
if (
|
||||
collar.getItem() instanceof ItemCollar collarItem &&
|
||||
collarItem.hasNickname(collar)
|
||||
com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collar) &&
|
||||
com.tiedup.remake.v2.bondage.CollarHelper.hasNickname(collar)
|
||||
) {
|
||||
displayName =
|
||||
collarItem.getNickname(collar) +
|
||||
com.tiedup.remake.v2.bondage.CollarHelper.getNickname(collar) +
|
||||
" (" +
|
||||
displayName +
|
||||
")";
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.tiedup.remake.items.base;
|
||||
|
||||
/**
|
||||
* Enum defining all mittens variants.
|
||||
* Used by GenericMittens to create mittens items via factory pattern.
|
||||
*
|
||||
* <p>Mittens system - blocks hand interactions when equipped.
|
||||
*
|
||||
* <p><b>Issue #12 fix:</b> Added textureSubfolder to eliminate string checks in renderers.
|
||||
*/
|
||||
public enum MittensVariant {
|
||||
LEATHER("mittens", "mittens");
|
||||
|
||||
private final String registryName;
|
||||
private final String textureSubfolder;
|
||||
|
||||
MittensVariant(String registryName, String textureSubfolder) {
|
||||
this.registryName = registryName;
|
||||
this.textureSubfolder = textureSubfolder;
|
||||
}
|
||||
|
||||
public String getRegistryName() {
|
||||
return registryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the texture subfolder for this mittens variant.
|
||||
* Used by renderers to locate texture files.
|
||||
*
|
||||
* @return Subfolder path under textures/entity/bondage/ (e.g., "mittens")
|
||||
*/
|
||||
public String getTextureSubfolder() {
|
||||
return textureSubfolder;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package com.tiedup.remake.items.bondage3d.gags;
|
||||
|
||||
import com.tiedup.remake.items.base.ItemGag;
|
||||
import com.tiedup.remake.items.bondage3d.IHas3DModelConfig;
|
||||
import com.tiedup.remake.items.bondage3d.Model3DConfig;
|
||||
import com.tiedup.remake.util.GagMaterial;
|
||||
import java.util.Set;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Ball Gag 3D - Extends ItemGag with 3D OBJ model rendering.
|
||||
* All 3D configuration is defined here.
|
||||
* Supports color variants via tinting the "Ball" material.
|
||||
*/
|
||||
public class ItemBallGag3D extends ItemGag implements IHas3DModelConfig {
|
||||
|
||||
// 3D config with "Ball" material tintable for color variants
|
||||
private static final Model3DConfig CONFIG = new Model3DConfig(
|
||||
"tiedup:models/obj/ball_gag/model.obj", // OBJ
|
||||
"tiedup:models/obj/ball_gag/texture.png", // Explicit texture
|
||||
0.0f, // posX
|
||||
1.55f, // posY
|
||||
0.0f, // posZ
|
||||
1.0f, // scale
|
||||
0.0f,
|
||||
0.0f,
|
||||
180.0f, // rotation
|
||||
Set.of("Ball") // Tintable materials (for color variants)
|
||||
);
|
||||
|
||||
public ItemBallGag3D() {
|
||||
super(new Item.Properties().stacksTo(16), GagMaterial.BALL);
|
||||
}
|
||||
|
||||
// ===== 3D Model Support =====
|
||||
|
||||
@Override
|
||||
public boolean uses3DModel() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ResourceLocation get3DModelLocation() {
|
||||
return ResourceLocation.tryParse(CONFIG.objPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the complete 3D configuration for the renderer.
|
||||
*/
|
||||
@Override
|
||||
public Model3DConfig getModelConfig() {
|
||||
return CONFIG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicit texture (if non-null, overrides MTL map_Kd).
|
||||
*/
|
||||
@Nullable
|
||||
public ResourceLocation getExplicitTexture() {
|
||||
String path = CONFIG.texturePath();
|
||||
return path != null ? ResourceLocation.tryParse(path) : null;
|
||||
}
|
||||
|
||||
// ===== Gag Properties =====
|
||||
|
||||
@Override
|
||||
public String getTextureSubfolder() {
|
||||
return "ballgags/normal"; // Fallback if 3D fails
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAttachPadlock() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user