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:
@@ -1,12 +1,15 @@
|
||||
package com.tiedup.remake.state.struggle;
|
||||
|
||||
import com.tiedup.remake.core.SettingsAccessor;
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.items.base.ILockable;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import com.tiedup.remake.util.ItemNBTHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
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.item.ItemStack;
|
||||
|
||||
@@ -32,28 +35,36 @@ public class StruggleAccessory extends StruggleState {
|
||||
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.
|
||||
* Accessories have 0 base resistance - only lock resistance exists.
|
||||
* Resistance is stored in the item's NBT to persist between attempts.
|
||||
* Accessories have 0 base resistance -- when locked, resistance comes from
|
||||
* {@link SettingsAccessor#getPadlockResistance}. Stored in a dedicated NBT
|
||||
* key on the accessory stack to persist between attempts.
|
||||
*
|
||||
* @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
|
||||
protected int getResistanceState(PlayerBindState state) {
|
||||
ItemStack stack = getAccessoryStack(state);
|
||||
if (stack.isEmpty()) return 0;
|
||||
|
||||
if (stack.getItem() instanceof ILockable lockable) {
|
||||
return lockable.getCurrentLockResistance(stack);
|
||||
if (stack.getItem() instanceof ILockable lockable && lockable.isLocked(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 resistance The new resistance value
|
||||
@@ -63,9 +74,7 @@ public class StruggleAccessory extends StruggleState {
|
||||
ItemStack stack = getAccessoryStack(state);
|
||||
if (stack.isEmpty()) return;
|
||||
|
||||
if (stack.getItem() instanceof ILockable lockable) {
|
||||
lockable.setCurrentLockResistance(stack, resistance);
|
||||
}
|
||||
ItemNBTHelper.setInt(stack, NBT_ACCESSORY_STRUGGLE, resistance);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,29 +96,15 @@ public class StruggleAccessory extends StruggleState {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only locked accessories can be struggled against
|
||||
if (!(stack.getItem() instanceof ILockable lockable)) {
|
||||
// Only require struggle if LOCKED or has BUILT_IN_LOCK.
|
||||
// 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 lockable.isLocked(stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.tiedup.remake.state.struggle;
|
||||
|
||||
import com.tiedup.remake.core.SettingsAccessor;
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
@@ -93,31 +92,6 @@ public class StruggleBinds extends StruggleState {
|
||||
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.
|
||||
* Drops all bondage items and completely unties the player.
|
||||
|
||||
@@ -107,13 +107,12 @@ public class StruggleCollar extends StruggleState {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if locked (works for V1 and V2 via ILockable)
|
||||
if (collar.getItem() instanceof ILockable lockable) {
|
||||
if (!lockable.isLocked(collar)) {
|
||||
TiedUpMod.LOGGER.debug("[StruggleCollar] Collar is not locked");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Only require struggle if LOCKED or has BUILT_IN_LOCK.
|
||||
// Unlocked collars without built-in lock can be freely removed (no struggle needed).
|
||||
boolean isLocked = collar.getItem() instanceof ILockable lockable && lockable.isLocked(collar);
|
||||
boolean hasBuiltInLock = DataDrivenBondageItem.hasBuiltInLock(collar);
|
||||
if (!isLocked && !hasBuiltInLock) {
|
||||
TiedUpMod.LOGGER.debug("[StruggleCollar] Collar is not locked and has no built-in lock — no struggle needed");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -212,16 +212,6 @@ public abstract class StruggleState {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user