package com.tiedup.remake.commands; 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.items.clothes.GenericClothes; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.item.ItemStack; /** * Command handler for clothes configuration. * * Subcommands: * /tiedup clothes url set - Set dynamic texture URL on held clothes * /tiedup clothes url reset - Remove dynamic texture URL * /tiedup clothes fullskin - Toggle full-skin mode * /tiedup clothes smallarms - Toggle small arms forcing * /tiedup clothes keephead - Toggle keep head mode (preserves wearer's head) * /tiedup clothes layer - Toggle layer visibility * * All commands operate on the clothes item held in main hand. */ public class ClothesCommand { /** * Create the /tiedup clothes ... command tree. * * @return The command builder */ public static LiteralArgumentBuilder< CommandSourceStack > createClothesCommand() { return Commands.literal("clothes") // /tiedup clothes url set // /tiedup clothes url reset .then( Commands.literal("url") .then( Commands.literal("set").then( Commands.argument( "url", StringArgumentType.greedyString() ).executes(ClothesCommand::setUrl) ) ) .then( Commands.literal("reset").executes( ClothesCommand::resetUrl ) ) ) // /tiedup clothes fullskin .then( Commands.literal("fullskin").executes( ClothesCommand::toggleFullSkin ) ) // /tiedup clothes smallarms .then( Commands.literal("smallarms").executes( ClothesCommand::toggleSmallArms ) ) // /tiedup clothes keephead .then( Commands.literal("keephead").executes( ClothesCommand::toggleKeepHead ) ) // /tiedup clothes layer .then( Commands.literal("layer").then( Commands.argument("part", StringArgumentType.word()) .suggests((ctx, builder) -> { builder.suggest("head"); builder.suggest("body"); builder.suggest("leftarm"); builder.suggest("rightarm"); builder.suggest("leftleg"); builder.suggest("rightleg"); return builder.buildFuture(); }) .executes(ClothesCommand::toggleLayer) ) ); } /** * Get the clothes ItemStack from player's main hand. * Returns null and sends error message if not holding clothes. */ private static ItemStack getHeldClothes( CommandContext ctx ) throws CommandSyntaxException { ServerPlayer player = ctx.getSource().getPlayerOrException(); ItemStack held = player.getMainHandItem(); if (held.isEmpty() || !(held.getItem() instanceof GenericClothes)) { ctx .getSource() .sendFailure( Component.translatable("command.tiedup.clothes.not_holding") ); return null; } return held; } /** * /tiedup clothes url set */ private static int setUrl(CommandContext ctx) throws CommandSyntaxException { ItemStack clothes = getHeldClothes(ctx); if (clothes == null) return 0; String url = StringArgumentType.getString(ctx, "url"); // Basic URL validation if (!url.startsWith("https://")) { ctx .getSource() .sendFailure( Component.translatable( "command.tiedup.clothes.url_must_https" ) ); return 0; } // Check URL length if (url.length() > 2048) { ctx .getSource() .sendFailure( Component.translatable( "command.tiedup.clothes.url_too_long" ) ); return 0; } GenericClothes item = (GenericClothes) clothes.getItem(); item.setDynamicTextureUrl(clothes, url); ctx .getSource() .sendSuccess( () -> Component.translatable("command.tiedup.clothes.url_set"), false ); return 1; } /** * /tiedup clothes url reset */ private static int resetUrl(CommandContext ctx) throws CommandSyntaxException { ItemStack clothes = getHeldClothes(ctx); if (clothes == null) return 0; GenericClothes item = (GenericClothes) clothes.getItem(); item.removeDynamicTextureUrl(clothes); ctx .getSource() .sendSuccess( () -> Component.translatable("command.tiedup.clothes.url_reset"), false ); return 1; } /** * /tiedup clothes fullskin */ private static int toggleFullSkin(CommandContext ctx) throws CommandSyntaxException { ItemStack clothes = getHeldClothes(ctx); if (clothes == null) return 0; GenericClothes item = (GenericClothes) clothes.getItem(); boolean newState = !item.isFullSkinEnabled(clothes); item.setFullSkinEnabled(clothes, newState); String stateKey = newState ? "enabled" : "disabled"; ctx .getSource() .sendSuccess( () -> Component.translatable( "command.tiedup.clothes.fullskin_" + stateKey ), false ); return 1; } /** * /tiedup clothes smallarms */ private static int toggleSmallArms(CommandContext ctx) throws CommandSyntaxException { ItemStack clothes = getHeldClothes(ctx); if (clothes == null) return 0; GenericClothes item = (GenericClothes) clothes.getItem(); boolean newState = !item.shouldForceSmallArms(clothes); item.setForceSmallArms(clothes, newState); String stateKey = newState ? "enabled" : "disabled"; ctx .getSource() .sendSuccess( () -> Component.translatable( "command.tiedup.clothes.smallarms_" + stateKey ), false ); return 1; } /** * /tiedup clothes keephead * When enabled, the wearer's head/hat is preserved instead of being replaced by clothes. */ private static int toggleKeepHead(CommandContext ctx) throws CommandSyntaxException { ItemStack clothes = getHeldClothes(ctx); if (clothes == null) return 0; GenericClothes item = (GenericClothes) clothes.getItem(); boolean newState = !item.isKeepHeadEnabled(clothes); item.setKeepHeadEnabled(clothes, newState); String stateKey = newState ? "enabled" : "disabled"; ctx .getSource() .sendSuccess( () -> Component.translatable( "command.tiedup.clothes.keephead_" + stateKey ), false ); return 1; } /** * /tiedup clothes layer */ private static int toggleLayer(CommandContext ctx) throws CommandSyntaxException { ItemStack clothes = getHeldClothes(ctx); if (clothes == null) return 0; String part = StringArgumentType.getString(ctx, "part").toLowerCase(); String layerKey = mapPartToLayerKey(part); if (layerKey == null) { ctx .getSource() .sendFailure( Component.translatable( "command.tiedup.clothes.unknown_layer", part ) ); return 0; } GenericClothes item = (GenericClothes) clothes.getItem(); boolean newState = !item.isLayerEnabled(clothes, layerKey); item.setLayerEnabled(clothes, layerKey, newState); String stateKey = newState ? "visible" : "hidden"; ctx .getSource() .sendSuccess( () -> Component.translatable( "command.tiedup.clothes.layer_" + stateKey, part ), false ); return 1; } /** * Map user-friendly part name to NBT layer key. */ private static String mapPartToLayerKey(String part) { return switch (part) { case "head" -> GenericClothes.LAYER_HEAD; case "body" -> GenericClothes.LAYER_BODY; case "leftarm" -> GenericClothes.LAYER_LEFT_ARM; case "rightarm" -> GenericClothes.LAYER_RIGHT_ARM; case "leftleg" -> GenericClothes.LAYER_LEFT_LEG; case "rightleg" -> GenericClothes.LAYER_RIGHT_LEG; default -> null; }; } }