Clean repo for open source release
Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
This commit is contained in:
@@ -0,0 +1,526 @@
|
||||
package com.tiedup.remake.items.clothes;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.network.sync.SyncManager;
|
||||
import com.tiedup.remake.state.IBondageState;
|
||||
import com.tiedup.remake.util.KidnappedHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.v2.bondage.IV2BondageItem;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Generic clothes item with full NBT-based configuration.
|
||||
*
|
||||
* <p>Clothes are cosmetic items that can:
|
||||
* <ul>
|
||||
* <li>Use dynamic textures from URLs</li>
|
||||
* <li>Replace the entire player skin (full-skin mode)</li>
|
||||
* <li>Force slim arm model</li>
|
||||
* <li>Control visibility of wearer's body parts</li>
|
||||
* <li>Be locked with padlocks</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Unlike other bondage items, clothes have NO gameplay effects - they are purely visual.
|
||||
*/
|
||||
public class GenericClothes extends Item implements ILockable, IV2BondageItem {
|
||||
|
||||
// ========== NBT KEYS ==========
|
||||
public static final String NBT_DYNAMIC_TEXTURE = "dynamicTexture";
|
||||
public static final String NBT_FULL_SKIN = "fullSkin";
|
||||
public static final String NBT_SMALL_ARMS = "smallArms";
|
||||
public static final String NBT_KEEP_HEAD = "keepHead";
|
||||
public static final String NBT_LAYER_VISIBILITY = "layerVisibility";
|
||||
public static final String NBT_LOCKED = "locked";
|
||||
public static final String NBT_LOCKABLE = "lockable";
|
||||
public static final String NBT_LOCKED_BY_KEY_UUID = "lockedByKeyUUID";
|
||||
|
||||
// Layer visibility keys
|
||||
public static final String LAYER_HEAD = "head";
|
||||
public static final String LAYER_BODY = "body";
|
||||
public static final String LAYER_LEFT_ARM = "leftArm";
|
||||
public static final String LAYER_RIGHT_ARM = "rightArm";
|
||||
public static final String LAYER_LEFT_LEG = "leftLeg";
|
||||
public static final String LAYER_RIGHT_LEG = "rightLeg";
|
||||
|
||||
public GenericClothes() {
|
||||
super(new Item.Properties().stacksTo(16));
|
||||
}
|
||||
|
||||
// ========== Lifecycle Hooks ==========
|
||||
|
||||
@Override
|
||||
public void onEquipped(ItemStack stack, LivingEntity entity) {
|
||||
// Clothes have no special equip effects - purely cosmetic
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnequipped(ItemStack stack, LivingEntity entity) {
|
||||
// Clothes have no special unequip effects
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when player right-clicks another entity with clothes.
|
||||
* Allows putting clothes on tied-up entities (Players and NPCs).
|
||||
*
|
||||
* Unlike other bondage items, clothes can also be put on non-tied players
|
||||
* if game rules allow it (roleplay scenarios).
|
||||
*
|
||||
* @param stack The item stack
|
||||
* @param player The player using the item
|
||||
* @param target The entity being interacted with
|
||||
* @param hand The hand holding the item
|
||||
* @return SUCCESS if clothes equipped/replaced, PASS otherwise
|
||||
*/
|
||||
@Override
|
||||
public InteractionResult interactLivingEntity(
|
||||
ItemStack stack,
|
||||
Player player,
|
||||
LivingEntity target,
|
||||
InteractionHand hand
|
||||
) {
|
||||
// Server-side only
|
||||
if (player.level().isClientSide) {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
// Check if target can wear clothes (Player, EntityDamsel, EntityKidnapper)
|
||||
IBondageState targetState = KidnappedHelper.getKidnappedState(target);
|
||||
if (targetState == null) {
|
||||
return InteractionResult.PASS; // Entity cannot wear clothes
|
||||
}
|
||||
|
||||
// Unlike gags/blindfolds, clothes can be put on non-tied players too
|
||||
// But if tied, always allowed. If not tied, check if target allows it.
|
||||
if (!targetState.isTiedUp() && !targetState.canChangeClothes(player)) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
// Case 1: No clothes yet - equip new one
|
||||
if (!targetState.hasClothes()) {
|
||||
ItemStack clothesCopy = stack.copyWithCount(1);
|
||||
targetState.equip(BodyRegionV2.TORSO, clothesCopy);
|
||||
stack.shrink(1);
|
||||
|
||||
// Sync equipment to all tracking clients
|
||||
if (target instanceof ServerPlayer serverPlayer) {
|
||||
SyncManager.syncInventory(serverPlayer);
|
||||
SyncManager.syncClothesConfig(serverPlayer);
|
||||
}
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[GenericClothes] {} put clothes on {}",
|
||||
player.getName().getString(),
|
||||
target.getName().getString()
|
||||
);
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
// Case 2: Already has clothes - replace them
|
||||
else {
|
||||
ItemStack clothesCopy = stack.copyWithCount(1);
|
||||
ItemStack oldClothes = targetState.replaceEquipment(
|
||||
BodyRegionV2.TORSO, clothesCopy, false
|
||||
);
|
||||
if (!oldClothes.isEmpty()) {
|
||||
stack.shrink(1);
|
||||
targetState.kidnappedDropItem(oldClothes);
|
||||
|
||||
// Sync equipment to all tracking clients
|
||||
if (target instanceof ServerPlayer serverPlayer) {
|
||||
SyncManager.syncInventory(serverPlayer);
|
||||
SyncManager.syncClothesConfig(serverPlayer);
|
||||
}
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[GenericClothes] {} replaced clothes on {}",
|
||||
player.getName().getString(),
|
||||
target.getName().getString()
|
||||
);
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
// ========== Dynamic Texture Methods ==========
|
||||
|
||||
/**
|
||||
* Get the dynamic texture URL from this clothes item.
|
||||
*
|
||||
* @param stack The ItemStack to check
|
||||
* @return The URL string, or null if not set
|
||||
*/
|
||||
@Nullable
|
||||
public String getDynamicTextureUrl(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
if (tag != null && tag.contains(NBT_DYNAMIC_TEXTURE)) {
|
||||
String url = tag.getString(NBT_DYNAMIC_TEXTURE);
|
||||
return url.isEmpty() ? null : url;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dynamic texture URL for this clothes item.
|
||||
*
|
||||
* @param stack The ItemStack to modify
|
||||
* @param url The URL to set
|
||||
*/
|
||||
public void setDynamicTextureUrl(ItemStack stack, String url) {
|
||||
if (url != null && !url.isEmpty()) {
|
||||
stack.getOrCreateTag().putString(NBT_DYNAMIC_TEXTURE, url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the dynamic texture URL from this clothes item.
|
||||
*
|
||||
* @param stack The ItemStack to modify
|
||||
*/
|
||||
public void removeDynamicTextureUrl(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
if (tag != null) {
|
||||
tag.remove(NBT_DYNAMIC_TEXTURE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this clothes item has a dynamic texture URL set.
|
||||
*
|
||||
* @param stack The ItemStack to check
|
||||
* @return true if a URL is set
|
||||
*/
|
||||
public boolean hasDynamicTextureUrl(ItemStack stack) {
|
||||
return getDynamicTextureUrl(stack) != null;
|
||||
}
|
||||
|
||||
// ========== Full Skin / Small Arms Methods ==========
|
||||
|
||||
/**
|
||||
* Check if full-skin mode is enabled.
|
||||
* In full-skin mode, the clothes texture replaces the entire player skin.
|
||||
*
|
||||
* @param stack The ItemStack to check
|
||||
* @return true if full-skin mode is enabled
|
||||
*/
|
||||
public boolean isFullSkinEnabled(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
return tag != null && tag.getBoolean(NBT_FULL_SKIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set full-skin mode.
|
||||
*
|
||||
* @param stack The ItemStack to modify
|
||||
* @param enabled true to enable full-skin mode
|
||||
*/
|
||||
public void setFullSkinEnabled(ItemStack stack, boolean enabled) {
|
||||
stack.getOrCreateTag().putBoolean(NBT_FULL_SKIN, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if small arms (slim model) should be forced.
|
||||
*
|
||||
* @param stack The ItemStack to check
|
||||
* @return true if small arms should be forced
|
||||
*/
|
||||
public boolean shouldForceSmallArms(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
return tag != null && tag.getBoolean(NBT_SMALL_ARMS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether small arms (slim model) should be forced.
|
||||
*
|
||||
* @param stack The ItemStack to modify
|
||||
* @param enabled true to force small arms
|
||||
*/
|
||||
public void setForceSmallArms(ItemStack stack, boolean enabled) {
|
||||
stack.getOrCreateTag().putBoolean(NBT_SMALL_ARMS, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if keep head mode is enabled.
|
||||
* When enabled, the wearer's head/hat layers are preserved instead of being
|
||||
* replaced by the clothes texture. Useful for keeping the original face.
|
||||
*
|
||||
* @param stack The ItemStack to check
|
||||
* @return true if keep head mode is enabled
|
||||
*/
|
||||
public boolean isKeepHeadEnabled(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
return tag != null && tag.getBoolean(NBT_KEEP_HEAD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set keep head mode.
|
||||
* When enabled, the wearer's head/hat layers are preserved.
|
||||
*
|
||||
* @param stack The ItemStack to modify
|
||||
* @param enabled true to keep the wearer's head
|
||||
*/
|
||||
public void setKeepHeadEnabled(ItemStack stack, boolean enabled) {
|
||||
stack.getOrCreateTag().putBoolean(NBT_KEEP_HEAD, enabled);
|
||||
}
|
||||
|
||||
// ========== Layer Visibility Methods ==========
|
||||
|
||||
/**
|
||||
* Check if a specific body layer is enabled (visible on wearer).
|
||||
* Defaults to true (visible) if not set.
|
||||
*
|
||||
* @param stack The ItemStack to check
|
||||
* @param layer The layer key (use LAYER_* constants)
|
||||
* @return true if the layer is visible
|
||||
*/
|
||||
public boolean isLayerEnabled(ItemStack stack, String layer) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
if (tag == null || !tag.contains(NBT_LAYER_VISIBILITY)) {
|
||||
return true; // Default: all layers visible
|
||||
}
|
||||
CompoundTag layers = tag.getCompound(NBT_LAYER_VISIBILITY);
|
||||
// If not specified, default to visible
|
||||
return !layers.contains(layer) || layers.getBoolean(layer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the visibility of a specific body layer on the wearer.
|
||||
*
|
||||
* @param stack The ItemStack to modify
|
||||
* @param layer The layer key (use LAYER_* constants)
|
||||
* @param enabled true to show the layer, false to hide it
|
||||
*/
|
||||
public void setLayerEnabled(
|
||||
ItemStack stack,
|
||||
String layer,
|
||||
boolean enabled
|
||||
) {
|
||||
CompoundTag tag = stack.getOrCreateTag();
|
||||
CompoundTag layers = tag.contains(NBT_LAYER_VISIBILITY)
|
||||
? tag.getCompound(NBT_LAYER_VISIBILITY)
|
||||
: new CompoundTag();
|
||||
layers.putBoolean(layer, enabled);
|
||||
tag.put(NBT_LAYER_VISIBILITY, layers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all layer visibility settings as a compound tag.
|
||||
*
|
||||
* @param stack The ItemStack to check
|
||||
* @return The layer visibility compound, or null if not set
|
||||
*/
|
||||
@Nullable
|
||||
public CompoundTag getLayerVisibility(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
if (tag != null && tag.contains(NBT_LAYER_VISIBILITY)) {
|
||||
return tag.getCompound(NBT_LAYER_VISIBILITY);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ========== IV2BondageItem Implementation ==========
|
||||
|
||||
private static final Set<BodyRegionV2> REGIONS =
|
||||
Collections.unmodifiableSet(EnumSet.of(BodyRegionV2.TORSO));
|
||||
|
||||
@Override
|
||||
public Set<BodyRegionV2> getOccupiedRegions() {
|
||||
return REGIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public ResourceLocation getModelLocation() {
|
||||
return null; // Clothes use URL-texture rendering, not GLB models
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPosePriority() {
|
||||
return 0; // Cosmetic item, never forces a pose
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEscapeDifficulty() {
|
||||
return 0; // Cosmetic, no struggle resistance
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsColor() {
|
||||
return false; // Color is handled via URL texture, not variant system
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSlimModel() {
|
||||
return false; // Slim/wide is handled via NBT smallArms flag, not model variants
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEquip(ItemStack stack, LivingEntity entity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUnequip(ItemStack stack, LivingEntity entity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ========== ILockable Implementation ==========
|
||||
|
||||
@Override
|
||||
public ItemStack setLocked(ItemStack stack, boolean state) {
|
||||
stack.getOrCreateTag().putBoolean(NBT_LOCKED, state);
|
||||
if (!state) {
|
||||
// When unlocking, clear lock-related data
|
||||
clearLockResistance(stack);
|
||||
setJammed(stack, false);
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocked(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
return tag != null && tag.getBoolean(NBT_LOCKED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack setLockable(ItemStack stack, boolean state) {
|
||||
stack.getOrCreateTag().putBoolean(NBT_LOCKABLE, state);
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLockable(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
// Default to true if not set
|
||||
return (
|
||||
tag == null ||
|
||||
!tag.contains(NBT_LOCKABLE) ||
|
||||
tag.getBoolean(NBT_LOCKABLE)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public UUID getLockedByKeyUUID(ItemStack stack) {
|
||||
CompoundTag tag = stack.getTag();
|
||||
if (tag != null && tag.hasUUID(NBT_LOCKED_BY_KEY_UUID)) {
|
||||
return tag.getUUID(NBT_LOCKED_BY_KEY_UUID);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLockedByKeyUUID(ItemStack stack, @Nullable UUID keyUUID) {
|
||||
CompoundTag tag = stack.getOrCreateTag();
|
||||
if (keyUUID != null) {
|
||||
tag.putUUID(NBT_LOCKED_BY_KEY_UUID, keyUUID);
|
||||
setLocked(stack, true);
|
||||
initializeLockResistance(stack);
|
||||
} else {
|
||||
tag.remove(NBT_LOCKED_BY_KEY_UUID);
|
||||
setLocked(stack, false);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Tooltip ==========
|
||||
|
||||
@Override
|
||||
public void appendHoverText(
|
||||
ItemStack stack,
|
||||
@Nullable Level level,
|
||||
List<Component> tooltip,
|
||||
TooltipFlag flag
|
||||
) {
|
||||
super.appendHoverText(stack, level, tooltip, flag);
|
||||
|
||||
// Dynamic texture info
|
||||
String url = getDynamicTextureUrl(stack);
|
||||
if (url != null) {
|
||||
tooltip.add(
|
||||
Component.translatable(
|
||||
"item.tiedup.clothes.tooltip.has_url"
|
||||
).withStyle(ChatFormatting.GREEN)
|
||||
);
|
||||
if (isFullSkinEnabled(stack)) {
|
||||
tooltip.add(
|
||||
Component.translatable(
|
||||
"item.tiedup.clothes.tooltip.full_skin"
|
||||
).withStyle(ChatFormatting.AQUA)
|
||||
);
|
||||
}
|
||||
if (shouldForceSmallArms(stack)) {
|
||||
tooltip.add(
|
||||
Component.translatable(
|
||||
"item.tiedup.clothes.tooltip.small_arms"
|
||||
).withStyle(ChatFormatting.AQUA)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
tooltip.add(
|
||||
Component.translatable(
|
||||
"item.tiedup.clothes.tooltip.no_url"
|
||||
).withStyle(ChatFormatting.GRAY)
|
||||
);
|
||||
}
|
||||
|
||||
// Layer visibility info
|
||||
CompoundTag layers = getLayerVisibility(stack);
|
||||
if (layers != null) {
|
||||
StringBuilder disabled = new StringBuilder();
|
||||
if (!isLayerEnabled(stack, LAYER_HEAD)) disabled.append("head ");
|
||||
if (!isLayerEnabled(stack, LAYER_BODY)) disabled.append("body ");
|
||||
if (!isLayerEnabled(stack, LAYER_LEFT_ARM)) disabled.append(
|
||||
"L.arm "
|
||||
);
|
||||
if (!isLayerEnabled(stack, LAYER_RIGHT_ARM)) disabled.append(
|
||||
"R.arm "
|
||||
);
|
||||
if (!isLayerEnabled(stack, LAYER_LEFT_LEG)) disabled.append(
|
||||
"L.leg "
|
||||
);
|
||||
if (!isLayerEnabled(stack, LAYER_RIGHT_LEG)) disabled.append(
|
||||
"R.leg "
|
||||
);
|
||||
|
||||
if (!disabled.isEmpty()) {
|
||||
tooltip.add(
|
||||
Component.translatable(
|
||||
"item.tiedup.clothes.tooltip.layers_disabled",
|
||||
disabled.toString().trim()
|
||||
).withStyle(ChatFormatting.YELLOW)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Lock info
|
||||
if (isLocked(stack)) {
|
||||
tooltip.add(
|
||||
Component.translatable("item.tiedup.locked").withStyle(
|
||||
ChatFormatting.RED
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user