feat(D-01/A): self-bondage region routing (A11)

- handleV2SelfBondage: split into region-based routing
  - NECK → blocked (cannot self-collar)
  - ARMS → handleV2SelfBind (tying task with progress bar)
  - Other → handleV2SelfAccessory (instant equip)
- handleV2SelfAccessory: arms-bound check via BindModeHelper,
  locked check for swap, V2EquipmentHelper for conflict resolution
This commit is contained in:
Adrien
2026-04-14 16:06:01 +02:00
parent c240f48b29
commit 21c1fc3b57

View File

@@ -14,8 +14,10 @@ import com.tiedup.remake.tasks.TyingTask;
import com.tiedup.remake.tasks.V2TyingPlayerTask; import com.tiedup.remake.tasks.V2TyingPlayerTask;
import com.tiedup.remake.util.KidnappedHelper; import com.tiedup.remake.util.KidnappedHelper;
import com.tiedup.remake.v2.BodyRegionV2; import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.BindModeHelper;
import com.tiedup.remake.v2.bondage.IV2BondageItem; import com.tiedup.remake.v2.bondage.IV2BondageItem;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper; import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import com.tiedup.remake.v2.bondage.V2EquipResult;
import java.util.function.Supplier; import java.util.function.Supplier;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
@@ -222,71 +224,112 @@ public class PacketSelfBondage {
IV2BondageItem v2Item, IV2BondageItem v2Item,
IBondageState state IBondageState state
) { ) {
java.util.Set<BodyRegionV2> regions = v2Item.getOccupiedRegions(stack);
// Cannot self-collar
if (regions.contains(BodyRegionV2.NECK)) {
TiedUpMod.LOGGER.debug("[SelfBondage] {} tried to self-collar — blocked", player.getName().getString());
return;
}
// ARMS: tying task with progress bar
if (regions.contains(BodyRegionV2.ARMS)) {
handleV2SelfBind(player, stack, v2Item, state);
return;
}
// Accessories (MOUTH, EYES, EARS, HANDS): instant equip
handleV2SelfAccessory(player, stack, v2Item, state);
}
private static void handleV2SelfBind(
ServerPlayer player,
ItemStack stack,
IV2BondageItem v2Item,
IBondageState state
) {
// Can't self-tie if already tied
if (state.isTiedUp()) {
TiedUpMod.LOGGER.debug("[SelfBondage] {} tried V2 self-tie but already tied", player.getName().getString());
return;
}
// Check if all target regions are already occupied or blocked // Check if all target regions are already occupied or blocked
boolean allBlocked = true; boolean allBlocked = true;
for (BodyRegionV2 region : v2Item.getOccupiedRegions(stack)) { for (BodyRegionV2 region : v2Item.getOccupiedRegions(stack)) {
if ( if (!V2EquipmentHelper.isRegionOccupied(player, region)
!V2EquipmentHelper.isRegionOccupied(player, region) && && !V2EquipmentHelper.isRegionBlocked(player, region)) {
!V2EquipmentHelper.isRegionBlocked(player, region)
) {
allBlocked = false; allBlocked = false;
break; break;
} }
} }
if (allBlocked) { if (allBlocked) {
TiedUpMod.LOGGER.debug( TiedUpMod.LOGGER.debug("[SelfBondage] {} tried V2 self-equip but all regions occupied", player.getName().getString());
"[SelfBondage] {} tried V2 self-equip but all regions occupied",
player.getName().getString()
);
return; return;
} }
PlayerBindState playerState = PlayerBindState.getInstance(player); PlayerBindState playerState = PlayerBindState.getInstance(player);
if (playerState == null) return; if (playerState == null) return;
int tyingSeconds = SettingsAccessor.getTyingPlayerTime( int tyingSeconds = SettingsAccessor.getTyingPlayerTime(player.level().getGameRules());
player.level().getGameRules()
);
// Create V2 tying task (uses V2EquipmentHelper on completion, NOT putBindOn)
V2TyingPlayerTask newTask = new V2TyingPlayerTask( V2TyingPlayerTask newTask = new V2TyingPlayerTask(
stack.copy(), // copy for display/matching stack.copy(), stack, state, player, tyingSeconds, player.level(), player
stack, // live reference for consumption
state,
player, // target is self
tyingSeconds,
player.level(),
player // kidnapper is also self
); );
TyingTask currentTask = playerState.getCurrentTyingTask(); TyingTask currentTask = playerState.getCurrentTyingTask();
if (currentTask == null
if ( || !currentTask.isSameTarget(player)
currentTask == null || || currentTask.isOutdated()
!currentTask.isSameTarget(player) || || !ItemStack.matches(currentTask.getBind(), stack)) {
currentTask.isOutdated() ||
!ItemStack.matches(currentTask.getBind(), stack)
) {
// Start new task
playerState.setCurrentTyingTask(newTask); playerState.setCurrentTyingTask(newTask);
newTask.start(); newTask.start();
TiedUpMod.LOGGER.debug(
"[SelfBondage] {} started V2 self-tying ({} seconds)",
player.getName().getString(),
tyingSeconds
);
} else { } else {
// Continue existing task — just mark active
currentTask.update(); currentTask.update();
} }
// If we started a new task, mark it active too
if (playerState.getCurrentTyingTask() == newTask) { if (playerState.getCurrentTyingTask() == newTask) {
newTask.update(); newTask.update();
} }
} }
private static void handleV2SelfAccessory(
ServerPlayer player,
ItemStack stack,
IV2BondageItem v2Item,
IBondageState state
) {
// Can't equip accessories if arms are fully bound
ItemStack currentBind = V2EquipmentHelper.getInRegion(player, BodyRegionV2.ARMS);
if (!currentBind.isEmpty() && BindModeHelper.hasArmsBound(currentBind)) {
TiedUpMod.LOGGER.debug("[SelfBondage] {} can't self-accessory — arms bound", player.getName().getString());
return;
}
// Check if region is occupied — try to swap
for (BodyRegionV2 region : v2Item.getOccupiedRegions(stack)) {
if (V2EquipmentHelper.isRegionOccupied(player, region)) {
ItemStack existing = V2EquipmentHelper.getInRegion(player, region);
// Can't swap if locked
if (existing.getItem() instanceof ILockable lockable && lockable.isLocked(existing)) {
TiedUpMod.LOGGER.debug("[SelfBondage] {} can't swap — current is locked", player.getName().getString());
return;
}
}
}
// Equip via V2EquipmentHelper (handles conflict resolution, displaced items)
V2EquipResult result = V2EquipmentHelper.equipItem(player, stack.copy());
if (result.isSuccess()) {
for (ItemStack displaced : result.displaced()) {
player.spawnAtLocation(displaced);
}
stack.shrink(1);
SyncManager.syncInventory(player);
TiedUpMod.LOGGER.info("[SelfBondage] {} self-equipped V2 accessory", player.getName().getString());
}
}
/** /**
* Handle self-equipping an accessory (gag, blindfold, mittens, earplugs). * Handle self-equipping an accessory (gag, blindfold, mittens, earplugs).
* Can be used anytime (no need to be tied). * Can be used anytime (no need to be tied).