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.
319 lines
9.0 KiB
Java
319 lines
9.0 KiB
Java
package com.tiedup.remake.blocks.entity;
|
|
|
|
import com.tiedup.remake.items.clothes.GenericClothes;
|
|
import com.tiedup.remake.v2.BodyRegionV2;
|
|
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
|
import com.tiedup.remake.v2.bondage.CollarHelper;
|
|
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
|
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
|
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
|
|
/**
|
|
* Base BlockEntity for blocks that store bondage items.
|
|
*
|
|
*
|
|
* Stores up to 6 bondage items:
|
|
* - Bind (ropes, chains, straitjacket, etc.)
|
|
* - Gag
|
|
* - Blindfold
|
|
* - Earplugs
|
|
* - Collar
|
|
* - Clothes
|
|
*
|
|
* Features:
|
|
* - Full NBT serialization
|
|
* - Network synchronization for client rendering
|
|
* - Item type validation on load
|
|
*
|
|
* Based on original TileEntityBondageItemHandler from 1.12.2
|
|
*/
|
|
public abstract class BondageItemBlockEntity
|
|
extends BlockEntity
|
|
implements IBondageItemHolder
|
|
{
|
|
|
|
// STORED ITEMS
|
|
|
|
private ItemStack bind = ItemStack.EMPTY;
|
|
private ItemStack gag = ItemStack.EMPTY;
|
|
private ItemStack blindfold = ItemStack.EMPTY;
|
|
private ItemStack earplugs = ItemStack.EMPTY;
|
|
private ItemStack collar = ItemStack.EMPTY;
|
|
private ItemStack clothes = ItemStack.EMPTY;
|
|
|
|
/**
|
|
* Off-mode prevents network updates.
|
|
* Used when reading NBT for tooltips without affecting the world.
|
|
*/
|
|
private final boolean offMode;
|
|
|
|
// CONSTRUCTORS
|
|
|
|
public BondageItemBlockEntity(
|
|
BlockEntityType<?> type,
|
|
BlockPos pos,
|
|
BlockState state
|
|
) {
|
|
this(type, pos, state, false);
|
|
}
|
|
|
|
public BondageItemBlockEntity(
|
|
BlockEntityType<?> type,
|
|
BlockPos pos,
|
|
BlockState state,
|
|
boolean offMode
|
|
) {
|
|
super(type, pos, state);
|
|
this.offMode = offMode;
|
|
}
|
|
|
|
// BIND
|
|
|
|
@Override
|
|
public ItemStack getBind() {
|
|
return this.bind;
|
|
}
|
|
|
|
@Override
|
|
public void setBind(ItemStack bind) {
|
|
this.bind = bind != null ? bind : ItemStack.EMPTY;
|
|
this.setChangedAndSync();
|
|
}
|
|
|
|
// GAG
|
|
|
|
@Override
|
|
public ItemStack getGag() {
|
|
return this.gag;
|
|
}
|
|
|
|
@Override
|
|
public void setGag(ItemStack gag) {
|
|
this.gag = gag != null ? gag : ItemStack.EMPTY;
|
|
this.setChangedAndSync();
|
|
}
|
|
|
|
// BLINDFOLD
|
|
|
|
@Override
|
|
public ItemStack getBlindfold() {
|
|
return this.blindfold;
|
|
}
|
|
|
|
@Override
|
|
public void setBlindfold(ItemStack blindfold) {
|
|
this.blindfold = blindfold != null ? blindfold : ItemStack.EMPTY;
|
|
this.setChangedAndSync();
|
|
}
|
|
|
|
// EARPLUGS
|
|
|
|
@Override
|
|
public ItemStack getEarplugs() {
|
|
return this.earplugs;
|
|
}
|
|
|
|
@Override
|
|
public void setEarplugs(ItemStack earplugs) {
|
|
this.earplugs = earplugs != null ? earplugs : ItemStack.EMPTY;
|
|
this.setChangedAndSync();
|
|
}
|
|
|
|
// COLLAR
|
|
|
|
@Override
|
|
public ItemStack getCollar() {
|
|
return this.collar;
|
|
}
|
|
|
|
@Override
|
|
public void setCollar(ItemStack collar) {
|
|
this.collar = collar != null ? collar : ItemStack.EMPTY;
|
|
this.setChangedAndSync();
|
|
}
|
|
|
|
// CLOTHES
|
|
|
|
@Override
|
|
public ItemStack getClothes() {
|
|
return this.clothes;
|
|
}
|
|
|
|
@Override
|
|
public void setClothes(ItemStack clothes) {
|
|
this.clothes = clothes != null ? clothes : ItemStack.EMPTY;
|
|
this.setChangedAndSync();
|
|
}
|
|
|
|
// STATE
|
|
|
|
@Override
|
|
public boolean isArmed() {
|
|
return !this.bind.isEmpty();
|
|
}
|
|
|
|
/**
|
|
* Clear all stored bondage items.
|
|
* Called after applying items to a target.
|
|
*/
|
|
public void clearAllItems() {
|
|
this.bind = ItemStack.EMPTY;
|
|
this.gag = ItemStack.EMPTY;
|
|
this.blindfold = ItemStack.EMPTY;
|
|
this.earplugs = ItemStack.EMPTY;
|
|
this.collar = ItemStack.EMPTY;
|
|
this.clothes = ItemStack.EMPTY;
|
|
this.setChangedAndSync();
|
|
}
|
|
|
|
// NBT SERIALIZATION
|
|
|
|
@Override
|
|
public void load(CompoundTag tag) {
|
|
super.load(tag);
|
|
this.readBondageData(tag);
|
|
}
|
|
|
|
@Override
|
|
protected void saveAdditional(CompoundTag tag) {
|
|
super.saveAdditional(tag);
|
|
this.writeBondageData(tag);
|
|
}
|
|
|
|
@Override
|
|
public void readBondageData(CompoundTag tag) {
|
|
// Read bind with type validation (V2 ARMS-region item)
|
|
if (tag.contains("bind")) {
|
|
ItemStack bindStack = ItemStack.of(tag.getCompound("bind"));
|
|
if (!bindStack.isEmpty() && BindModeHelper.isBindItem(bindStack)) {
|
|
this.bind = bindStack;
|
|
}
|
|
}
|
|
|
|
// Read gag with type validation (V2 GAGGING component)
|
|
if (tag.contains("gag")) {
|
|
ItemStack gagStack = ItemStack.of(tag.getCompound("gag"));
|
|
if (!gagStack.isEmpty()
|
|
&& DataDrivenBondageItem.getComponent(gagStack, ComponentType.GAGGING, GaggingComponent.class) != null) {
|
|
this.gag = gagStack;
|
|
}
|
|
}
|
|
|
|
// Read blindfold with type validation (V2 EYES-region item)
|
|
if (tag.contains("blindfold")) {
|
|
ItemStack blindfoldStack = ItemStack.of(tag.getCompound("blindfold"));
|
|
if (!blindfoldStack.isEmpty() && isDataDrivenForRegion(blindfoldStack, BodyRegionV2.EYES)) {
|
|
this.blindfold = blindfoldStack;
|
|
}
|
|
}
|
|
|
|
// Read earplugs with type validation (V2 EARS-region item)
|
|
if (tag.contains("earplugs")) {
|
|
ItemStack earplugsStack = ItemStack.of(tag.getCompound("earplugs"));
|
|
if (!earplugsStack.isEmpty() && isDataDrivenForRegion(earplugsStack, BodyRegionV2.EARS)) {
|
|
this.earplugs = earplugsStack;
|
|
}
|
|
}
|
|
|
|
// Read collar with type validation (V2 collar)
|
|
if (tag.contains("collar")) {
|
|
ItemStack collarStack = ItemStack.of(tag.getCompound("collar"));
|
|
if (!collarStack.isEmpty() && CollarHelper.isCollar(collarStack)) {
|
|
this.collar = collarStack;
|
|
}
|
|
}
|
|
|
|
// Read clothes with type validation
|
|
if (tag.contains("clothes")) {
|
|
ItemStack clothesStack = ItemStack.of(tag.getCompound("clothes"));
|
|
if (
|
|
!clothesStack.isEmpty() &&
|
|
clothesStack.getItem() instanceof GenericClothes
|
|
) {
|
|
this.clothes = clothesStack;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public CompoundTag writeBondageData(CompoundTag tag) {
|
|
if (!this.bind.isEmpty()) {
|
|
tag.put("bind", this.bind.save(new CompoundTag()));
|
|
}
|
|
if (!this.gag.isEmpty()) {
|
|
tag.put("gag", this.gag.save(new CompoundTag()));
|
|
}
|
|
if (!this.blindfold.isEmpty()) {
|
|
tag.put("blindfold", this.blindfold.save(new CompoundTag()));
|
|
}
|
|
if (!this.earplugs.isEmpty()) {
|
|
tag.put("earplugs", this.earplugs.save(new CompoundTag()));
|
|
}
|
|
if (!this.collar.isEmpty()) {
|
|
tag.put("collar", this.collar.save(new CompoundTag()));
|
|
}
|
|
if (!this.clothes.isEmpty()) {
|
|
tag.put("clothes", this.clothes.save(new CompoundTag()));
|
|
}
|
|
return tag;
|
|
}
|
|
|
|
// V2 HELPERS
|
|
|
|
/** Check if a stack is a data-driven item occupying the given body region. */
|
|
private static boolean isDataDrivenForRegion(ItemStack stack, BodyRegionV2 region) {
|
|
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
|
return def != null && def.occupiedRegions().contains(region);
|
|
}
|
|
|
|
// NETWORK SYNC
|
|
|
|
/**
|
|
* Mark dirty and sync to clients.
|
|
*/
|
|
protected void setChangedAndSync() {
|
|
if (!this.offMode && this.level != null) {
|
|
this.setChanged();
|
|
// Notify clients of block update
|
|
this.level.sendBlockUpdated(
|
|
this.worldPosition,
|
|
this.getBlockState(),
|
|
this.getBlockState(),
|
|
3
|
|
);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public CompoundTag getUpdateTag() {
|
|
CompoundTag tag = super.getUpdateTag();
|
|
this.writeBondageData(tag);
|
|
return tag;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
|
return ClientboundBlockEntityDataPacket.create(this);
|
|
}
|
|
|
|
@Override
|
|
public void handleUpdateTag(CompoundTag tag) {
|
|
if (!this.offMode) {
|
|
this.readBondageData(tag);
|
|
}
|
|
}
|
|
}
|