Phase 1 (state): PlayerBindState, PlayerCaptorManager, PlayerEquipment, PlayerDataRetrieval, PlayerLifecycle, PlayerShockCollar, StruggleAccessory Phase 2 (client): AnimationTickHandler, NpcAnimationTickHandler, 5 render handlers, DamselModel, 3 client mixins, SelfBondageInputHandler, SlaveManagementScreen, ActionPanel, SlaveEntryWidget, ModKeybindings Phase 3 (entities): 28 entity/AI files migrated to CollarHelper, BindModeHelper, PoseTypeHelper, createStack() Phase 4 (network): PacketSlaveAction, PacketMasterEquip, PacketAssignCellToCollar, PacketNpcCommand, PacketFurnitureForcemount Phase 5 (events): RestraintTaskTickHandler, PetPlayRestrictionHandler, PlayerEnslavementHandler, ChatEventHandler, LaborAttackPunishmentHandler Phase 6 (commands): BondageSubCommand, CollarCommand, NPCCommand, KidnapSetCommand Phase 7 (compat): MCAKidnappedAdapter, MCA mixins Phase 8 (misc): GagTalkManager, PetRequestManager, HangingCagePiece, BondageItemBlockEntity, TrappedChestBlockEntity, DispenserBehaviors, BondageItemLoaderUtility, RestraintApplicator, StruggleSessionManager, MovementStyleResolver, CampLifecycleManager Some files retain dual V1/V2 checks (instanceof V1 || V2Helper) for coexistence — V1-only branches removed in Branch D.
240 lines
8.1 KiB
Java
240 lines
8.1 KiB
Java
package com.tiedup.remake.events.captivity;
|
|
|
|
import com.tiedup.remake.core.SettingsAccessor;
|
|
import com.tiedup.remake.core.TiedUpMod;
|
|
import com.tiedup.remake.entities.LeashProxyEntity;
|
|
import com.tiedup.remake.state.IBondageState;
|
|
import com.tiedup.remake.v2.bondage.CollarHelper;
|
|
import com.tiedup.remake.state.IPlayerLeashAccess;
|
|
import com.tiedup.remake.state.PlayerBindState;
|
|
import com.tiedup.remake.util.KidnappedHelper;
|
|
import com.tiedup.remake.v2.BodyRegionV2;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.Items;
|
|
import net.minecraftforge.event.TickEvent;
|
|
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
import net.minecraftforge.fml.common.Mod;
|
|
|
|
/**
|
|
* Event handler for master-slave (enslavement) interactions.
|
|
*
|
|
* <h2>Key Mechanisms</h2>
|
|
* <ul>
|
|
* <li><b>Enslavement:</b> Right-click with Lead on tied player → enslaves them</li>
|
|
* <li><b>Freeing:</b> Right-click with empty hand on your slave → frees them</li>
|
|
* </ul>
|
|
*
|
|
* <h2>Leash System</h2>
|
|
* Uses a proxy-based leash system where a {@link LeashProxyEntity} follows the player
|
|
* and holds the leash. The player does NOT mount anything - traction is applied via push().
|
|
*
|
|
* @see PlayerBindState#free()
|
|
* @see LeashProxyEntity
|
|
* @see IPlayerLeashAccess
|
|
*/
|
|
@Mod.EventBusSubscriber(modid = TiedUpMod.MOD_ID)
|
|
public class PlayerEnslavementHandler {
|
|
|
|
/**
|
|
* Prevent enslaved players from attacking/breaking their constraints.
|
|
* Handles Left-Click (Attack) events.
|
|
*/
|
|
@SubscribeEvent
|
|
public static void onPlayerAttackEntity(
|
|
net.minecraftforge.event.entity.player.AttackEntityEvent event
|
|
) {
|
|
Player player = event.getEntity();
|
|
Entity target = event.getTarget();
|
|
|
|
// Check if player is leashed (enslaved) via proxy system
|
|
if (
|
|
player instanceof IPlayerLeashAccess access &&
|
|
access.tiedup$isLeashed()
|
|
) {
|
|
// Prevent breaking the LeashKnot or the LeashProxy itself
|
|
if (
|
|
target instanceof
|
|
net.minecraft.world.entity.decoration.LeashFenceKnotEntity ||
|
|
target instanceof LeashProxyEntity
|
|
) {
|
|
event.setCanceled(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle player interactions with other players for enslavement/freeing.
|
|
*
|
|
* Uses IBondageState for condition checks (isEnslavable)
|
|
* Note: Enslavement system (getEnslavedBy, free) remains PlayerBindState-specific
|
|
*/
|
|
@SubscribeEvent
|
|
public static void onPlayerInteractEntity(
|
|
PlayerInteractEvent.EntityInteract event
|
|
) {
|
|
Player master = event.getEntity();
|
|
Entity target = event.getTarget();
|
|
|
|
// 1. Prevention Logic: Slaves cannot interact with leash knots or proxies
|
|
if (
|
|
master instanceof IPlayerLeashAccess access &&
|
|
access.tiedup$isLeashed()
|
|
) {
|
|
if (
|
|
target instanceof
|
|
net.minecraft.world.entity.decoration.LeashFenceKnotEntity ||
|
|
target instanceof LeashProxyEntity
|
|
) {
|
|
event.setCanceled(true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Only handle player-to-player interactions for enslavement logic
|
|
if (!(target instanceof Player)) {
|
|
return;
|
|
}
|
|
Player slave = (Player) target;
|
|
|
|
// Server-side only
|
|
if (master.level().isClientSide) {
|
|
return;
|
|
}
|
|
|
|
// Only handle MAIN_HAND to avoid double processing
|
|
if (event.getHand() != net.minecraft.world.InteractionHand.MAIN_HAND) {
|
|
return;
|
|
}
|
|
|
|
// Check if enslavement is enabled
|
|
if (
|
|
!SettingsAccessor.isEnslavementEnabled(
|
|
master.level().getGameRules()
|
|
)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// Get IBondageState for condition checks
|
|
IBondageState slaveKidnappedState = KidnappedHelper.getKidnappedState(
|
|
slave
|
|
);
|
|
if (slaveKidnappedState == null) {
|
|
return;
|
|
}
|
|
|
|
// Get PlayerBindState for enslavement operations (Player-only system)
|
|
PlayerBindState masterState = PlayerBindState.getInstance(master);
|
|
PlayerBindState slaveState = PlayerBindState.getInstance(slave);
|
|
|
|
if (masterState == null || slaveState == null) {
|
|
return;
|
|
}
|
|
|
|
ItemStack heldItem = master.getItemInHand(event.getHand());
|
|
|
|
// Scenario 1: Enslavement with Lead
|
|
if (heldItem.is(Items.LEAD)) {
|
|
// Check if target is enslavable (using IBondageState)
|
|
if (!slaveKidnappedState.isEnslavable()) {
|
|
// Exception: collar owner can capture even if not tied
|
|
boolean canCapture = false;
|
|
if (slaveKidnappedState.hasCollar()) {
|
|
ItemStack collar = slaveKidnappedState.getEquipment(
|
|
BodyRegionV2.NECK
|
|
);
|
|
if (CollarHelper.isCollar(collar)) {
|
|
if (
|
|
CollarHelper
|
|
.getOwners(collar)
|
|
.contains(master.getUUID())
|
|
) {
|
|
canCapture = true;
|
|
}
|
|
}
|
|
}
|
|
if (!canCapture) {
|
|
TiedUpMod.LOGGER.debug(
|
|
"[PlayerEnslavementHandler] {} cannot be enslaved - not tied and not collar owner",
|
|
slave.getName().getString()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (slaveState.isCaptive()) {
|
|
TiedUpMod.LOGGER.debug(
|
|
"[PlayerEnslavementHandler] {} is already captured",
|
|
slave.getName().getString()
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Attempt capture
|
|
boolean success = slaveState.getCapturedBy(
|
|
masterState.getCaptorManager()
|
|
);
|
|
|
|
if (success) {
|
|
heldItem.shrink(1);
|
|
TiedUpMod.LOGGER.info(
|
|
"[PlayerEnslavementHandler] {} enslaved {} (lead consumed)",
|
|
master.getName().getString(),
|
|
slave.getName().getString()
|
|
);
|
|
event.setCanceled(true);
|
|
} else {
|
|
TiedUpMod.LOGGER.warn(
|
|
"[PlayerEnslavementHandler] Failed to enslave {} by {}",
|
|
slave.getName().getString(),
|
|
master.getName().getString()
|
|
);
|
|
}
|
|
}
|
|
// Scenario 2: Freeing with Empty Hand
|
|
else if (heldItem.isEmpty()) {
|
|
if (!slaveState.isCaptive()) return;
|
|
|
|
if (slaveState.getCaptor() != masterState.getCaptorManager()) {
|
|
TiedUpMod.LOGGER.debug(
|
|
"[PlayerEnslavementHandler] {} tried to free {} but is not the captor",
|
|
master.getName().getString(),
|
|
slave.getName().getString()
|
|
);
|
|
return;
|
|
}
|
|
|
|
slaveState.free();
|
|
TiedUpMod.LOGGER.info(
|
|
"[PlayerEnslavementHandler] {} freed {}",
|
|
master.getName().getString(),
|
|
slave.getName().getString()
|
|
);
|
|
event.setCanceled(true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Periodic check for enslaved players.
|
|
*
|
|
*/
|
|
@SubscribeEvent
|
|
public static void onPlayerTick(TickEvent.PlayerTickEvent event) {
|
|
if (event.phase != TickEvent.Phase.END) return;
|
|
Player player = event.player;
|
|
if (player.level().isClientSide) return;
|
|
if (player.tickCount % 20 != 0) return;
|
|
|
|
PlayerBindState state = PlayerBindState.getInstance(player);
|
|
if (state == null) return;
|
|
|
|
if (state.isCaptive()) {
|
|
state.checkStillCaptive();
|
|
}
|
|
state.getCaptorManager().cleanupInvalidCaptives();
|
|
}
|
|
}
|