From f945e9449baf0665906393f189af787c84e19a47 Mon Sep 17 00:00:00 2001 From: NotEvil Date: Wed, 15 Apr 2026 10:48:21 +0200 Subject: [PATCH 1/3] =?UTF-8?q?feat(D-01/E):=20quickwins=20=E2=80=94=20deb?= =?UTF-8?q?ug=20toggle,=20HumanChairHelper=20move,=20collar=20equip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Q1: Remove F9 debug toggle from GltfAnimationApplier + delete dead GltfRenderLayer + remove keybind registration Q2: Move HumanChairHelper from state/ to util/ — pure utility with no state dependency. 7 import updates. Q3: Wire NECK collar equip flow in DataDrivenBondageItem: - Target must be tied up (V1 rule preserved) - Distance + line-of-sight validation - Owner added to NBT before equip via CollarHelper.addOwner() - V2EquipmentHelper handles conflict resolution - ModSounds.COLLAR_PUT played on success - OwnershipComponent.onEquipped registers in CollarRegistry --- .../render/DogPoseRenderHandler.java | 2 +- .../animation/render/PetBedRenderHandler.java | 2 +- .../animation/tick/AnimationTickHandler.java | 2 +- .../client/gltf/GltfAnimationApplier.java | 48 -------- .../remake/client/gltf/GltfClientSetup.java | 48 +------- .../remake/client/gltf/GltfRenderLayer.java | 106 ------------------ .../tiedup/remake/entities/EntityMaster.java | 2 +- .../ai/master/MasterHumanChairGoal.java | 2 +- .../mixin/MixinLivingEntityBodyRot.java | 2 +- .../remake/mixin/client/MixinCamera.java | 2 +- .../{state => util}/HumanChairHelper.java | 2 +- .../bondage/client/V2BondageRenderLayer.java | 4 +- .../datadriven/DataDrivenBondageItem.java | 43 ++++++- 13 files changed, 50 insertions(+), 215 deletions(-) delete mode 100644 src/main/java/com/tiedup/remake/client/gltf/GltfRenderLayer.java rename src/main/java/com/tiedup/remake/{state => util}/HumanChairHelper.java (98%) diff --git a/src/main/java/com/tiedup/remake/client/animation/render/DogPoseRenderHandler.java b/src/main/java/com/tiedup/remake/client/animation/render/DogPoseRenderHandler.java index f61f877..0c77dd7 100644 --- a/src/main/java/com/tiedup/remake/client/animation/render/DogPoseRenderHandler.java +++ b/src/main/java/com/tiedup/remake/client/animation/render/DogPoseRenderHandler.java @@ -3,7 +3,7 @@ package com.tiedup.remake.client.animation.render; import com.tiedup.remake.core.TiedUpMod; import com.tiedup.remake.items.base.PoseType; import com.tiedup.remake.v2.bondage.PoseTypeHelper; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; diff --git a/src/main/java/com/tiedup/remake/client/animation/render/PetBedRenderHandler.java b/src/main/java/com/tiedup/remake/client/animation/render/PetBedRenderHandler.java index 53e0bbd..289fb19 100644 --- a/src/main/java/com/tiedup/remake/client/animation/render/PetBedRenderHandler.java +++ b/src/main/java/com/tiedup/remake/client/animation/render/PetBedRenderHandler.java @@ -4,7 +4,7 @@ import com.tiedup.remake.client.state.PetBedClientState; import com.tiedup.remake.core.TiedUpMod; import com.tiedup.remake.items.base.PoseType; import com.tiedup.remake.v2.bondage.PoseTypeHelper; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import net.minecraft.client.player.AbstractClientPlayer; diff --git a/src/main/java/com/tiedup/remake/client/animation/tick/AnimationTickHandler.java b/src/main/java/com/tiedup/remake/client/animation/tick/AnimationTickHandler.java index 4b31a49..f9b4ebd 100644 --- a/src/main/java/com/tiedup/remake/client/animation/tick/AnimationTickHandler.java +++ b/src/main/java/com/tiedup/remake/client/animation/tick/AnimationTickHandler.java @@ -17,7 +17,7 @@ import com.tiedup.remake.client.state.PetBedClientState; import com.tiedup.remake.items.base.PoseType; import com.tiedup.remake.v2.bondage.BindModeHelper; import com.tiedup.remake.v2.bondage.PoseTypeHelper; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import com.tiedup.remake.v2.bondage.IV2BondageEquipment; diff --git a/src/main/java/com/tiedup/remake/client/gltf/GltfAnimationApplier.java b/src/main/java/com/tiedup/remake/client/gltf/GltfAnimationApplier.java index 5d2f4f8..e83d879 100644 --- a/src/main/java/com/tiedup/remake/client/gltf/GltfAnimationApplier.java +++ b/src/main/java/com/tiedup/remake/client/gltf/GltfAnimationApplier.java @@ -13,8 +13,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; -import net.minecraft.client.Minecraft; -import net.minecraft.client.player.AbstractClientPlayer; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.LivingEntity; import net.minecraftforge.api.distmarker.Dist; @@ -433,52 +431,6 @@ public final class GltfAnimationApplier { ContextAnimationFactory.clearCache(); } - // LEGACY F9 DEBUG TOGGLE - - private static boolean debugEnabled = false; - - /** - * Toggle debug mode via F9 key. - * When enabled, applies handcuffs V2 animation (rightArm + leftArm) to the local player - * using STAND_IDLE context. When disabled, clears all V2 animation. - */ - public static void toggle() { - debugEnabled = !debugEnabled; - LOGGER.info( - "[GltfPipeline] Debug toggle: {}", - debugEnabled ? "ON" : "OFF" - ); - - AbstractClientPlayer player = Minecraft.getInstance().player; - if (player == null) return; - - if (debugEnabled) { - ResourceLocation modelLoc = ResourceLocation.fromNamespaceAndPath( - "tiedup", - "models/gltf/v2/handcuffs/cuffs_prototype.glb" - ); - Set armParts = Set.of("rightArm", "leftArm"); - RegionBoneMapper.BoneOwnership debugOwnership = - new RegionBoneMapper.BoneOwnership(armParts, Set.of()); - applyV2Animation( - player, - modelLoc, - null, - AnimationContext.STAND_IDLE, - debugOwnership - ); - } else { - clearV2Animation(player); - } - } - - /** - * Whether F9 debug mode is currently enabled. - */ - public static boolean isEnabled() { - return debugEnabled; - } - // INTERNAL /** diff --git a/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java b/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java index 4527318..7c2b204 100644 --- a/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java +++ b/src/main/java/com/tiedup/remake/client/gltf/GltfClientSetup.java @@ -1,11 +1,9 @@ package com.tiedup.remake.client.gltf; -import com.mojang.blaze3d.platform.InputConstants; import com.tiedup.remake.client.animation.context.ContextAnimationFactory; import com.tiedup.remake.client.animation.context.ContextGlbRegistry; import com.tiedup.remake.v2.bondage.client.V2BondageRenderLayer; import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemReloadListener; -import net.minecraft.client.KeyMapping; import net.minecraft.client.renderer.entity.player.PlayerRenderer; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.server.packs.resources.SimplePreparableReloadListener; @@ -13,8 +11,6 @@ import net.minecraft.util.profiling.ProfilerFiller; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.EntityRenderersEvent; import net.minecraftforge.client.event.RegisterClientReloadListenersEvent; -import net.minecraftforge.client.event.RegisterKeyMappingsEvent; -import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; @@ -23,24 +19,16 @@ import org.apache.logging.log4j.Logger; /** * Forge event registration for the glTF pipeline. - * Registers keybind (F9), render layers, and animation factory. + * Registers render layers and animation factory. */ public final class GltfClientSetup { private static final Logger LOGGER = LogManager.getLogger("GltfPipeline"); - private static final String KEY_CATEGORY = "key.categories.tiedup"; - static final KeyMapping TOGGLE_KEY = new KeyMapping( - "key.tiedup.gltf_toggle", - InputConstants.Type.KEYSYM, - InputConstants.KEY_F9, - KEY_CATEGORY - ); - private GltfClientSetup() {} /** - * MOD bus event subscribers (FMLClientSetupEvent, RegisterKeyMappings, AddLayers). + * MOD bus event subscribers (FMLClientSetupEvent, AddLayers). */ @Mod.EventBusSubscriber( modid = "tiedup", @@ -58,21 +46,11 @@ public final class GltfClientSetup { }); } - @SubscribeEvent - public static void onRegisterKeybindings( - RegisterKeyMappingsEvent event - ) { - event.register(TOGGLE_KEY); - LOGGER.info("[GltfPipeline] Keybind registered: F9"); - } - @SuppressWarnings("unchecked") @SubscribeEvent public static void onAddLayers(EntityRenderersEvent.AddLayers event) { - // Add GltfRenderLayer (prototype/debug with F9 toggle) to player renderers var defaultRenderer = event.getSkin("default"); if (defaultRenderer instanceof PlayerRenderer playerRenderer) { - playerRenderer.addLayer(new GltfRenderLayer(playerRenderer)); playerRenderer.addLayer( new V2BondageRenderLayer<>(playerRenderer) ); @@ -81,10 +59,9 @@ public final class GltfClientSetup { ); } - // Add both layers to slim player renderer (Alex) + // Add V2 layer to slim player renderer (Alex) var slimRenderer = event.getSkin("slim"); if (slimRenderer instanceof PlayerRenderer playerRenderer) { - playerRenderer.addLayer(new GltfRenderLayer(playerRenderer)); playerRenderer.addLayer( new V2BondageRenderLayer<>(playerRenderer) ); @@ -143,23 +120,4 @@ public final class GltfClientSetup { } } - /** - * FORGE bus event subscribers (ClientTickEvent for keybind toggle). - */ - @Mod.EventBusSubscriber( - modid = "tiedup", - bus = Mod.EventBusSubscriber.Bus.FORGE, - value = Dist.CLIENT - ) - public static class ForgeBusEvents { - - @SubscribeEvent - public static void onClientTick(TickEvent.ClientTickEvent event) { - if (event.phase != TickEvent.Phase.END) return; - - while (TOGGLE_KEY.consumeClick()) { - GltfAnimationApplier.toggle(); - } - } - } } diff --git a/src/main/java/com/tiedup/remake/client/gltf/GltfRenderLayer.java b/src/main/java/com/tiedup/remake/client/gltf/GltfRenderLayer.java deleted file mode 100644 index fe54166..0000000 --- a/src/main/java/com/tiedup/remake/client/gltf/GltfRenderLayer.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.tiedup.remake.client.gltf; - -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.Minecraft; -import net.minecraft.client.model.PlayerModel; -import net.minecraft.client.player.AbstractClientPlayer; -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.entity.RenderLayerParent; -import net.minecraft.client.renderer.entity.layers.RenderLayer; -import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.joml.Matrix4f; - -/** - * RenderLayer that renders the glTF mesh (handcuffs) on the player. - * Only active when enabled and only renders on the local player. - *

