feat(D-01/A): poseType, helpers, OWNERSHIP ComponentType (A4, A5, A6)
- DataDrivenItemDefinition: add poseType field, parsed from JSON "pose_type" - PoseTypeHelper: resolves PoseType from V2 definition or V1 ItemBind fallback - BindModeHelper: static bind mode NBT utilities (isBindItem, hasArmsBound, hasLegsBound, cycleBindModeId) — works for V1 and V2 items - CollarHelper: complete static utility class for collar operations with dual-path V2/V1 dispatch (ownership, features, shock, GPS, choke, alert) - ComponentType: add OWNERSHIP enum value - OwnershipComponent: stub class (lifecycle hooks added in next commit)
This commit is contained in:
@@ -0,0 +1,95 @@
|
|||||||
|
package com.tiedup.remake.v2.bondage;
|
||||||
|
|
||||||
|
import com.tiedup.remake.items.base.ItemBind;
|
||||||
|
import com.tiedup.remake.v2.BodyRegionV2;
|
||||||
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
|
||||||
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static utilities for bind mode operations on any bondage item stack (V1 or V2).
|
||||||
|
*
|
||||||
|
* <p>Bind mode determines whether a bind restrains arms, legs, or both.
|
||||||
|
* The mode is stored in the stack's NBT tag {@code "bindMode"}.</p>
|
||||||
|
*/
|
||||||
|
public final class BindModeHelper {
|
||||||
|
|
||||||
|
private BindModeHelper() {}
|
||||||
|
|
||||||
|
private static final String NBT_BIND_MODE = "bindMode";
|
||||||
|
|
||||||
|
public static final String MODE_FULL = "full";
|
||||||
|
public static final String MODE_ARMS = "arms";
|
||||||
|
public static final String MODE_LEGS = "legs";
|
||||||
|
|
||||||
|
private static final String[] MODE_CYCLE = { MODE_FULL, MODE_ARMS, MODE_LEGS };
|
||||||
|
|
||||||
|
private static final Map<String, String> MODE_TRANSLATION_KEYS = Map.of(
|
||||||
|
MODE_FULL, "tiedup.bindmode.full",
|
||||||
|
MODE_ARMS, "tiedup.bindmode.arms",
|
||||||
|
MODE_LEGS, "tiedup.bindmode.legs"
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given stack is a bind item (V2 ARMS-region item or V1 ItemBind).
|
||||||
|
*/
|
||||||
|
public static boolean isBindItem(ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return false;
|
||||||
|
// V2: check data-driven definition
|
||||||
|
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
||||||
|
if (def != null) {
|
||||||
|
return def.occupiedRegions().contains(BodyRegionV2.ARMS);
|
||||||
|
}
|
||||||
|
// V1 fallback
|
||||||
|
return stack.getItem() instanceof ItemBind;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bind mode ID from the stack's NBT.
|
||||||
|
* @return "full", "arms", or "legs" (defaults to "full")
|
||||||
|
*/
|
||||||
|
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_ARMS.equals(value) || MODE_LEGS.equals(value)) return value;
|
||||||
|
return MODE_FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True if arms are restrained (mode is "arms" or "full"). */
|
||||||
|
public static boolean hasArmsBound(ItemStack stack) {
|
||||||
|
String mode = getBindModeId(stack);
|
||||||
|
return MODE_ARMS.equals(mode) || MODE_FULL.equals(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True if legs are restrained (mode is "legs" or "full"). */
|
||||||
|
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.
|
||||||
|
* @return the new mode ID
|
||||||
|
*/
|
||||||
|
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. */
|
||||||
|
public static String getBindModeTranslationKey(ItemStack stack) {
|
||||||
|
return MODE_TRANSLATION_KEYS.getOrDefault(getBindModeId(stack), "tiedup.bindmode.full");
|
||||||
|
}
|
||||||
|
}
|
||||||
371
src/main/java/com/tiedup/remake/v2/bondage/CollarHelper.java
Normal file
371
src/main/java/com/tiedup/remake/v2/bondage/CollarHelper.java
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
package com.tiedup.remake.v2.bondage;
|
||||||
|
|
||||||
|
import com.tiedup.remake.items.ItemChokeCollar;
|
||||||
|
import com.tiedup.remake.items.ItemGpsCollar;
|
||||||
|
import com.tiedup.remake.items.ItemShockCollar;
|
||||||
|
import com.tiedup.remake.items.ItemShockCollarAuto;
|
||||||
|
import com.tiedup.remake.items.base.ItemCollar;
|
||||||
|
import com.tiedup.remake.v2.bondage.component.ChokingComponent;
|
||||||
|
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
||||||
|
import com.tiedup.remake.v2.bondage.component.GpsComponent;
|
||||||
|
import com.tiedup.remake.v2.bondage.component.OwnershipComponent;
|
||||||
|
import com.tiedup.remake.v2.bondage.component.ShockComponent;
|
||||||
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static utility for collar operations bridging V1 (ItemCollar subclasses)
|
||||||
|
* and V2 (data-driven items with OwnershipComponent).
|
||||||
|
*/
|
||||||
|
public final class CollarHelper {
|
||||||
|
|
||||||
|
private CollarHelper() {}
|
||||||
|
|
||||||
|
// ===== DETECTION =====
|
||||||
|
|
||||||
|
// True if the stack is any kind of collar (V2 ownership component or V1 ItemCollar)
|
||||||
|
public static boolean isCollar(ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return false;
|
||||||
|
if (DataDrivenBondageItem.getComponent(stack, ComponentType.OWNERSHIP, OwnershipComponent.class) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return stack.getItem() instanceof ItemCollar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== OWNERSHIP (NBT: "owners") =====
|
||||||
|
|
||||||
|
// Returns all owner UUIDs stored in the collar's "owners" ListTag
|
||||||
|
public static List<UUID> getOwners(ItemStack stack) {
|
||||||
|
return getListUUIDs(stack, "owners");
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if the given UUID is in the owners list
|
||||||
|
public static boolean isOwner(ItemStack stack, UUID uuid) {
|
||||||
|
return hasUUIDInList(stack, "owners", uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if the given player is an owner
|
||||||
|
public static boolean isOwner(ItemStack stack, Player player) {
|
||||||
|
return isOwner(stack, player.getUUID());
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if the collar has at least one owner
|
||||||
|
public static boolean hasOwner(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null || !tag.contains("owners", Tag.TAG_LIST)) return false;
|
||||||
|
return !tag.getList("owners", Tag.TAG_COMPOUND).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds an owner entry {uuid, name} to the "owners" ListTag
|
||||||
|
public static void addOwner(ItemStack stack, UUID uuid, String name) {
|
||||||
|
addToList(stack, "owners", uuid, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience: add a player as owner
|
||||||
|
public static void addOwner(ItemStack stack, Player player) {
|
||||||
|
addOwner(stack, player.getUUID(), player.getGameProfile().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes an owner by UUID
|
||||||
|
public static void removeOwner(ItemStack stack, UUID uuid) {
|
||||||
|
removeFromList(stack, "owners", uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== BLACKLIST (NBT: "blacklist") =====
|
||||||
|
|
||||||
|
public static List<UUID> getBlacklist(ItemStack stack) {
|
||||||
|
return getListUUIDs(stack, "blacklist");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBlacklisted(ItemStack stack, UUID uuid) {
|
||||||
|
return hasUUIDInList(stack, "blacklist", uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addToBlacklist(ItemStack stack, UUID uuid, String name) {
|
||||||
|
addToList(stack, "blacklist", uuid, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeFromBlacklist(ItemStack stack, UUID uuid) {
|
||||||
|
removeFromList(stack, "blacklist", uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== WHITELIST (NBT: "whitelist") =====
|
||||||
|
|
||||||
|
public static List<UUID> getWhitelist(ItemStack stack) {
|
||||||
|
return getListUUIDs(stack, "whitelist");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isWhitelisted(ItemStack stack, UUID uuid) {
|
||||||
|
return hasUUIDInList(stack, "whitelist", uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addToWhitelist(ItemStack stack, UUID uuid, String name) {
|
||||||
|
addToList(stack, "whitelist", uuid, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeFromWhitelist(ItemStack stack, UUID uuid) {
|
||||||
|
removeFromList(stack, "whitelist", uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== LIST INTERNALS =====
|
||||||
|
|
||||||
|
private static List<UUID> getListUUIDs(ItemStack stack, String listKey) {
|
||||||
|
List<UUID> result = new ArrayList<>();
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null || !tag.contains(listKey, Tag.TAG_LIST)) return result;
|
||||||
|
ListTag list = tag.getList(listKey, Tag.TAG_COMPOUND);
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
CompoundTag entry = list.getCompound(i);
|
||||||
|
if (entry.contains("uuid")) {
|
||||||
|
try {
|
||||||
|
result.add(UUID.fromString(entry.getString("uuid")));
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
// Malformed UUID in NBT, skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasUUIDInList(ItemStack stack, String listKey, UUID uuid) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null || !tag.contains(listKey, Tag.TAG_LIST)) return false;
|
||||||
|
String uuidStr = uuid.toString();
|
||||||
|
ListTag list = tag.getList(listKey, Tag.TAG_COMPOUND);
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
if (uuidStr.equals(list.getCompound(i).getString("uuid"))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addToList(ItemStack stack, String listKey, UUID uuid, String name) {
|
||||||
|
CompoundTag tag = stack.getOrCreateTag();
|
||||||
|
ListTag list = tag.contains(listKey, Tag.TAG_LIST)
|
||||||
|
? tag.getList(listKey, Tag.TAG_COMPOUND)
|
||||||
|
: new ListTag();
|
||||||
|
// Prevent duplicates
|
||||||
|
String uuidStr = uuid.toString();
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
if (uuidStr.equals(list.getCompound(i).getString("uuid"))) return;
|
||||||
|
}
|
||||||
|
CompoundTag entry = new CompoundTag();
|
||||||
|
entry.putString("uuid", uuidStr);
|
||||||
|
entry.putString("name", name);
|
||||||
|
list.add(entry);
|
||||||
|
tag.put(listKey, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeFromList(ItemStack stack, String listKey, UUID uuid) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null || !tag.contains(listKey, Tag.TAG_LIST)) return;
|
||||||
|
String uuidStr = uuid.toString();
|
||||||
|
ListTag list = tag.getList(listKey, Tag.TAG_COMPOUND);
|
||||||
|
list.removeIf(element ->
|
||||||
|
uuidStr.equals(((CompoundTag) element).getString("uuid"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== FEATURES =====
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static String getNickname(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null || !tag.contains("nickname")) return null;
|
||||||
|
return tag.getString("nickname");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setNickname(ItemStack stack, String nickname) {
|
||||||
|
stack.getOrCreateTag().putString("nickname", nickname);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasNickname(ItemStack stack) {
|
||||||
|
return stack.hasTag() && stack.getTag().contains("nickname");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static UUID getCellId(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null || !tag.contains("cellId")) return null;
|
||||||
|
try {
|
||||||
|
return UUID.fromString(tag.getString("cellId"));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setCellId(ItemStack stack, UUID cellId) {
|
||||||
|
stack.getOrCreateTag().putString("cellId", cellId.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasCellAssigned(ItemStack stack) {
|
||||||
|
return getCellId(stack) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isKidnappingModeEnabled(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
return tag != null && tag.getBoolean("kidnappingMode");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setKidnappingModeEnabled(ItemStack stack, boolean enabled) {
|
||||||
|
stack.getOrCreateTag().putBoolean("kidnappingMode", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kidnapping mode is ready when enabled AND a cell is assigned
|
||||||
|
public static boolean isKidnappingModeReady(ItemStack stack) {
|
||||||
|
return isKidnappingModeEnabled(stack) && hasCellAssigned(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean shouldTieToPole(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
return tag != null && tag.getBoolean("tieToPole");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setShouldTieToPole(ItemStack stack, boolean value) {
|
||||||
|
stack.getOrCreateTag().putBoolean("tieToPole", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default true when tag is absent or key is missing
|
||||||
|
public static boolean shouldWarnMasters(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null || !tag.contains("warnMasters")) return true;
|
||||||
|
return tag.getBoolean("warnMasters");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setShouldWarnMasters(ItemStack stack, boolean value) {
|
||||||
|
stack.getOrCreateTag().putBoolean("warnMasters", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBondageServiceEnabled(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
return tag != null && tag.getBoolean("bondageservice");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setBondageServiceEnabled(ItemStack stack, boolean enabled) {
|
||||||
|
stack.getOrCreateTag().putBoolean("bondageservice", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static String getServiceSentence(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null || !tag.contains("servicesentence")) return null;
|
||||||
|
return tag.getString("servicesentence");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setServiceSentence(ItemStack stack, String sentence) {
|
||||||
|
stack.getOrCreateTag().putString("servicesentence", sentence);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== SHOCK =====
|
||||||
|
|
||||||
|
// True if the collar can shock (V2 ShockComponent or V1 ItemShockCollar)
|
||||||
|
public static boolean canShock(ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return false;
|
||||||
|
if (DataDrivenBondageItem.getComponent(stack, ComponentType.SHOCK, ShockComponent.class) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return stack.getItem() instanceof ItemShockCollar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPublicShock(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
return tag != null && tag.getBoolean("public_mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPublicShock(ItemStack stack, boolean publicMode) {
|
||||||
|
stack.getOrCreateTag().putBoolean("public_mode", publicMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// V2: from ShockComponent auto interval, V1: from ItemShockCollarAuto field, else 0
|
||||||
|
public static int getShockInterval(ItemStack stack) {
|
||||||
|
ShockComponent comp = DataDrivenBondageItem.getComponent(
|
||||||
|
stack, ComponentType.SHOCK, ShockComponent.class
|
||||||
|
);
|
||||||
|
if (comp != null) return comp.getAutoInterval();
|
||||||
|
if (stack.getItem() instanceof ItemShockCollarAuto auto) {
|
||||||
|
return auto.getInterval();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== GPS =====
|
||||||
|
|
||||||
|
// True if the collar has GPS capabilities
|
||||||
|
public static boolean hasGPS(ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return false;
|
||||||
|
if (DataDrivenBondageItem.getComponent(stack, ComponentType.GPS, GpsComponent.class) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return stack.getItem() instanceof ItemGpsCollar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasPublicTracking(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
return tag != null && tag.getBoolean("publicTracking");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPublicTracking(ItemStack stack, boolean publicTracking) {
|
||||||
|
stack.getOrCreateTag().putBoolean("publicTracking", publicTracking);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GPS active defaults to true when absent
|
||||||
|
public static boolean isActive(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
if (tag == null || !tag.contains("gpsActive")) return true;
|
||||||
|
return tag.getBoolean("gpsActive");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setActive(ItemStack stack, boolean active) {
|
||||||
|
stack.getOrCreateTag().putBoolean("gpsActive", active);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== CHOKE =====
|
||||||
|
|
||||||
|
// True if the collar is a choke collar
|
||||||
|
public static boolean isChokeCollar(ItemStack stack) {
|
||||||
|
if (stack.isEmpty()) return false;
|
||||||
|
if (DataDrivenBondageItem.getComponent(stack, ComponentType.CHOKING, ChokingComponent.class) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return stack.getItem() instanceof ItemChokeCollar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isChoking(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
return tag != null && tag.getBoolean("choking");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setChoking(ItemStack stack, boolean choking) {
|
||||||
|
stack.getOrCreateTag().putBoolean("choking", choking);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPetPlayMode(ItemStack stack) {
|
||||||
|
CompoundTag tag = stack.getTag();
|
||||||
|
return tag != null && tag.getBoolean("petPlayMode");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPetPlayMode(ItemStack stack, boolean petPlay) {
|
||||||
|
stack.getOrCreateTag().putBoolean("petPlayMode", petPlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== ALERT SUPPRESSION =====
|
||||||
|
|
||||||
|
// Executes the action with collar removal alerts suppressed
|
||||||
|
public static void runWithSuppressedAlert(Runnable action) {
|
||||||
|
ItemCollar.runWithSuppressedAlert(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if removal alerts are currently suppressed (ThreadLocal state)
|
||||||
|
public static boolean isRemovalAlertSuppressed() {
|
||||||
|
return ItemCollar.isRemovalAlertSuppressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.tiedup.remake.v2.bondage;
|
||||||
|
|
||||||
|
import com.tiedup.remake.items.base.ItemBind;
|
||||||
|
import com.tiedup.remake.items.base.PoseType;
|
||||||
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
|
||||||
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the {@link PoseType} for any bondage item stack (V1 or V2).
|
||||||
|
*
|
||||||
|
* <p>V2 items read from the data-driven definition's {@code pose_type} field.
|
||||||
|
* V1 items fall back to {@code ItemBind.getPoseType()}.</p>
|
||||||
|
*/
|
||||||
|
public final class PoseTypeHelper {
|
||||||
|
|
||||||
|
private PoseTypeHelper() {}
|
||||||
|
|
||||||
|
public static PoseType getPoseType(ItemStack stack) {
|
||||||
|
// V2: read from data-driven definition
|
||||||
|
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
||||||
|
if (def != null && def.poseType() != null) {
|
||||||
|
try {
|
||||||
|
return PoseType.valueOf(def.poseType().toUpperCase());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return PoseType.STANDARD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// V1 fallback
|
||||||
|
if (stack.getItem() instanceof ItemBind bind) {
|
||||||
|
return bind.getPoseType();
|
||||||
|
}
|
||||||
|
return PoseType.STANDARD;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,8 @@ public enum ComponentType {
|
|||||||
SHOCK("shock", ShockComponent::fromJson),
|
SHOCK("shock", ShockComponent::fromJson),
|
||||||
GPS("gps", GpsComponent::fromJson),
|
GPS("gps", GpsComponent::fromJson),
|
||||||
CHOKING("choking", ChokingComponent::fromJson),
|
CHOKING("choking", ChokingComponent::fromJson),
|
||||||
ADJUSTABLE("adjustable", AdjustableComponent::fromJson);
|
ADJUSTABLE("adjustable", AdjustableComponent::fromJson),
|
||||||
|
OWNERSHIP("ownership", OwnershipComponent::fromJson);
|
||||||
|
|
||||||
private final String jsonKey;
|
private final String jsonKey;
|
||||||
private final Function<JsonObject, IItemComponent> factory;
|
private final Function<JsonObject, IItemComponent> factory;
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.tiedup.remake.v2.bondage.component;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component: collar ownership behavior for data-driven items.
|
||||||
|
*
|
||||||
|
* <p>Marks an item as a collar with ownership capabilities.
|
||||||
|
* Lifecycle hooks handle CollarRegistry registration/unregistration.</p>
|
||||||
|
*
|
||||||
|
* <p>JSON config: {@code "ownership": {}}</p>
|
||||||
|
*/
|
||||||
|
public class OwnershipComponent implements IItemComponent {
|
||||||
|
|
||||||
|
private OwnershipComponent() {}
|
||||||
|
|
||||||
|
public static IItemComponent fromJson(JsonObject config) {
|
||||||
|
return new OwnershipComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,9 @@ public record DataDrivenItemDefinition(
|
|||||||
/** Body regions this item blocks. Defaults to occupiedRegions if not specified. */
|
/** Body regions this item blocks. Defaults to occupiedRegions if not specified. */
|
||||||
Set<BodyRegionV2> blockedRegions,
|
Set<BodyRegionV2> blockedRegions,
|
||||||
|
|
||||||
|
/** Optional pose type identifier (e.g., "STANDARD", "STRAITJACKET", "DOG"). */
|
||||||
|
@Nullable String poseType,
|
||||||
|
|
||||||
/** Pose priority for conflict resolution. Higher wins. */
|
/** Pose priority for conflict resolution. Higher wins. */
|
||||||
int posePriority,
|
int posePriority,
|
||||||
|
|
||||||
|
|||||||
@@ -188,6 +188,9 @@ public final class DataDrivenItemParser {
|
|||||||
blockedRegions = occupiedRegions;
|
blockedRegions = occupiedRegions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optional: pose_type (e.g., "STANDARD", "STRAITJACKET", "DOG")
|
||||||
|
String poseType = getStringOrNull(root, "pose_type");
|
||||||
|
|
||||||
// Optional: pose_priority (default 0)
|
// Optional: pose_priority (default 0)
|
||||||
int posePriority = getIntOrDefault(root, "pose_priority", 0);
|
int posePriority = getIntOrDefault(root, "pose_priority", 0);
|
||||||
|
|
||||||
@@ -328,6 +331,7 @@ public final class DataDrivenItemParser {
|
|||||||
animationSource,
|
animationSource,
|
||||||
occupiedRegions,
|
occupiedRegions,
|
||||||
blockedRegions,
|
blockedRegions,
|
||||||
|
poseType,
|
||||||
posePriority,
|
posePriority,
|
||||||
escapeDifficulty,
|
escapeDifficulty,
|
||||||
lockable,
|
lockable,
|
||||||
|
|||||||
Reference in New Issue
Block a user