Files
TiedUp-/src/main/java/com/tiedup/remake/entities/BondageServiceHandler.java

199 lines
6.3 KiB
Java

package com.tiedup.remake.entities;
import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.v2.bondage.CollarHelper;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
import net.minecraft.resources.ResourceLocation;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.util.MessageDispatcher;
import com.tiedup.remake.util.teleport.Position;
import com.tiedup.remake.util.time.Timer;
import com.tiedup.remake.v2.BodyRegionV2;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.ChatFormatting;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
/**
* Handles the bondage service system for TiedUp NPCs.
* When enabled via collar settings, players who attack the NPC
* will be captured and teleported to the configured prison.
*
* <p>Flow:
* <ul>
* <li>First hit: Warning message + 10 second timer</li>
* <li>Second hit within timer: Capture and teleport to prison</li>
* </ul>
*
* <p>This system is transient - serviceTargets are not persisted.
*/
public class BondageServiceHandler {
private static final String DEFAULT_MESSAGE =
"Hello! Would you like to be tied up in my owner's prison?";
private static final int WARNING_TIMER_SECONDS = 10;
private final AbstractTiedUpNpc npc;
private final Map<UUID, Timer> serviceTargets = new HashMap<>();
public BondageServiceHandler(AbstractTiedUpNpc npc) {
this.npc = npc;
}
/**
* Check if bondage service is enabled for this damsel.
* Requires: collar with bondage service flag ON and cell assigned.
*
* @return true if bondage service is active
*/
public boolean isEnabled() {
if (!npc.hasCollar()) return false;
ItemStack collar = npc.getEquipment(BodyRegionV2.NECK);
return (
CollarHelper.hasCellAssigned(collar) &&
CollarHelper.isBondageServiceEnabled(collar)
);
}
/**
* Get the custom bondage service message from the collar.
*
* @return Custom message or default message
*/
public String getMessage() {
if (npc.hasCollar()) {
ItemStack collar = npc.getEquipment(BodyRegionV2.NECK);
String message = CollarHelper.getServiceSentence(collar);
if (message != null && !message.isEmpty()) {
return message;
}
}
return DEFAULT_MESSAGE;
}
/**
* Handle a player attack on the damsel.
* If bondage service is enabled, intercepts the attack.
*
* @param player The attacking player
* @return true if the attack was intercepted (damage should be cancelled)
*/
public boolean handlePlayerAttack(Player player) {
if (player == null || npc.isTiedUp() || !isEnabled()) {
return false;
}
UUID playerID = player.getUUID();
Timer timer = serviceTargets.get(playerID);
if (timer != null && !timer.isExpired()) {
// SECOND HIT - Capture!
capturePlayer(player);
serviceTargets.remove(playerID);
} else {
// FIRST HIT - Warning
String message = getMessage() + " Hit me again!";
MessageDispatcher.talkTo(npc, player, message);
serviceTargets.put(
playerID,
new Timer(WARNING_TIMER_SECONDS, npc.level())
);
}
return true; // Attack intercepted
}
/**
* Capture a player and teleport them to the configured cell.
*
* @param player The player to capture
*/
private void capturePlayer(Player player) {
ItemStack collar = npc.getEquipment(BodyRegionV2.NECK);
if (!CollarHelper.isCollar(collar)) return;
java.util.UUID cellId = CollarHelper.getCellId(collar);
if (cellId == null) return;
// Get cell position from registry
if (
!(npc.level() instanceof
net.minecraft.server.level.ServerLevel serverLevel)
) return;
com.tiedup.remake.cells.CellDataV2 cell =
com.tiedup.remake.cells.CellRegistryV2.get(serverLevel).getCell(
cellId
);
if (cell == null) return;
Position cellPosition = new Position(
cell.getSpawnPoint().above(),
serverLevel.dimension()
);
// Warn masters if configured
warnOwners(player, collar);
// Get player's kidnapped state
PlayerBindState state = PlayerBindState.getInstance(player);
if (state != null) {
// Apply bondage
state.equip(
BodyRegionV2.ARMS,
DataDrivenBondageItem.createStack(ResourceLocation.fromNamespaceAndPath("tiedup", "ropes"))
);
state.equip(
BodyRegionV2.MOUTH,
DataDrivenBondageItem.createStack(ResourceLocation.fromNamespaceAndPath("tiedup", "ball_gag"))
);
// Teleport to cell
state.teleportToPosition(cellPosition);
// Tie to pole if configured on collar
if (CollarHelper.shouldTieToPole(collar)) {
state.tieToClosestPole(3);
}
}
// Announce capture
MessageDispatcher.talkTo(npc, player, "Welcome to my owner's cell!");
}
/**
* Warn collar owners about a capture via bondage service.
*
* @param capturedPlayer The player who was captured
* @param itemCollar The collar item
* @param collarStack The collar ItemStack
*/
private void warnOwners(
Player capturedPlayer,
ItemStack collarStack
) {
if (!CollarHelper.shouldWarnMasters(collarStack)) {
return;
}
String message =
npc.getNpcName() +
" captured " +
capturedPlayer.getName().getString() +
" via bondage service!";
for (UUID ownerUUID : CollarHelper.getOwners(collarStack)) {
Player owner = npc.level().getPlayerByUUID(ownerUUID);
if (owner != null) {
SystemMessageManager.sendChatToPlayer(
owner,
message,
ChatFormatting.GOLD
);
}
}
}
}