- * Uses the live skinning path: reads live skeleton from HumanoidModel - * via {@link GltfLiveBoneReader}, following PlayerAnimator + bendy-lib rotations. - * Falls back to GLB-internal skinning via {@link GltfSkinningEngine} if live reading fails. - */ -@OnlyIn(Dist.CLIENT) -public class GltfRenderLayer - extends RenderLayer> -{ - - private static final Logger LOGGER = LogManager.getLogger("GltfPipeline"); - - private static final ResourceLocation CUFFS_MODEL = - ResourceLocation.fromNamespaceAndPath( - "tiedup", - "models/gltf/v2/handcuffs/cuffs_prototype.glb" - ); - - public GltfRenderLayer( - RenderLayerParent< - AbstractClientPlayer, - PlayerModel - > renderer - ) { - super(renderer); - } - - /** - * The Y translate offset to place the glTF mesh in the MC PoseStack. - *

- * After LivingEntityRenderer's scale(-1,-1,1) + translate(0,-1.501,0), - * the PoseStack origin is at the model top (1.501 blocks above feet), Y-down. - * The glTF mesh (MC-converted) has feet at Y=0 and head at Y≈-1.5. - * Translating by 1.501 maps glTF feet to PoseStack feet and head to top. - */ - private static final float ALIGNMENT_Y = 1.501f; - - @Override - public void render( - PoseStack poseStack, - MultiBufferSource buffer, - int packedLight, - AbstractClientPlayer entity, - float limbSwing, - float limbSwingAmount, - float partialTick, - float ageInTicks, - float netHeadYaw, - float headPitch - ) { - if (!GltfAnimationApplier.isEnabled()) return; - if (entity != Minecraft.getInstance().player) return; - - GltfData data = GltfCache.get(CUFFS_MODEL); - if (data == null) return; - - // Live path: read skeleton from HumanoidModel (after PlayerAnimator) - PlayerModel parentModel = this.getParentModel(); - Matrix4f[] joints = GltfLiveBoneReader.computeJointMatricesFromModel( - parentModel, - data, - entity - ); - if (joints == null) { - // Fallback to GLB-internal path if live reading fails - joints = GltfSkinningEngine.computeJointMatrices(data); - } - - poseStack.pushPose(); - - // Align glTF mesh with MC model (feet-to-feet alignment) - poseStack.translate(0, ALIGNMENT_Y, 0); - - GltfMeshRenderer.renderSkinned( - data, - joints, - poseStack, - buffer, - packedLight, - net.minecraft.client.renderer.entity.LivingEntityRenderer.getOverlayCoords( - entity, - 0.0f - ) - ); - poseStack.popPose(); - } -} diff --git a/src/main/java/com/tiedup/remake/entities/EntityMaster.java b/src/main/java/com/tiedup/remake/entities/EntityMaster.java index 0c52da9..aaa9be8 100644 --- a/src/main/java/com/tiedup/remake/entities/EntityMaster.java +++ b/src/main/java/com/tiedup/remake/entities/EntityMaster.java @@ -1072,7 +1072,7 @@ public class EntityMaster extends EntityKidnapperElite { if ( tag != null && tag.getBoolean( - com.tiedup.remake.state.HumanChairHelper.NBT_KEY + com.tiedup.remake.util.HumanChairHelper.NBT_KEY ) ) { bindState.unequip(BodyRegionV2.ARMS); diff --git a/src/main/java/com/tiedup/remake/entities/ai/master/MasterHumanChairGoal.java b/src/main/java/com/tiedup/remake/entities/ai/master/MasterHumanChairGoal.java index 9fb0909..9d85789 100644 --- a/src/main/java/com/tiedup/remake/entities/ai/master/MasterHumanChairGoal.java +++ b/src/main/java/com/tiedup/remake/entities/ai/master/MasterHumanChairGoal.java @@ -5,7 +5,7 @@ import com.tiedup.remake.dialogue.DialogueBridge; import com.tiedup.remake.entities.EntityMaster; import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem; import net.minecraft.resources.ResourceLocation; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import java.util.EnumSet; diff --git a/src/main/java/com/tiedup/remake/mixin/MixinLivingEntityBodyRot.java b/src/main/java/com/tiedup/remake/mixin/MixinLivingEntityBodyRot.java index 5eca9ff..6f08da5 100644 --- a/src/main/java/com/tiedup/remake/mixin/MixinLivingEntityBodyRot.java +++ b/src/main/java/com/tiedup/remake/mixin/MixinLivingEntityBodyRot.java @@ -1,6 +1,6 @@ package com.tiedup.remake.mixin; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import net.minecraft.world.entity.LivingEntity; diff --git a/src/main/java/com/tiedup/remake/mixin/client/MixinCamera.java b/src/main/java/com/tiedup/remake/mixin/client/MixinCamera.java index 814cf5c..6b1c715 100644 --- a/src/main/java/com/tiedup/remake/mixin/client/MixinCamera.java +++ b/src/main/java/com/tiedup/remake/mixin/client/MixinCamera.java @@ -2,7 +2,7 @@ package com.tiedup.remake.mixin.client; import com.tiedup.remake.items.base.PoseType; import com.tiedup.remake.v2.bondage.PoseTypeHelper; -import com.tiedup.remake.state.HumanChairHelper; +import com.tiedup.remake.util.HumanChairHelper; import com.tiedup.remake.state.PlayerBindState; import com.tiedup.remake.v2.BodyRegionV2; import net.minecraft.client.Camera; diff --git a/src/main/java/com/tiedup/remake/state/HumanChairHelper.java b/src/main/java/com/tiedup/remake/util/HumanChairHelper.java similarity index 98% rename from src/main/java/com/tiedup/remake/state/HumanChairHelper.java rename to src/main/java/com/tiedup/remake/util/HumanChairHelper.java index 73aa51b..f432dd6 100644 --- a/src/main/java/com/tiedup/remake/state/HumanChairHelper.java +++ b/src/main/java/com/tiedup/remake/util/HumanChairHelper.java @@ -1,4 +1,4 @@ -package com.tiedup.remake.state; +package com.tiedup.remake.util; import com.tiedup.remake.items.base.PoseType; import net.minecraft.nbt.CompoundTag; diff --git a/src/main/java/com/tiedup/remake/v2/bondage/client/V2BondageRenderLayer.java b/src/main/java/com/tiedup/remake/v2/bondage/client/V2BondageRenderLayer.java index 62f3b33..7434012 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/client/V2BondageRenderLayer.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/client/V2BondageRenderLayer.java @@ -46,9 +46,7 @@ import org.joml.Matrix4f; * pushPose/popPose pair. Joint matrices are computed per-GLB-model * because different GLB models have different skeletons. * - *

Unlike {@link com.tiedup.remake.client.gltf.GltfRenderLayer}, - * this layer is always active (no F9 toggle guard) and renders on - * ALL entities (no local-player-only guard). + *

This layer is always active and renders on ALL entities. */ @OnlyIn(Dist.CLIENT) public class V2BondageRenderLayer< diff --git a/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java b/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java index 6d3e61c..e6a90dc 100644 --- a/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java +++ b/src/main/java/com/tiedup/remake/v2/bondage/datadriven/DataDrivenBondageItem.java @@ -1,7 +1,12 @@ package com.tiedup.remake.v2.bondage.datadriven; +import com.tiedup.remake.core.ModSounds; import com.tiedup.remake.core.TiedUpMod; +import com.tiedup.remake.state.IRestrainable; +import com.tiedup.remake.util.KidnappedHelper; import com.tiedup.remake.v2.BodyRegionV2; +import com.tiedup.remake.v2.bondage.CollarHelper; +import com.tiedup.remake.v2.bondage.V2EquipResult; import com.tiedup.remake.v2.bondage.BindModeHelper; import com.tiedup.remake.v2.bondage.IV2BondageEquipment; import com.tiedup.remake.v2.bondage.TyingInteractionHelper; @@ -168,11 +173,39 @@ public class DataDrivenBondageItem extends AbstractV2BondageItem { return TyingInteractionHelper.handleTying(serverPlayer, target, stack, hand); } - // NECK: blocked — collar equip requires owner setup (add owner to NBT, - // register in CollarRegistry, play sound, sync) which is not yet wired - // through interactLivingEntity. TODO: implement collar equip flow. - if (regions.contains(BodyRegionV2.NECK)) { - TiedUpMod.LOGGER.debug("[DataDrivenBondageItem] NECK equip via right-click not yet implemented"); + // NECK: collar equip flow + if (regions.contains(BodyRegionV2.NECK) && player instanceof ServerPlayer serverPlayer) { + // Target must be tied up to collar (V1 rule preserved) + IRestrainable targetState = KidnappedHelper.getKidnappedState(target); + if (targetState == null || !targetState.isTiedUp()) { + return InteractionResult.PASS; + } + + // Distance + line-of-sight + if (player.distanceTo(target) > 4.0 || !player.hasLineOfSight(target)) { + return InteractionResult.PASS; + } + + // Add player as owner in NBT before equip + ItemStack collarCopy = stack.copy(); + CollarHelper.addOwner(collarCopy, player); + + // Equip via V2 system (handles conflict resolution, displaced items) + V2EquipResult result = V2EquipmentHelper.equipItem(target, collarCopy); + if (result.isSuccess()) { + for (ItemStack displaced : result.displaced()) { + target.spawnAtLocation(displaced); + } + stack.shrink(1); + // Play collar equip sound + target.level().playSound(null, target.getX(), target.getY(), target.getZ(), + ModSounds.COLLAR_PUT.get(), + net.minecraft.sounds.SoundSource.PLAYERS, 0.5f, 1.0f); + V2EquipmentHelper.sync(target); + TiedUpMod.LOGGER.info("[DataDrivenBondageItem] {} collared {} (owner added)", + player.getName().getString(), target.getName().getString()); + return InteractionResult.SUCCESS; + } return InteractionResult.PASS; } From c1e1f56058701e8d24b67c2b675bc7d1ada1dd40 Mon Sep 17 00:00:00 2001 From: NotEvil Date: Wed, 15 Apr 2026 11:09:48 +0200 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20split=20BondageSubCommand=20120?= =?UTF-8?q?7L=20=E2=86=92=205=20focused=20files=20(UC-01)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BindCommands.java: tie, untie (156L) - GagCommands.java: gag, ungag (140L) - BlindfoldCommands.java: blindfold, unblind (142L) - CollarCommands.java: collar, takecollar, enslave, free (288L) - AccessoryCommands.java: putearplugs, takeearplugs, putclothes, takeclothes, fullyrestrain, adjust (469L) - BondageSubCommand.java: thin delegator (19L) Zero logic changes — purely mechanical code move. --- .../subcommands/AccessoryCommands.java | 469 +++++++ .../commands/subcommands/BindCommands.java | 156 +++ .../subcommands/BlindfoldCommands.java | 142 ++ .../subcommands/BondageSubCommand.java | 1202 +---------------- .../commands/subcommands/CollarCommands.java | 288 ++++ .../commands/subcommands/GagCommands.java | 140 ++ 6 files changed, 1202 insertions(+), 1195 deletions(-) create mode 100644 src/main/java/com/tiedup/remake/commands/subcommands/AccessoryCommands.java create mode 100644 src/main/java/com/tiedup/remake/commands/subcommands/BindCommands.java create mode 100644 src/main/java/com/tiedup/remake/commands/subcommands/BlindfoldCommands.java create mode 100644 src/main/java/com/tiedup/remake/commands/subcommands/CollarCommands.java create mode 100644 src/main/java/com/tiedup/remake/commands/subcommands/GagCommands.java diff --git a/src/main/java/com/tiedup/remake/commands/subcommands/AccessoryCommands.java b/src/main/java/com/tiedup/remake/commands/subcommands/AccessoryCommands.java new file mode 100644 index 0000000..2f0a851 --- /dev/null +++ b/src/main/java/com/tiedup/remake/commands/subcommands/AccessoryCommands.java @@ -0,0 +1,469 @@ +package com.tiedup.remake.commands.subcommands; + +import com.mojang.brigadier.arguments.FloatArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.tiedup.remake.items.base.AdjustmentHelper; +import com.tiedup.remake.items.ModItems; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.tiedup.remake.commands.CommandHelper; +import com.tiedup.remake.v2.bondage.CollarHelper; +import com.tiedup.remake.core.SystemMessageManager; +import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem; +import net.minecraft.resources.ResourceLocation; +import com.tiedup.remake.state.PlayerBindState; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +@SuppressWarnings("null") +public class AccessoryCommands { + + public static void register(LiteralArgumentBuilder root) { + // /tiedup putearplugs + root.then( + Commands.literal("putearplugs") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(AccessoryCommands::putearplugs) + ) + ); + // /tiedup takeearplugs + root.then( + Commands.literal("takeearplugs") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(AccessoryCommands::takeearplugs) + ) + ); + // /tiedup putclothes + root.then( + Commands.literal("putclothes") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(AccessoryCommands::putclothes) + ) + ); + // /tiedup takeclothes + root.then( + Commands.literal("takeclothes") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(AccessoryCommands::takeclothes) + ) + ); + // /tiedup fullyrestrain + root.then( + Commands.literal("fullyrestrain") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(AccessoryCommands::fullyrestrain) + ) + ); + // /tiedup adjust + root.then( + Commands.literal("adjust") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument("player", EntityArgument.player()).then( + Commands.argument("type", StringArgumentType.word()) + .suggests((ctx, builder) -> { + builder.suggest("gag"); + builder.suggest("blindfold"); + builder.suggest("all"); + return builder.buildFuture(); + }) + .then( + Commands.argument( + "value", + FloatArgumentType.floatArg(-4.0f, 4.0f) + ).executes(AccessoryCommands::adjust) + ) + ) + ) + ); + } + + static int putearplugs(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (state.hasEarplugs()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " already has earplugs" + ) + ); + return 0; + } + + ItemStack earplugs = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_earplugs")); + state.putEarplugsOn(earplugs); + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + " has been given earplugs" + ), + true + ); + SystemMessageManager.sendToTarget( + context.getSource().getEntity(), + targetPlayer, + SystemMessageManager.MessageCategory.EARPLUGS_ON + ); + + return 1; + } + + static int takeearplugs(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (!state.hasEarplugs()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " does not have earplugs" + ) + ); + return 0; + } + + state.takeEarplugsOff(); + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + "'s earplugs have been removed" + ), + true + ); + SystemMessageManager.sendToPlayer( + targetPlayer, + SystemMessageManager.MessageCategory.INFO, + "Your earplugs have been removed!" + ); + + return 1; + } + + static int putclothes(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (state.hasClothes()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " already has clothes" + ) + ); + return 0; + } + + ItemStack clothes = new ItemStack(ModItems.CLOTHES.get()); + state.putClothesOn(clothes); + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + " has been given clothes" + ), + true + ); + + return 1; + } + + static int takeclothes(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (!state.hasClothes()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " is not wearing clothes" + ) + ); + return 0; + } + + ItemStack removed = state.takeClothesOff(); + CommandHelper.syncPlayerState(targetPlayer, state); + + if (!removed.isEmpty()) { + targetPlayer.drop(removed, false); + } + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "Removed clothes from " + + targetPlayer.getName().getString() + ), + true + ); + return 1; + } + + static int fullyrestrain(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + int applied = 0; + + if (!state.isTiedUp()) { + ItemStack ropes = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes")); + state.putBindOn(ropes); + applied++; + } + + if (!state.isGagged()) { + ItemStack gag = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag")); + state.putGagOn(gag); + applied++; + } + + if (!state.isBlindfolded()) { + ItemStack blindfold = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold")); + state.putBlindfoldOn(blindfold); + applied++; + } + + if (!state.hasCollar()) { + ItemStack collar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(new net.minecraft.resources.ResourceLocation("tiedup", "classic_collar")); + if ( + context.getSource().getEntity() instanceof ServerPlayer executor + ) { + CollarHelper.addOwner(collar, executor); + } + state.putCollarOn(collar); + applied++; + } + + if (!state.hasEarplugs()) { + ItemStack earplugs = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_earplugs")); + state.putEarplugsOn(earplugs); + applied++; + } + + if (applied == 0) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " is already fully restrained" + ) + ); + return 0; + } + + CommandHelper.syncPlayerState(targetPlayer, state); + + int finalApplied = applied; + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + " has been fully restrained (" + + finalApplied + + " items applied)" + ), + true + ); + SystemMessageManager.sendToPlayer( + targetPlayer, + SystemMessageManager.MessageCategory.INFO, + "You have been fully restrained!" + ); + + return 1; + } + + static int adjust(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + String type = StringArgumentType.getString(context, "type"); + float value = FloatArgumentType.getFloat(context, "value"); + + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + boolean adjustedGag = false; + boolean adjustedBlindfold = false; + + if (type.equals("gag") || type.equals("all")) { + ItemStack gag = state.getEquipment( + com.tiedup.remake.v2.BodyRegionV2.MOUTH + ); + if (!gag.isEmpty()) { + AdjustmentHelper.setAdjustment(gag, value); + adjustedGag = true; + } + } + + if (type.equals("blindfold") || type.equals("all")) { + ItemStack blindfold = state.getEquipment( + com.tiedup.remake.v2.BodyRegionV2.EYES + ); + if (!blindfold.isEmpty()) { + AdjustmentHelper.setAdjustment(blindfold, value); + adjustedBlindfold = true; + } + } + + if ( + !type.equals("gag") && + !type.equals("blindfold") && + !type.equals("all") + ) { + context + .getSource() + .sendFailure( + Component.literal( + "Invalid type. Use: gag, blindfold, or all" + ) + ); + return 0; + } + + if (!adjustedGag && !adjustedBlindfold) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " has no " + + type + + " to adjust" + ) + ); + return 0; + } + + CommandHelper.syncPlayerState(targetPlayer, state); + + String items = + adjustedGag && adjustedBlindfold + ? "gag and blindfold" + : adjustedGag + ? "gag" + : "blindfold"; + String valueStr = String.format("%.2f", value); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7aAdjusted " + + items + + " for " + + targetPlayer.getName().getString() + + " to " + + valueStr + + " pixels" + ), + true + ); + + return 1; + } + +} diff --git a/src/main/java/com/tiedup/remake/commands/subcommands/BindCommands.java b/src/main/java/com/tiedup/remake/commands/subcommands/BindCommands.java new file mode 100644 index 0000000..84a82df --- /dev/null +++ b/src/main/java/com/tiedup/remake/commands/subcommands/BindCommands.java @@ -0,0 +1,156 @@ +package com.tiedup.remake.commands.subcommands; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.tiedup.remake.commands.CommandHelper; +import com.tiedup.remake.core.SystemMessageManager; +import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem; +import net.minecraft.resources.ResourceLocation; +import com.tiedup.remake.state.PlayerBindState; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +@SuppressWarnings("null") +public class BindCommands { + + public static void register(LiteralArgumentBuilder root) { + // /tiedup untie + root.then( + Commands.literal("untie") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(BindCommands::untie) + ) + ); + } + + static int tie(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (state.isTiedUp()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " is already tied up" + ) + ); + return 0; + } + + ItemStack ropes = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes")); + state.putBindOn(ropes); + + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + " has been tied up" + ), + true + ); + SystemMessageManager.sendTiedUp( + context.getSource().getEntity(), + targetPlayer + ); + + return 1; + } + + static int untie(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if ( + !state.isTiedUp() && + !state.isGagged() && + !state.isBlindfolded() && + !state.hasCollar() + ) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " is not restrained" + ) + ); + return 0; + } + + boolean removed = false; + if (state.isTiedUp()) { + state.takeBindOff(); + removed = true; + } + if (state.isGagged()) { + state.takeGagOff(); + removed = true; + } + if (state.isBlindfolded()) { + state.takeBlindfoldOff(); + removed = true; + } + if (state.hasCollar()) { + state.takeCollarOff(); + removed = true; + } + if (state.hasEarplugs()) { + state.takeEarplugsOff(); + removed = true; + } + + if (removed) { + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + " has been freed from all restraints" + ), + true + ); + SystemMessageManager.sendFreed(targetPlayer); + + return 1; + } + + return 0; + } + +} diff --git a/src/main/java/com/tiedup/remake/commands/subcommands/BlindfoldCommands.java b/src/main/java/com/tiedup/remake/commands/subcommands/BlindfoldCommands.java new file mode 100644 index 0000000..44d297b --- /dev/null +++ b/src/main/java/com/tiedup/remake/commands/subcommands/BlindfoldCommands.java @@ -0,0 +1,142 @@ +package com.tiedup.remake.commands.subcommands; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.tiedup.remake.commands.CommandHelper; +import com.tiedup.remake.core.SystemMessageManager; +import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem; +import net.minecraft.resources.ResourceLocation; +import com.tiedup.remake.state.PlayerBindState; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +@SuppressWarnings("null") +public class BlindfoldCommands { + + public static void register(LiteralArgumentBuilder root) { + // /tiedup blindfold + root.then( + Commands.literal("blindfold") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(BlindfoldCommands::blindfold) + ) + ); + // /tiedup unblind + root.then( + Commands.literal("unblind") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(BlindfoldCommands::unblind) + ) + ); + } + + static int blindfold(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (state.isBlindfolded()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " is already blindfolded" + ) + ); + return 0; + } + + ItemStack blindfold = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold")); + state.putBlindfoldOn(blindfold); + + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + " has been blindfolded" + ), + true + ); + SystemMessageManager.sendToTarget( + context.getSource().getEntity(), + targetPlayer, + SystemMessageManager.MessageCategory.BLINDFOLDED + ); + + return 1; + } + + static int unblind(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (!state.isBlindfolded()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " is not blindfolded" + ) + ); + return 0; + } + + state.takeBlindfoldOff(); + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + "'s blindfold has been removed" + ), + true + ); + SystemMessageManager.sendToTarget( + context.getSource().getEntity(), + targetPlayer, + SystemMessageManager.MessageCategory.UNBLINDFOLDED + ); + + return 1; + } + +} diff --git a/src/main/java/com/tiedup/remake/commands/subcommands/BondageSubCommand.java b/src/main/java/com/tiedup/remake/commands/subcommands/BondageSubCommand.java index 20b8301..e6a5c05 100644 --- a/src/main/java/com/tiedup/remake/commands/subcommands/BondageSubCommand.java +++ b/src/main/java/com/tiedup/remake/commands/subcommands/BondageSubCommand.java @@ -1,1207 +1,19 @@ package com.tiedup.remake.commands.subcommands; -import com.mojang.brigadier.arguments.FloatArgumentType; -import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.tiedup.remake.commands.CommandHelper; -import com.tiedup.remake.core.SystemMessageManager; -import com.tiedup.remake.items.ModItems; -import com.tiedup.remake.items.base.AdjustmentHelper; -import com.tiedup.remake.network.ModNetwork; -import com.tiedup.remake.v2.bondage.CollarHelper; -import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem; -import net.minecraft.resources.ResourceLocation; -import com.tiedup.remake.network.sync.PacketSyncBindState; -import com.tiedup.remake.state.PlayerBindState; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.arguments.EntityArgument; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.item.ItemStack; /** * Bondage-related sub-commands for /tiedup. - * Handles: tie, untie, gag, ungag, blindfold, unblind, collar, takecollar, - * takeearplugs, putearplugs, takeclothes, putclothes, fullyrestrain, enslave, free, adjust + * Delegates to specialized command classes. */ -@SuppressWarnings("null") public class BondageSubCommand { - public static void register( - LiteralArgumentBuilder root - ) { - // /tiedup tie - root.then( - Commands.literal("tie") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::tie) - ) - ); - // /tiedup untie - root.then( - Commands.literal("untie") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::untie) - ) - ); - // /tiedup gag - root.then( - Commands.literal("gag") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::gag) - ) - ); - // /tiedup ungag - root.then( - Commands.literal("ungag") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::ungag) - ) - ); - // /tiedup blindfold - root.then( - Commands.literal("blindfold") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::blindfold) - ) - ); - // /tiedup unblind - root.then( - Commands.literal("unblind") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::unblind) - ) - ); - // /tiedup collar - root.then( - Commands.literal("collar") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::collar) - ) - ); - // /tiedup takecollar - root.then( - Commands.literal("takecollar") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::takecollar) - ) - ); - // /tiedup takeearplugs - root.then( - Commands.literal("takeearplugs") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::takeearplugs) - ) - ); - // /tiedup putearplugs - root.then( - Commands.literal("putearplugs") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::putearplugs) - ) - ); - // /tiedup takeclothes - root.then( - Commands.literal("takeclothes") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::takeclothes) - ) - ); - // /tiedup putclothes - root.then( - Commands.literal("putclothes") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::putclothes) - ) - ); - // /tiedup fullyrestrain - root.then( - Commands.literal("fullyrestrain") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::fullyrestrain) - ) - ); - // /tiedup enslave - root.then( - Commands.literal("enslave") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::enslave) - ) - ); - // /tiedup free - root.then( - Commands.literal("free") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument( - "player", - EntityArgument.player() - ).executes(BondageSubCommand::free) - ) - ); - // /tiedup adjust - root.then( - Commands.literal("adjust") - .requires(CommandHelper.REQUIRES_OP) - .then( - Commands.argument("player", EntityArgument.player()).then( - Commands.argument("type", StringArgumentType.word()) - .suggests((ctx, builder) -> { - builder.suggest("gag"); - builder.suggest("blindfold"); - builder.suggest("all"); - return builder.buildFuture(); - }) - .then( - Commands.argument( - "value", - FloatArgumentType.floatArg(-4.0f, 4.0f) - ).executes(BondageSubCommand::adjust) - ) - ) - ) - ); - } - - // Command Implementations - - /** - * /tiedup tie - * - * Forcefully tie a player with ropes (default bind). - * Uses ItemRopes from ModItems. - */ - private static int tie(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (state.isTiedUp()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " is already tied up" - ) - ); - return 0; - } - - ItemStack ropes = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes")); - state.putBindOn(ropes); - - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - " has been tied up" - ), - true - ); - SystemMessageManager.sendTiedUp( - context.getSource().getEntity(), - targetPlayer - ); - - return 1; - } - - /** - * /tiedup untie - * - * Forcefully untie a player, removing ALL bondage equipment. - */ - private static int untie(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if ( - !state.isTiedUp() && - !state.isGagged() && - !state.isBlindfolded() && - !state.hasCollar() - ) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " is not restrained" - ) - ); - return 0; - } - - boolean removed = false; - if (state.isTiedUp()) { - state.takeBindOff(); - removed = true; - } - if (state.isGagged()) { - state.takeGagOff(); - removed = true; - } - if (state.isBlindfolded()) { - state.takeBlindfoldOff(); - removed = true; - } - if (state.hasCollar()) { - state.takeCollarOff(); - removed = true; - } - if (state.hasEarplugs()) { - state.takeEarplugsOff(); - removed = true; - } - - if (removed) { - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - " has been freed from all restraints" - ), - true - ); - SystemMessageManager.sendFreed(targetPlayer); - - return 1; - } - - return 0; - } - - /** - * /tiedup gag - * - * Forcefully gag a player with cloth gag (default). - */ - private static int gag(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (state.isGagged()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " is already gagged" - ) - ); - return 0; - } - - ItemStack gag = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag")); - state.putGagOn(gag); - - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - " has been gagged" - ), - true - ); - SystemMessageManager.sendGagged( - context.getSource().getEntity(), - targetPlayer - ); - - return 1; - } - - /** - * /tiedup blindfold - * - * Forcefully blindfold a player with classic blindfold (default). - */ - private static int blindfold(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (state.isBlindfolded()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " is already blindfolded" - ) - ); - return 0; - } - - ItemStack blindfold = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold")); - state.putBlindfoldOn(blindfold); - - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - " has been blindfolded" - ), - true - ); - SystemMessageManager.sendToTarget( - context.getSource().getEntity(), - targetPlayer, - SystemMessageManager.MessageCategory.BLINDFOLDED - ); - - return 1; - } - - /** - * /tiedup collar - * - * Give a collar to a player (forces collar even if not tied). - * The command executor becomes the owner of the collar. - */ - private static int collar(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (state.hasCollar()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " already has a collar" - ) - ); - return 0; - } - - ItemStack collar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(new net.minecraft.resources.ResourceLocation("tiedup", "classic_collar")); - - if (context.getSource().getEntity() instanceof ServerPlayer executor) { - CollarHelper.addOwner(collar, executor); - } - - state.putCollarOn(collar); - - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - " has been collared" - ), - true - ); - SystemMessageManager.sendToTarget( - context.getSource().getEntity(), - targetPlayer, - SystemMessageManager.MessageCategory.COLLARED - ); - - return 1; - } - - /** - * /tiedup free - * - * Free a player from slavery (removes master relationship). - * Does NOT remove collar or other equipment. - */ - private static int free(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (!state.isCaptive()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + " is not captured" - ) - ); - return 0; - } - - state.free(true); - - PacketSyncBindState statePacket = PacketSyncBindState.fromPlayer( - targetPlayer - ); - if (statePacket != null) { - ModNetwork.sendToPlayer(statePacket, targetPlayer); - } - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - " has been freed from slavery" - ), - true - ); - SystemMessageManager.sendFreed(targetPlayer); - - return 1; - } - - /** - * /tiedup adjust - * - * Adjust the Y position of gags and/or blindfolds on a player. - * Value range: -4.0 to +4.0 (pixels, 1 pixel = 1/16 block) - * - * Types: - * - gag: Adjust only gag - * - blindfold: Adjust only blindfold - * - all: Adjust both gag and blindfold - */ - private static int adjust(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - String type = StringArgumentType.getString(context, "type"); - float value = FloatArgumentType.getFloat(context, "value"); - - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - boolean adjustedGag = false; - boolean adjustedBlindfold = false; - - if (type.equals("gag") || type.equals("all")) { - ItemStack gag = state.getEquipment( - com.tiedup.remake.v2.BodyRegionV2.MOUTH - ); - if (!gag.isEmpty()) { - AdjustmentHelper.setAdjustment(gag, value); - adjustedGag = true; - } - } - - if (type.equals("blindfold") || type.equals("all")) { - ItemStack blindfold = state.getEquipment( - com.tiedup.remake.v2.BodyRegionV2.EYES - ); - if (!blindfold.isEmpty()) { - AdjustmentHelper.setAdjustment(blindfold, value); - adjustedBlindfold = true; - } - } - - if ( - !type.equals("gag") && - !type.equals("blindfold") && - !type.equals("all") - ) { - context - .getSource() - .sendFailure( - Component.literal( - "Invalid type. Use: gag, blindfold, or all" - ) - ); - return 0; - } - - if (!adjustedGag && !adjustedBlindfold) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " has no " + - type + - " to adjust" - ) - ); - return 0; - } - - CommandHelper.syncPlayerState(targetPlayer, state); - - String items = - adjustedGag && adjustedBlindfold - ? "gag and blindfold" - : adjustedGag - ? "gag" - : "blindfold"; - String valueStr = String.format("%.2f", value); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7aAdjusted " + - items + - " for " + - targetPlayer.getName().getString() + - " to " + - valueStr + - " pixels" - ), - true - ); - - return 1; - } - - /** - * /tiedup ungag - * - * Remove gag from a player. - */ - private static int ungag(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (!state.isGagged()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + " is not gagged" - ) - ); - return 0; - } - - state.takeGagOff(); - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - "'s gag has been removed" - ), - true - ); - SystemMessageManager.sendToTarget( - context.getSource().getEntity(), - targetPlayer, - SystemMessageManager.MessageCategory.UNGAGGED - ); - - return 1; - } - - /** - * /tiedup unblind - * - * Remove blindfold from a player. - */ - private static int unblind(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (!state.isBlindfolded()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " is not blindfolded" - ) - ); - return 0; - } - - state.takeBlindfoldOff(); - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - "'s blindfold has been removed" - ), - true - ); - SystemMessageManager.sendToTarget( - context.getSource().getEntity(), - targetPlayer, - SystemMessageManager.MessageCategory.UNBLINDFOLDED - ); - - return 1; - } - - /** - * /tiedup takecollar - * - * Remove collar from a player. - */ - private static int takecollar(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (!state.hasCollar()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " does not have a collar" - ) - ); - return 0; - } - - state.takeCollarOff(); - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - "'s collar has been removed" - ), - true - ); - SystemMessageManager.sendToTarget( - context.getSource().getEntity(), - targetPlayer, - SystemMessageManager.MessageCategory.UNCOLLARED - ); - - return 1; - } - - /** - * /tiedup takeearplugs - * - * Remove earplugs from a player. - */ - private static int takeearplugs(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (!state.hasEarplugs()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " does not have earplugs" - ) - ); - return 0; - } - - state.takeEarplugsOff(); - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - "'s earplugs have been removed" - ), - true - ); - SystemMessageManager.sendToPlayer( - targetPlayer, - SystemMessageManager.MessageCategory.INFO, - "Your earplugs have been removed!" - ); - - return 1; - } - - /** - * /tiedup takeclothes - * - * Remove clothes from a player. - */ - private static int takeclothes(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (!state.hasClothes()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " is not wearing clothes" - ) - ); - return 0; - } - - ItemStack removed = state.takeClothesOff(); - CommandHelper.syncPlayerState(targetPlayer, state); - - if (!removed.isEmpty()) { - targetPlayer.drop(removed, false); - } - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "Removed clothes from " + - targetPlayer.getName().getString() - ), - true - ); - return 1; - } - - /** - * /tiedup putclothes - * - * Put clothes on a player. - */ - private static int putclothes(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (state.hasClothes()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " already has clothes" - ) - ); - return 0; - } - - ItemStack clothes = new ItemStack(ModItems.CLOTHES.get()); - state.putClothesOn(clothes); - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - " has been given clothes" - ), - true - ); - - return 1; - } - - /** - * /tiedup putearplugs - * - * Put earplugs on a player. - */ - private static int putearplugs(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - if (state.hasEarplugs()) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " already has earplugs" - ) - ); - return 0; - } - - ItemStack earplugs = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_earplugs")); - state.putEarplugsOn(earplugs); - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - " has been given earplugs" - ), - true - ); - SystemMessageManager.sendToTarget( - context.getSource().getEntity(), - targetPlayer, - SystemMessageManager.MessageCategory.EARPLUGS_ON - ); - - return 1; - } - - /** - * /tiedup fullyrestrain - * - * Apply full bondage: bind + gag + blindfold + collar + earplugs - */ - private static int fullyrestrain(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - int applied = 0; - - if (!state.isTiedUp()) { - ItemStack ropes = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes")); - state.putBindOn(ropes); - applied++; - } - - if (!state.isGagged()) { - ItemStack gag = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag")); - state.putGagOn(gag); - applied++; - } - - if (!state.isBlindfolded()) { - ItemStack blindfold = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold")); - state.putBlindfoldOn(blindfold); - applied++; - } - - if (!state.hasCollar()) { - ItemStack collar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(new net.minecraft.resources.ResourceLocation("tiedup", "classic_collar")); - if ( - context.getSource().getEntity() instanceof ServerPlayer executor - ) { - CollarHelper.addOwner(collar, executor); - } - state.putCollarOn(collar); - applied++; - } - - if (!state.hasEarplugs()) { - ItemStack earplugs = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_earplugs")); - state.putEarplugsOn(earplugs); - applied++; - } - - if (applied == 0) { - context - .getSource() - .sendFailure( - Component.literal( - targetPlayer.getName().getString() + - " is already fully restrained" - ) - ); - return 0; - } - - CommandHelper.syncPlayerState(targetPlayer, state); - - int finalApplied = applied; - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - " has been fully restrained (" + - finalApplied + - " items applied)" - ), - true - ); - SystemMessageManager.sendToPlayer( - targetPlayer, - SystemMessageManager.MessageCategory.INFO, - "You have been fully restrained!" - ); - - return 1; - } - - /** - * /tiedup enslave - * - * Fully restrain and enslave a player. - * The command executor becomes the master. - */ - private static int enslave(CommandContext context) - throws CommandSyntaxException { - ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); - PlayerBindState state = PlayerBindState.getInstance(targetPlayer); - - if (state == null) { - context - .getSource() - .sendFailure(Component.literal("Failed to get player state")); - return 0; - } - - // First fully restrain - if (!state.isTiedUp()) { - ItemStack ropes = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes")); - state.putBindOn(ropes); - } - if (!state.isGagged()) { - ItemStack gag = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag")); - state.putGagOn(gag); - } - if (!state.isBlindfolded()) { - ItemStack blindfold = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold")); - state.putBlindfoldOn(blindfold); - } - if (!state.hasCollar()) { - ItemStack collar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(new net.minecraft.resources.ResourceLocation("tiedup", "classic_collar")); - if ( - context.getSource().getEntity() instanceof ServerPlayer executor - ) { - CollarHelper.addOwner(collar, executor); - } - state.putCollarOn(collar); - } - if (!state.hasEarplugs()) { - ItemStack earplugs = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_earplugs")); - state.putEarplugsOn(earplugs); - } - - // Capture target (this makes them a captive) - if (context.getSource().getEntity() instanceof ServerPlayer master) { - PlayerBindState masterState = PlayerBindState.getInstance(master); - if (masterState != null && masterState.getCaptorManager() != null) { - masterState.getCaptorManager().addCaptive(state); - } - } - - CommandHelper.syncPlayerState(targetPlayer, state); - - context - .getSource() - .sendSuccess( - () -> - Component.literal( - "\u00a7a" + - targetPlayer.getName().getString() + - " has been enslaved" - ), - true - ); - SystemMessageManager.sendEnslaved( - context.getSource().getEntity(), - targetPlayer - ); - - return 1; + public static void register(LiteralArgumentBuilder root) { + BindCommands.register(root); + GagCommands.register(root); + BlindfoldCommands.register(root); + CollarCommands.register(root); + AccessoryCommands.register(root); } } diff --git a/src/main/java/com/tiedup/remake/commands/subcommands/CollarCommands.java b/src/main/java/com/tiedup/remake/commands/subcommands/CollarCommands.java new file mode 100644 index 0000000..d83682d --- /dev/null +++ b/src/main/java/com/tiedup/remake/commands/subcommands/CollarCommands.java @@ -0,0 +1,288 @@ +package com.tiedup.remake.commands.subcommands; + +import com.tiedup.remake.network.ModNetwork; +import com.tiedup.remake.network.sync.PacketSyncBindState; +import com.tiedup.remake.v2.bondage.CollarHelper; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.tiedup.remake.commands.CommandHelper; +import com.tiedup.remake.core.SystemMessageManager; +import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem; +import net.minecraft.resources.ResourceLocation; +import com.tiedup.remake.state.PlayerBindState; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +@SuppressWarnings("null") +public class CollarCommands { + + public static void register(LiteralArgumentBuilder root) { + // /tiedup collar + root.then( + Commands.literal("collar") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(CollarCommands::collar) + ) + ); + // /tiedup takecollar + root.then( + Commands.literal("takecollar") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(CollarCommands::takecollar) + ) + ); + // /tiedup enslave + root.then( + Commands.literal("enslave") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(CollarCommands::enslave) + ) + ); + // /tiedup free + root.then( + Commands.literal("free") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(CollarCommands::free) + ) + ); + } + + static int collar(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (state.hasCollar()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " already has a collar" + ) + ); + return 0; + } + + ItemStack collar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(new net.minecraft.resources.ResourceLocation("tiedup", "classic_collar")); + + if (context.getSource().getEntity() instanceof ServerPlayer executor) { + CollarHelper.addOwner(collar, executor); + } + + state.putCollarOn(collar); + + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + " has been collared" + ), + true + ); + SystemMessageManager.sendToTarget( + context.getSource().getEntity(), + targetPlayer, + SystemMessageManager.MessageCategory.COLLARED + ); + + return 1; + } + + static int takecollar(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (!state.hasCollar()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " does not have a collar" + ) + ); + return 0; + } + + state.takeCollarOff(); + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + "'s collar has been removed" + ), + true + ); + SystemMessageManager.sendToTarget( + context.getSource().getEntity(), + targetPlayer, + SystemMessageManager.MessageCategory.UNCOLLARED + ); + + return 1; + } + + static int enslave(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + // First fully restrain + if (!state.isTiedUp()) { + ItemStack ropes = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "ropes")); + state.putBindOn(ropes); + } + if (!state.isGagged()) { + ItemStack gag = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag")); + state.putGagOn(gag); + } + if (!state.isBlindfolded()) { + ItemStack blindfold = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_blindfold")); + state.putBlindfoldOn(blindfold); + } + if (!state.hasCollar()) { + ItemStack collar = com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem.createStack(new net.minecraft.resources.ResourceLocation("tiedup", "classic_collar")); + if ( + context.getSource().getEntity() instanceof ServerPlayer executor + ) { + CollarHelper.addOwner(collar, executor); + } + state.putCollarOn(collar); + } + if (!state.hasEarplugs()) { + ItemStack earplugs = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "classic_earplugs")); + state.putEarplugsOn(earplugs); + } + + // Capture target (this makes them a captive) + if (context.getSource().getEntity() instanceof ServerPlayer master) { + PlayerBindState masterState = PlayerBindState.getInstance(master); + if (masterState != null && masterState.getCaptorManager() != null) { + masterState.getCaptorManager().addCaptive(state); + } + } + + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + " has been enslaved" + ), + true + ); + SystemMessageManager.sendEnslaved( + context.getSource().getEntity(), + targetPlayer + ); + + return 1; + } + + static int free(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (!state.isCaptive()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + " is not captured" + ) + ); + return 0; + } + + state.free(true); + + PacketSyncBindState statePacket = PacketSyncBindState.fromPlayer( + targetPlayer + ); + if (statePacket != null) { + ModNetwork.sendToPlayer(statePacket, targetPlayer); + } + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + " has been freed from slavery" + ), + true + ); + SystemMessageManager.sendFreed(targetPlayer); + + return 1; + } + +} diff --git a/src/main/java/com/tiedup/remake/commands/subcommands/GagCommands.java b/src/main/java/com/tiedup/remake/commands/subcommands/GagCommands.java new file mode 100644 index 0000000..2f3a2ef --- /dev/null +++ b/src/main/java/com/tiedup/remake/commands/subcommands/GagCommands.java @@ -0,0 +1,140 @@ +package com.tiedup.remake.commands.subcommands; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.tiedup.remake.commands.CommandHelper; +import com.tiedup.remake.core.SystemMessageManager; +import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem; +import net.minecraft.resources.ResourceLocation; +import com.tiedup.remake.state.PlayerBindState; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.EntityArgument; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; + +@SuppressWarnings("null") +public class GagCommands { + + public static void register(LiteralArgumentBuilder root) { + // /tiedup gag + root.then( + Commands.literal("gag") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(GagCommands::gag) + ) + ); + // /tiedup ungag + root.then( + Commands.literal("ungag") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(GagCommands::ungag) + ) + ); + } + + static int gag(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (state.isGagged()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + + " is already gagged" + ) + ); + return 0; + } + + ItemStack gag = DataDrivenBondageItem.createStack(new ResourceLocation("tiedup", "cloth_gag")); + state.putGagOn(gag); + + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + " has been gagged" + ), + true + ); + SystemMessageManager.sendGagged( + context.getSource().getEntity(), + targetPlayer + ); + + return 1; + } + + static int ungag(CommandContext context) + throws CommandSyntaxException { + ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player"); + PlayerBindState state = PlayerBindState.getInstance(targetPlayer); + + if (state == null) { + context + .getSource() + .sendFailure(Component.literal("Failed to get player state")); + return 0; + } + + if (!state.isGagged()) { + context + .getSource() + .sendFailure( + Component.literal( + targetPlayer.getName().getString() + " is not gagged" + ) + ); + return 0; + } + + state.takeGagOff(); + CommandHelper.syncPlayerState(targetPlayer, state); + + context + .getSource() + .sendSuccess( + () -> + Component.literal( + "\u00a7a" + + targetPlayer.getName().getString() + + "'s gag has been removed" + ), + true + ); + SystemMessageManager.sendToTarget( + context.getSource().getEntity(), + targetPlayer, + SystemMessageManager.MessageCategory.UNGAGGED + ); + + return 1; + } + +} From d3bdb026f38830fb730551c4be6a15209d9b2a14 Mon Sep 17 00:00:00 2001 From: NotEvil Date: Wed, 15 Apr 2026 11:14:28 +0200 Subject: [PATCH 3/3] fix: restore missing /tiedup tie command registration (review) --- .../remake/commands/subcommands/BindCommands.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/com/tiedup/remake/commands/subcommands/BindCommands.java b/src/main/java/com/tiedup/remake/commands/subcommands/BindCommands.java index 84a82df..f8d897c 100644 --- a/src/main/java/com/tiedup/remake/commands/subcommands/BindCommands.java +++ b/src/main/java/com/tiedup/remake/commands/subcommands/BindCommands.java @@ -19,6 +19,17 @@ import net.minecraft.world.item.ItemStack; public class BindCommands { public static void register(LiteralArgumentBuilder root) { + // /tiedup tie + root.then( + Commands.literal("tie") + .requires(CommandHelper.REQUIRES_OP) + .then( + Commands.argument( + "player", + EntityArgument.player() + ).executes(BindCommands::tie) + ) + ); // /tiedup untie root.then( Commands.literal("untie")