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.
191 lines
6.3 KiB
Java
191 lines
6.3 KiB
Java
package com.tiedup.remake.client.events;
|
|
|
|
import com.tiedup.remake.items.base.ItemCollar;
|
|
import com.tiedup.remake.network.ModNetwork;
|
|
import com.tiedup.remake.network.selfbondage.PacketSelfBondage;
|
|
import com.tiedup.remake.v2.bondage.BindModeHelper;
|
|
import com.tiedup.remake.v2.bondage.CollarHelper;
|
|
import com.tiedup.remake.v2.bondage.IV2BondageItem;
|
|
import com.tiedup.remake.v2.bondage.component.BlindingComponent;
|
|
import com.tiedup.remake.v2.bondage.component.ComponentType;
|
|
import com.tiedup.remake.v2.bondage.component.GaggingComponent;
|
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemDefinition;
|
|
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemRegistry;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.player.LocalPlayer;
|
|
import net.minecraft.world.InteractionHand;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraftforge.api.distmarker.Dist;
|
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
|
import net.minecraftforge.event.TickEvent;
|
|
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
import net.minecraftforge.fml.common.Mod;
|
|
|
|
/**
|
|
* Client-side event handler for self-bondage input.
|
|
*
|
|
* Intercepts left-click when holding bondage items and
|
|
* sends packets continuously to the server to perform self-bondage.
|
|
*
|
|
* Self-bondage items:
|
|
* - Binds (rope, chain, etc.) - Self-tie (requires holding left-click)
|
|
* - Gags - Self-gag (if already tied, instant)
|
|
* - Blindfolds - Self-blindfold (if already tied, instant)
|
|
* - Mittens - Self-mitten (if already tied, instant)
|
|
* - Earplugs - Self-earplug (if already tied, instant)
|
|
* - Collar - NOT ALLOWED (cannot self-collar)
|
|
*/
|
|
@Mod.EventBusSubscriber(
|
|
modid = "tiedup",
|
|
value = Dist.CLIENT,
|
|
bus = Mod.EventBusSubscriber.Bus.FORGE
|
|
)
|
|
@OnlyIn(Dist.CLIENT)
|
|
public class SelfBondageInputHandler {
|
|
|
|
/** Track if we're currently in self-bondage mode */
|
|
private static boolean isSelfBondageActive = false;
|
|
|
|
/** The hand we're using for self-bondage */
|
|
private static InteractionHand activeHand = null;
|
|
|
|
/** Tick counter for packet sending interval */
|
|
private static int tickCounter = 0;
|
|
|
|
/** Send packet every 4 ticks (5 times per second) for smooth progress */
|
|
private static final int PACKET_INTERVAL = 4;
|
|
|
|
/**
|
|
* Handle left-click in empty air - START self-bondage.
|
|
*/
|
|
@SubscribeEvent
|
|
public static void onLeftClickEmpty(
|
|
PlayerInteractEvent.LeftClickEmpty event
|
|
) {
|
|
startSelfBondage();
|
|
}
|
|
|
|
/**
|
|
* Handle left-click on block - START self-bondage (cancel block breaking).
|
|
*/
|
|
@SubscribeEvent
|
|
public static void onLeftClickBlock(
|
|
PlayerInteractEvent.LeftClickBlock event
|
|
) {
|
|
if (!event.getLevel().isClientSide()) return;
|
|
|
|
ItemStack stack = event.getItemStack();
|
|
if (isSelfBondageItem(stack)) {
|
|
event.setCanceled(true);
|
|
startSelfBondage();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Start self-bondage mode if holding a bondage item.
|
|
*/
|
|
private static void startSelfBondage() {
|
|
LocalPlayer player = Minecraft.getInstance().player;
|
|
if (player == null) return;
|
|
|
|
// Check main hand first, then off hand
|
|
InteractionHand hand = InteractionHand.MAIN_HAND;
|
|
ItemStack stack = player.getMainHandItem();
|
|
|
|
if (!isSelfBondageItem(stack)) {
|
|
stack = player.getOffhandItem();
|
|
hand = InteractionHand.OFF_HAND;
|
|
|
|
if (!isSelfBondageItem(stack)) {
|
|
return; // No bondage item in either hand
|
|
}
|
|
}
|
|
|
|
// Start self-bondage mode
|
|
isSelfBondageActive = true;
|
|
activeHand = hand;
|
|
tickCounter = 0;
|
|
|
|
// Send initial packet immediately
|
|
ModNetwork.sendToServer(new PacketSelfBondage(hand));
|
|
}
|
|
|
|
/**
|
|
* Client tick - continuously send packets while attack button is held.
|
|
*/
|
|
@SubscribeEvent
|
|
public static void onClientTick(TickEvent.ClientTickEvent event) {
|
|
if (event.phase != TickEvent.Phase.END) return;
|
|
if (!isSelfBondageActive) return;
|
|
|
|
Minecraft mc = Minecraft.getInstance();
|
|
LocalPlayer player = mc.player;
|
|
|
|
// Stop if conditions are no longer valid
|
|
if (player == null || mc.screen != null) {
|
|
stopSelfBondage();
|
|
return;
|
|
}
|
|
|
|
// Check if attack button is still held
|
|
if (!mc.options.keyAttack.isDown()) {
|
|
stopSelfBondage();
|
|
return;
|
|
}
|
|
|
|
// Check if still holding bondage item in the active hand
|
|
ItemStack stack = player.getItemInHand(activeHand);
|
|
if (!isSelfBondageItem(stack)) {
|
|
stopSelfBondage();
|
|
return;
|
|
}
|
|
|
|
// Send packet at interval for continuous progress
|
|
tickCounter++;
|
|
if (tickCounter >= PACKET_INTERVAL) {
|
|
tickCounter = 0;
|
|
ModNetwork.sendToServer(new PacketSelfBondage(activeHand));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop self-bondage mode.
|
|
*/
|
|
private static void stopSelfBondage() {
|
|
isSelfBondageActive = false;
|
|
activeHand = null;
|
|
tickCounter = 0;
|
|
}
|
|
|
|
/**
|
|
* Check if a stack supports self-bondage.
|
|
* Collar is explicitly excluded.
|
|
*/
|
|
private static boolean isSelfBondageItem(ItemStack stack) {
|
|
if (stack.isEmpty()) return false;
|
|
|
|
// Collar cannot be self-equipped (V1 collar guard + V2 ownership component)
|
|
if (stack.getItem() instanceof ItemCollar || CollarHelper.isCollar(stack)) {
|
|
return false;
|
|
}
|
|
|
|
// V2 bondage items support self-bondage (left-click hold with tying duration)
|
|
if (stack.getItem() instanceof IV2BondageItem) {
|
|
return true;
|
|
}
|
|
|
|
// V2 data-driven items: check if it occupies any bondage region
|
|
DataDrivenItemDefinition def = DataDrivenItemRegistry.get(stack);
|
|
if (def != null) {
|
|
return true;
|
|
}
|
|
|
|
// V1 fallback: bind items
|
|
return BindModeHelper.isBindItem(stack)
|
|
|| DataDrivenBondageItem.getComponent(stack, ComponentType.GAGGING, GaggingComponent.class) != null
|
|
|| DataDrivenBondageItem.getComponent(stack, ComponentType.BLINDING, BlindingComponent.class) != null;
|
|
}
|
|
}
|