feat(D-01/E): resistance & lock system rework (E1-E7)
E1: Initialize currentResistance in NBT at equip time from
ResistanceComponent — eliminates MAX-scan fallback bug
E2: BuiltInLockComponent for organic items (already committed)
E3: canStruggle refactor — new model:
- ARMS: always struggle-able (no lock gating)
- Non-ARMS: only if locked OR built-in lock
- Removed dead isItemLocked() from StruggleState + overrides
E4: canUnequip already handled by BuiltInLockComponent.blocksUnequip()
via ComponentHolder delegation
E5: Help/assist mechanic deferred (needs UI design)
E6: Removed lock resistance from ILockable (5 methods + NBT key deleted)
- GenericKnife: new knifeCutProgress NBT for cutting locks
- StruggleAccessory: accessoryStruggleResistance NBT replaces lock resistance
- PacketV2StruggleStart: uses config-based padlock resistance
- All lock/unlock packets cleaned of initializeLockResistance/clearLockResistance
E7: Fixed 3 pre-existing bugs:
- B2: DataDrivenItemRegistry.clear() synchronized on RELOAD_LOCK
- B3: V2TyingPlayerTask validates heldStack before equip (prevents duplication)
- B5: EntityKidnapperMerchant.remove() cleans playerToMerchant map (memory leak)
This commit is contained in:
@@ -889,6 +889,10 @@ public class EntityKidnapperMerchant extends EntityKidnapperElite {
|
|||||||
// Clear trading players to prevent dangling references
|
// Clear trading players to prevent dangling references
|
||||||
if (!this.level().isClientSide) {
|
if (!this.level().isClientSide) {
|
||||||
int count = tradingPlayers.size();
|
int count = tradingPlayers.size();
|
||||||
|
// Clean up reverse-lookup map BEFORE clearing to prevent memory leak
|
||||||
|
for (UUID playerUuid : tradingPlayers) {
|
||||||
|
playerToMerchant.remove(playerUuid);
|
||||||
|
}
|
||||||
this.tradingPlayers.clear();
|
this.tradingPlayers.clear();
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
TiedUpMod.LOGGER.debug(
|
TiedUpMod.LOGGER.debug(
|
||||||
|
|||||||
@@ -384,26 +384,27 @@ public class GenericKnife extends Item implements IKnife {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessory IS locked - reduce lock resistance
|
// Accessory IS locked - reduce lock resistance via knife cut progress
|
||||||
ILockable lockable = (ILockable) accessory.getItem();
|
ILockable lockable = (ILockable) accessory.getItem();
|
||||||
int currentRes = lockable.getCurrentLockResistance(accessory);
|
int cutProgress = com.tiedup.remake.util.ItemNBTHelper.getInt(accessory, "knifeCutProgress");
|
||||||
int newRes = Math.max(0, currentRes - speed);
|
int newProgress = cutProgress + speed;
|
||||||
lockable.setCurrentLockResistance(accessory, newRes);
|
int lockResistance = com.tiedup.remake.core.SettingsAccessor.getPadlockResistance(null);
|
||||||
|
com.tiedup.remake.util.ItemNBTHelper.setInt(accessory, "knifeCutProgress", newProgress);
|
||||||
|
|
||||||
TiedUpMod.LOGGER.debug(
|
TiedUpMod.LOGGER.debug(
|
||||||
"[GenericKnife] {} cutting {} lock: resistance {} -> {}",
|
"[GenericKnife] {} cutting {} lock: progress {} / {}",
|
||||||
player.getName().getString(),
|
player.getName().getString(),
|
||||||
target,
|
target,
|
||||||
currentRes,
|
newProgress,
|
||||||
newRes
|
lockResistance
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check if lock is destroyed
|
// Check if lock is destroyed
|
||||||
if (newRes <= 0) {
|
if (newProgress >= lockResistance) {
|
||||||
// Destroy the lock (remove padlock, clear lock state)
|
// Destroy the lock (remove padlock, clear lock state)
|
||||||
lockable.setLockedByKeyUUID(accessory, null); // Unlocks and clears locked state
|
lockable.setLockedByKeyUUID(accessory, null); // Unlocks and clears locked state
|
||||||
lockable.setLockable(accessory, false); // Remove padlock entirely
|
lockable.setLockable(accessory, false); // Remove padlock entirely
|
||||||
lockable.clearLockResistance(accessory);
|
com.tiedup.remake.util.ItemNBTHelper.remove(accessory, "knifeCutProgress");
|
||||||
lockable.setJammed(accessory, false);
|
lockable.setJammed(accessory, false);
|
||||||
|
|
||||||
state.clearKnifeCutTarget();
|
state.clearKnifeCutTarget();
|
||||||
|
|||||||
@@ -214,7 +214,6 @@ public class ItemLockpick extends Item {
|
|||||||
if (success) {
|
if (success) {
|
||||||
// SUCCESS: Unlock the item, PRESERVE the padlock
|
// SUCCESS: Unlock the item, PRESERVE the padlock
|
||||||
lockable.setLockedByKeyUUID(targetStack, null); // Unlock
|
lockable.setLockedByKeyUUID(targetStack, null); // Unlock
|
||||||
lockable.clearLockResistance(targetStack); // Clear struggle progress
|
|
||||||
// lockable stays true - padlock preserved!
|
// lockable stays true - padlock preserved!
|
||||||
|
|
||||||
SystemMessageManager.sendToPlayer(
|
SystemMessageManager.sendToPlayer(
|
||||||
|
|||||||
@@ -222,20 +222,6 @@ public interface ILockable {
|
|||||||
*/
|
*/
|
||||||
String NBT_JAMMED = "jammed";
|
String NBT_JAMMED = "jammed";
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the resistance added by the lock for struggle mechanics.
|
|
||||||
*
|
|
||||||
* <p>When locked, this value is added to the item's base resistance.
|
|
||||||
* Configurable via server config and GameRule.</p>
|
|
||||||
*
|
|
||||||
* @return Lock resistance value (default: 250, configurable)
|
|
||||||
*/
|
|
||||||
default int getLockResistance() {
|
|
||||||
return com.tiedup.remake.core.SettingsAccessor.getPadlockResistance(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the lock is jammed (lockpick failed critically).
|
* Check if the lock is jammed (lockpick failed critically).
|
||||||
*
|
*
|
||||||
@@ -266,63 +252,6 @@ public interface ILockable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== LOCK RESISTANCE (for struggle) ==========
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NBT key for current lock resistance during struggle.
|
|
||||||
*/
|
|
||||||
String NBT_LOCK_RESISTANCE = "lockResistance";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current lock resistance remaining for struggle.
|
|
||||||
* Initialized to getLockResistance() (configurable, default 250) when first locked.
|
|
||||||
*
|
|
||||||
* @param stack The ItemStack to check
|
|
||||||
* @return Current lock resistance (0 if not locked or fully struggled)
|
|
||||||
*/
|
|
||||||
default int getCurrentLockResistance(ItemStack stack) {
|
|
||||||
if (stack.isEmpty()) return 0;
|
|
||||||
|
|
||||||
// If locked but no resistance stored yet, initialize it
|
|
||||||
if (
|
|
||||||
isLocked(stack) &&
|
|
||||||
!ItemNBTHelper.contains(stack, NBT_LOCK_RESISTANCE)
|
|
||||||
) {
|
|
||||||
return getLockResistance(); // Configurable via ModConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
return ItemNBTHelper.getInt(stack, NBT_LOCK_RESISTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current lock resistance remaining for struggle.
|
|
||||||
*
|
|
||||||
* @param stack The ItemStack to modify
|
|
||||||
* @param resistance The new resistance value
|
|
||||||
*/
|
|
||||||
default void setCurrentLockResistance(ItemStack stack, int resistance) {
|
|
||||||
ItemNBTHelper.setInt(stack, NBT_LOCK_RESISTANCE, resistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize lock resistance when item is locked.
|
|
||||||
* Called when setLockedByKeyUUID is called with a non-null UUID.
|
|
||||||
*
|
|
||||||
* @param stack The ItemStack to initialize
|
|
||||||
*/
|
|
||||||
default void initializeLockResistance(ItemStack stack) {
|
|
||||||
setCurrentLockResistance(stack, getLockResistance());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear lock resistance when item is unlocked.
|
|
||||||
*
|
|
||||||
* @param stack The ItemStack to clear
|
|
||||||
*/
|
|
||||||
default void clearLockResistance(ItemStack stack) {
|
|
||||||
ItemNBTHelper.remove(stack, NBT_LOCK_RESISTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== LOCK BREAKING (struggle/force) ==========
|
// ========== LOCK BREAKING (struggle/force) ==========
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -334,7 +263,6 @@ public interface ILockable {
|
|||||||
* <li>Unlocks the item (lockedByKeyUUID = null)</li>
|
* <li>Unlocks the item (lockedByKeyUUID = null)</li>
|
||||||
* <li>Removes the lockable flag (no more padlock slot)</li>
|
* <li>Removes the lockable flag (no more padlock slot)</li>
|
||||||
* <li>Clears any jam state</li>
|
* <li>Clears any jam state</li>
|
||||||
* <li>Clears stored lock resistance</li>
|
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param stack The ItemStack to break the lock on
|
* @param stack The ItemStack to break the lock on
|
||||||
@@ -344,6 +272,5 @@ public interface ILockable {
|
|||||||
setLockedByKeyUUID(stack, null);
|
setLockedByKeyUUID(stack, null);
|
||||||
setLockable(stack, false);
|
setLockable(stack, false);
|
||||||
setJammed(stack, false);
|
setJammed(stack, false);
|
||||||
clearLockResistance(stack);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -394,7 +394,6 @@ public class GenericClothes extends Item implements ILockable, IV2BondageItem {
|
|||||||
stack.getOrCreateTag().putBoolean(NBT_LOCKED, state);
|
stack.getOrCreateTag().putBoolean(NBT_LOCKED, state);
|
||||||
if (!state) {
|
if (!state) {
|
||||||
// When unlocking, clear lock-related data
|
// When unlocking, clear lock-related data
|
||||||
clearLockResistance(stack);
|
|
||||||
setJammed(stack, false);
|
setJammed(stack, false);
|
||||||
}
|
}
|
||||||
return stack;
|
return stack;
|
||||||
@@ -439,7 +438,6 @@ public class GenericClothes extends Item implements ILockable, IV2BondageItem {
|
|||||||
if (keyUUID != null) {
|
if (keyUUID != null) {
|
||||||
tag.putUUID(NBT_LOCKED_BY_KEY_UUID, keyUUID);
|
tag.putUUID(NBT_LOCKED_BY_KEY_UUID, keyUUID);
|
||||||
setLocked(stack, true);
|
setLocked(stack, true);
|
||||||
initializeLockResistance(stack);
|
|
||||||
} else {
|
} else {
|
||||||
tag.remove(NBT_LOCKED_BY_KEY_UUID);
|
tag.remove(NBT_LOCKED_BY_KEY_UUID);
|
||||||
setLocked(stack, false);
|
setLocked(stack, false);
|
||||||
|
|||||||
@@ -622,18 +622,14 @@ public class StruggleSessionManager {
|
|||||||
);
|
);
|
||||||
if (accessoryStack.isEmpty()) return;
|
if (accessoryStack.isEmpty()) return;
|
||||||
|
|
||||||
if (
|
// Update accessory struggle resistance in dedicated NBT key
|
||||||
accessoryStack.getItem() instanceof
|
com.tiedup.remake.util.ItemNBTHelper.setInt(
|
||||||
com.tiedup.remake.items.base.ILockable lockable
|
accessoryStack,
|
||||||
) {
|
"accessoryStruggleResistance",
|
||||||
// Update the lock resistance to match session state
|
session.getCurrentResistance()
|
||||||
lockable.setCurrentLockResistance(
|
);
|
||||||
accessoryStack,
|
// Sync V2 equipment state
|
||||||
session.getCurrentResistance()
|
V2EquipmentHelper.sync(player);
|
||||||
);
|
|
||||||
// Sync V2 equipment state
|
|
||||||
V2EquipmentHelper.sync(player);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,47 +172,17 @@ public class PacketLockpickAttempt {
|
|||||||
!targetStack.isEmpty() &&
|
!targetStack.isEmpty() &&
|
||||||
targetStack.getItem() instanceof ILockable lockable
|
targetStack.getItem() instanceof ILockable lockable
|
||||||
) {
|
) {
|
||||||
// Get lock resistance BEFORE clearing it
|
|
||||||
int lockResistance = lockable.getCurrentLockResistance(targetStack);
|
|
||||||
|
|
||||||
// Unlock the item
|
// Unlock the item
|
||||||
lockable.setLockedByKeyUUID(targetStack, null);
|
lockable.setLockedByKeyUUID(targetStack, null);
|
||||||
lockable.clearLockResistance(targetStack);
|
|
||||||
|
|
||||||
TiedUpMod.LOGGER.info(
|
TiedUpMod.LOGGER.info(
|
||||||
"[PacketLockpickAttempt] Player {} successfully picked lock on {} ({}, resistance was {})",
|
"[PacketLockpickAttempt] Player {} successfully picked lock on {} ({})",
|
||||||
player.getName().getString(),
|
player.getName().getString(),
|
||||||
targetStack.getDisplayName().getString(),
|
targetStack.getDisplayName().getString(),
|
||||||
targetRegion,
|
targetRegion
|
||||||
lockResistance
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Deduct lock resistance from bind resistance
|
// Lock is now unlocked -- bind resistance is tracked separately via IHasResistance
|
||||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
|
||||||
if (state != null && state.isTiedUp() && lockResistance > 0) {
|
|
||||||
int currentBindResistance = state.getCurrentBindResistance();
|
|
||||||
int newBindResistance = Math.max(
|
|
||||||
0,
|
|
||||||
currentBindResistance - lockResistance
|
|
||||||
);
|
|
||||||
state.setCurrentBindResistance(newBindResistance);
|
|
||||||
|
|
||||||
TiedUpMod.LOGGER.info(
|
|
||||||
"[PacketLockpickAttempt] Deducted {} from bind resistance: {} -> {}",
|
|
||||||
lockResistance,
|
|
||||||
currentBindResistance,
|
|
||||||
newBindResistance
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if player escaped (resistance = 0)
|
|
||||||
if (newBindResistance <= 0) {
|
|
||||||
state.getStruggleBinds().successActionExternal(state);
|
|
||||||
TiedUpMod.LOGGER.info(
|
|
||||||
"[PacketLockpickAttempt] Player {} escaped via lockpick!",
|
|
||||||
player.getName().getString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Damage lockpick
|
// Damage lockpick
|
||||||
|
|||||||
@@ -432,26 +432,6 @@ public class PacketSlaveItemManage {
|
|||||||
// Lock with keyUUID
|
// Lock with keyUUID
|
||||||
lockable.setLockedByKeyUUID(itemStack, keyUUID);
|
lockable.setLockedByKeyUUID(itemStack, keyUUID);
|
||||||
|
|
||||||
if (
|
|
||||||
region == BodyRegionV2.ARMS &&
|
|
||||||
itemStack.getItem() instanceof com.tiedup.remake.items.base.IHasResistance resistanceItem
|
|
||||||
) {
|
|
||||||
int currentResistance = resistanceItem.getCurrentResistance(
|
|
||||||
itemStack,
|
|
||||||
target
|
|
||||||
);
|
|
||||||
int lockResistance = lockable.getLockResistance(); // Configurable via ModConfig
|
|
||||||
resistanceItem.setCurrentResistance(
|
|
||||||
itemStack,
|
|
||||||
currentResistance + lockResistance
|
|
||||||
);
|
|
||||||
TiedUpMod.LOGGER.info(
|
|
||||||
"[PacketSlaveItemManage] Added {} lock resistance to bind (total: {})",
|
|
||||||
lockResistance,
|
|
||||||
currentResistance + lockResistance
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TiedUpMod.LOGGER.info(
|
TiedUpMod.LOGGER.info(
|
||||||
"[PacketSlaveItemManage] {} locked {}'s {} with key {}",
|
"[PacketSlaveItemManage] {} locked {}'s {} with key {}",
|
||||||
sender.getName().getString(),
|
sender.getName().getString(),
|
||||||
@@ -528,7 +508,6 @@ public class PacketSlaveItemManage {
|
|||||||
|
|
||||||
// Unlock the item - just unlock, keep padlock attached (can be re-locked)
|
// Unlock the item - just unlock, keep padlock attached (can be re-locked)
|
||||||
lockable.setLockedByKeyUUID(itemStack, null);
|
lockable.setLockedByKeyUUID(itemStack, null);
|
||||||
lockable.clearLockResistance(itemStack); // Clear any struggle progress
|
|
||||||
// Note: Padlock is only dropped on REMOVE, not on UNLOCK
|
// Note: Padlock is only dropped on REMOVE, not on UNLOCK
|
||||||
|
|
||||||
TiedUpMod.LOGGER.info(
|
TiedUpMod.LOGGER.info(
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package com.tiedup.remake.state.struggle;
|
package com.tiedup.remake.state.struggle;
|
||||||
|
|
||||||
|
import com.tiedup.remake.core.SettingsAccessor;
|
||||||
import com.tiedup.remake.core.SystemMessageManager;
|
import com.tiedup.remake.core.SystemMessageManager;
|
||||||
import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
|
import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
|
||||||
import com.tiedup.remake.core.TiedUpMod;
|
import com.tiedup.remake.core.TiedUpMod;
|
||||||
import com.tiedup.remake.items.base.ILockable;
|
import com.tiedup.remake.items.base.ILockable;
|
||||||
import com.tiedup.remake.state.PlayerBindState;
|
import com.tiedup.remake.state.PlayerBindState;
|
||||||
|
import com.tiedup.remake.util.ItemNBTHelper;
|
||||||
import com.tiedup.remake.v2.BodyRegionV2;
|
import com.tiedup.remake.v2.BodyRegionV2;
|
||||||
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
|
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
|
||||||
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
@@ -32,28 +35,36 @@ public class StruggleAccessory extends StruggleState {
|
|||||||
this.accessoryRegion = accessoryRegion;
|
this.accessoryRegion = accessoryRegion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** NBT key for accessory struggle progress (replaces removed ILockable lock resistance). */
|
||||||
|
private static final String NBT_ACCESSORY_STRUGGLE = "accessoryStruggleResistance";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current resistance for this accessory.
|
* Get the current resistance for this accessory.
|
||||||
* Accessories have 0 base resistance - only lock resistance exists.
|
* Accessories have 0 base resistance -- when locked, resistance comes from
|
||||||
* Resistance is stored in the item's NBT to persist between attempts.
|
* {@link SettingsAccessor#getPadlockResistance}. Stored in a dedicated NBT
|
||||||
|
* key on the accessory stack to persist between attempts.
|
||||||
*
|
*
|
||||||
* @param state The player's bind state
|
* @param state The player's bind state
|
||||||
* @return Current resistance value (0 if not locked, 250 if locked and not yet struggled)
|
* @return Current resistance value (0 if not locked)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected int getResistanceState(PlayerBindState state) {
|
protected int getResistanceState(PlayerBindState state) {
|
||||||
ItemStack stack = getAccessoryStack(state);
|
ItemStack stack = getAccessoryStack(state);
|
||||||
if (stack.isEmpty()) return 0;
|
if (stack.isEmpty()) return 0;
|
||||||
|
|
||||||
if (stack.getItem() instanceof ILockable lockable) {
|
if (stack.getItem() instanceof ILockable lockable && lockable.isLocked(stack)) {
|
||||||
return lockable.getCurrentLockResistance(stack);
|
// If no struggle progress stored yet, initialize from config
|
||||||
|
if (!ItemNBTHelper.contains(stack, NBT_ACCESSORY_STRUGGLE)) {
|
||||||
|
return SettingsAccessor.getPadlockResistance(null);
|
||||||
|
}
|
||||||
|
return ItemNBTHelper.getInt(stack, NBT_ACCESSORY_STRUGGLE);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the current resistance for this accessory struggle.
|
* Set the current resistance for this accessory struggle.
|
||||||
* Stored in the item's NBT to persist between attempts.
|
* Stored in a dedicated NBT key on the accessory stack.
|
||||||
*
|
*
|
||||||
* @param state The player's bind state
|
* @param state The player's bind state
|
||||||
* @param resistance The new resistance value
|
* @param resistance The new resistance value
|
||||||
@@ -63,9 +74,7 @@ public class StruggleAccessory extends StruggleState {
|
|||||||
ItemStack stack = getAccessoryStack(state);
|
ItemStack stack = getAccessoryStack(state);
|
||||||
if (stack.isEmpty()) return;
|
if (stack.isEmpty()) return;
|
||||||
|
|
||||||
if (stack.getItem() instanceof ILockable lockable) {
|
ItemNBTHelper.setInt(stack, NBT_ACCESSORY_STRUGGLE, resistance);
|
||||||
lockable.setCurrentLockResistance(stack, resistance);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,29 +96,15 @@ public class StruggleAccessory extends StruggleState {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only locked accessories can be struggled against
|
// Only require struggle if LOCKED or has BUILT_IN_LOCK.
|
||||||
if (!(stack.getItem() instanceof ILockable lockable)) {
|
// Unlocked accessories without built-in lock can be freely removed (no struggle needed).
|
||||||
|
boolean isLocked = stack.getItem() instanceof ILockable lockable && lockable.isLocked(stack);
|
||||||
|
boolean hasBuiltInLock = DataDrivenBondageItem.hasBuiltInLock(stack);
|
||||||
|
if (!isLocked && !hasBuiltInLock) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return lockable.isLocked(stack);
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the accessory is locked.
|
|
||||||
*
|
|
||||||
* @param state The player's bind state
|
|
||||||
* @return true if the accessory is locked
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected boolean isItemLocked(PlayerBindState state) {
|
|
||||||
ItemStack stack = getAccessoryStack(state);
|
|
||||||
if (
|
|
||||||
stack.isEmpty() || !(stack.getItem() instanceof ILockable lockable)
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return lockable.isLocked(stack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.tiedup.remake.state.struggle;
|
package com.tiedup.remake.state.struggle;
|
||||||
|
|
||||||
import com.tiedup.remake.core.SettingsAccessor;
|
|
||||||
import com.tiedup.remake.core.SystemMessageManager;
|
import com.tiedup.remake.core.SystemMessageManager;
|
||||||
import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
|
import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
|
||||||
import com.tiedup.remake.core.TiedUpMod;
|
import com.tiedup.remake.core.TiedUpMod;
|
||||||
@@ -93,31 +92,6 @@ public class StruggleBinds extends StruggleState {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the bind item is locked.
|
|
||||||
* Used by StruggleState to apply x10 resistance penalty.
|
|
||||||
*
|
|
||||||
* @param state The player's bind state
|
|
||||||
* @return true if the bind is locked
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected boolean isItemLocked(PlayerBindState state) {
|
|
||||||
Player player = state.getPlayer();
|
|
||||||
if (player == null) return false;
|
|
||||||
|
|
||||||
ItemStack bindStack = V2EquipmentHelper.getInRegion(
|
|
||||||
player,
|
|
||||||
BodyRegionV2.ARMS
|
|
||||||
);
|
|
||||||
if (bindStack.isEmpty()) return false;
|
|
||||||
|
|
||||||
// Works for both V1 (ItemBind) and V2 (DataDrivenBondageItem) via ILockable
|
|
||||||
if (bindStack.getItem() instanceof ILockable lockable) {
|
|
||||||
return lockable.isLocked(bindStack);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the player successfully escapes from their binds.
|
* Called when the player successfully escapes from their binds.
|
||||||
* Drops all bondage items and completely unties the player.
|
* Drops all bondage items and completely unties the player.
|
||||||
|
|||||||
@@ -107,13 +107,12 @@ public class StruggleCollar extends StruggleState {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if locked (works for V1 and V2 via ILockable)
|
// Only require struggle if LOCKED or has BUILT_IN_LOCK.
|
||||||
if (collar.getItem() instanceof ILockable lockable) {
|
// Unlocked collars without built-in lock can be freely removed (no struggle needed).
|
||||||
if (!lockable.isLocked(collar)) {
|
boolean isLocked = collar.getItem() instanceof ILockable lockable && lockable.isLocked(collar);
|
||||||
TiedUpMod.LOGGER.debug("[StruggleCollar] Collar is not locked");
|
boolean hasBuiltInLock = DataDrivenBondageItem.hasBuiltInLock(collar);
|
||||||
return false;
|
if (!isLocked && !hasBuiltInLock) {
|
||||||
}
|
TiedUpMod.LOGGER.debug("[StruggleCollar] Collar is not locked and has no built-in lock — no struggle needed");
|
||||||
} else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -212,16 +212,6 @@ public abstract class StruggleState {
|
|||||||
*/
|
*/
|
||||||
protected abstract boolean canStruggle(PlayerBindState state);
|
protected abstract boolean canStruggle(PlayerBindState state);
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the item being struggled against is locked.
|
|
||||||
*
|
|
||||||
* @param state The player's bind state
|
|
||||||
* @return true if the item is locked
|
|
||||||
*/
|
|
||||||
protected boolean isItemLocked(PlayerBindState state) {
|
|
||||||
return false; // Default: not locked, subclasses override
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if debug logging is enabled.
|
* Check if debug logging is enabled.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -70,6 +70,13 @@ public class V2TyingPlayerTask extends TyingPlayerTask {
|
|||||||
targetEntity.getName().getString()
|
targetEntity.getName().getString()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Validate heldStack hasn't changed during tying (e.g. player swapped items)
|
||||||
|
if (heldStack.isEmpty() || !ItemStack.isSameItemSameTags(heldStack, bind)) {
|
||||||
|
TiedUpMod.LOGGER.warn("[V2TyingPlayerTask] heldStack changed during tying — aborting");
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Equip via V2 system
|
// Equip via V2 system
|
||||||
V2EquipResult result = V2EquipmentHelper.equipItem(targetEntity, bind);
|
V2EquipResult result = V2EquipmentHelper.equipItem(targetEntity, bind);
|
||||||
if (result.isSuccess()) {
|
if (result.isSuccess()) {
|
||||||
|
|||||||
@@ -356,6 +356,16 @@ public class DataDrivenBondageItem extends AbstractV2BondageItem {
|
|||||||
holder.onEquipped(stack, entity);
|
holder.onEquipped(stack, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize currentResistance in NBT from ResistanceComponent at equip time.
|
||||||
|
// This avoids the MAX-scan fallback in getBaseResistance(entity) which returns
|
||||||
|
// the highest resistance across ALL equipped data-driven items.
|
||||||
|
ResistanceComponent resistComp = getComponent(stack, ComponentType.RESISTANCE, ResistanceComponent.class);
|
||||||
|
if (resistComp != null) {
|
||||||
|
com.tiedup.remake.items.base.IHasResistance resistanceItem =
|
||||||
|
(com.tiedup.remake.items.base.IHasResistance) stack.getItem();
|
||||||
|
resistanceItem.setCurrentResistance(stack, resistComp.getBaseResistance());
|
||||||
|
}
|
||||||
|
|
||||||
// NPC speed reduction (players use MovementStyleManager, not this legacy path)
|
// NPC speed reduction (players use MovementStyleManager, not this legacy path)
|
||||||
if (!(entity instanceof Player)) {
|
if (!(entity instanceof Player)) {
|
||||||
Set<BodyRegionV2> regions = getOccupiedRegions(stack);
|
Set<BodyRegionV2> regions = getOccupiedRegions(stack);
|
||||||
|
|||||||
@@ -139,7 +139,9 @@ public final class DataDrivenItemRegistry {
|
|||||||
* Clear all definitions. Called on world unload or for testing.
|
* Clear all definitions. Called on world unload or for testing.
|
||||||
*/
|
*/
|
||||||
public static void clear() {
|
public static void clear() {
|
||||||
SNAPSHOT = RegistrySnapshot.EMPTY;
|
synchronized (RELOAD_LOCK) {
|
||||||
|
SNAPSHOT = RegistrySnapshot.EMPTY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== COMPONENT HOLDERS =====
|
// ===== COMPONENT HOLDERS =====
|
||||||
|
|||||||
@@ -129,7 +129,6 @@ public class PacketV2LockToggle {
|
|||||||
|
|
||||||
UUID keyUUID = heldKey.getKeyUUID(heldKeyStack);
|
UUID keyUUID = heldKey.getKeyUUID(heldKeyStack);
|
||||||
lockable.setLockedByKeyUUID(stack, keyUUID);
|
lockable.setLockedByKeyUUID(stack, keyUUID);
|
||||||
lockable.initializeLockResistance(stack);
|
|
||||||
|
|
||||||
TiedUpMod.LOGGER.debug(
|
TiedUpMod.LOGGER.debug(
|
||||||
"[V2LockToggle] Locked region {} on entity {}",
|
"[V2LockToggle] Locked region {} on entity {}",
|
||||||
@@ -142,12 +141,10 @@ public class PacketV2LockToggle {
|
|||||||
|
|
||||||
if (hasMasterKey) {
|
if (hasMasterKey) {
|
||||||
lockable.setLockedByKeyUUID(stack, null);
|
lockable.setLockedByKeyUUID(stack, null);
|
||||||
lockable.clearLockResistance(stack);
|
|
||||||
} else if (heldKey != null) {
|
} else if (heldKey != null) {
|
||||||
UUID keyUUID = heldKey.getKeyUUID(heldKeyStack);
|
UUID keyUUID = heldKey.getKeyUUID(heldKeyStack);
|
||||||
if (!lockable.matchesKey(stack, keyUUID)) return;
|
if (!lockable.matchesKey(stack, keyUUID)) return;
|
||||||
lockable.setLockedByKeyUUID(stack, null);
|
lockable.setLockedByKeyUUID(stack, null);
|
||||||
lockable.clearLockResistance(stack);
|
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public class PacketV2StruggleStart {
|
|||||||
if (stack.getItem() instanceof ILockable lockable) {
|
if (stack.getItem() instanceof ILockable lockable) {
|
||||||
isLocked = lockable.isLocked(stack);
|
isLocked = lockable.isLocked(stack);
|
||||||
if (isLocked) {
|
if (isLocked) {
|
||||||
resistance += lockable.getCurrentLockResistance(stack);
|
resistance += com.tiedup.remake.core.SettingsAccessor.getPadlockResistance(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user