Phase 3: Migrate Component.literal() in all remaining command files. - NPCCommand (34), CellCommand (33), SocialCommand (16), CollarCommand (25), KeyCommand (18), BountyCommand (6), KidnapSetCommand (2), CaptivityDebugCommand (7), InventorySubCommand (3), TestAnimSubCommand (2), MasterTestSubCommand (7), DebtSubCommand (8) - Strip all section sign color codes, use .withStyle(ChatFormatting) - 148 new keys in en_us.json (command.tiedup.*) - Debug/dynamic strings intentionally kept as literal
174 lines
5.5 KiB
Java
174 lines
5.5 KiB
Java
package com.tiedup.remake.commands;
|
|
|
|
import com.mojang.brigadier.CommandDispatcher;
|
|
import com.mojang.brigadier.context.CommandContext;
|
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
import com.tiedup.remake.bounty.Bounty;
|
|
import com.tiedup.remake.bounty.BountyManager;
|
|
import com.tiedup.remake.core.SettingsAccessor;
|
|
import com.tiedup.remake.core.TiedUpMod;
|
|
import com.tiedup.remake.state.IBondageState;
|
|
import com.tiedup.remake.util.KidnappedHelper;
|
|
import java.util.Optional;
|
|
import net.minecraft.ChatFormatting;
|
|
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;
|
|
|
|
/**
|
|
* Command: /bounty <player>
|
|
*
|
|
*
|
|
* Creates a bounty on a target player using the held item as reward.
|
|
*
|
|
* Requirements:
|
|
* - Must hold an item (the reward)
|
|
* - Cannot put bounty on yourself
|
|
* - Respects max bounties limit
|
|
* - Cannot be tied up when creating bounty
|
|
*/
|
|
public class BountyCommand {
|
|
|
|
public static void register(
|
|
CommandDispatcher<CommandSourceStack> dispatcher
|
|
) {
|
|
dispatcher.register(createBountyCommand());
|
|
TiedUpMod.LOGGER.info("Registered /bounty command");
|
|
}
|
|
|
|
/**
|
|
* Create the bounty command builder (for use as subcommand of /tiedup).
|
|
* @return The command builder
|
|
*/
|
|
public static com.mojang.brigadier.builder.LiteralArgumentBuilder<
|
|
CommandSourceStack
|
|
> createBountyCommand() {
|
|
return Commands.literal("bounty").then(
|
|
Commands.argument("target", EntityArgument.player()).executes(
|
|
BountyCommand::execute
|
|
)
|
|
);
|
|
}
|
|
|
|
private static int execute(CommandContext<CommandSourceStack> context)
|
|
throws CommandSyntaxException {
|
|
CommandSourceStack source = context.getSource();
|
|
|
|
// Must be a player
|
|
Optional<ServerPlayer> playerOpt = CommandHelper.getPlayerOrFail(
|
|
source
|
|
);
|
|
if (playerOpt.isEmpty()) return 0;
|
|
ServerPlayer player = playerOpt.get();
|
|
|
|
ServerPlayer target = EntityArgument.getPlayer(context, "target");
|
|
|
|
// Cannot bounty yourself
|
|
if (player.getUUID().equals(target.getUUID())) {
|
|
source.sendFailure(
|
|
Component.translatable(
|
|
"command.tiedup.bounty.cannot_self"
|
|
).withStyle(ChatFormatting.RED)
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
// Check if player is tied up
|
|
IBondageState playerState = KidnappedHelper.getKidnappedState(player);
|
|
if (playerState != null && playerState.isTiedUp()) {
|
|
source.sendFailure(
|
|
Component.translatable(
|
|
"command.tiedup.bounty.tied_up"
|
|
).withStyle(ChatFormatting.RED)
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
// Get bounty manager
|
|
BountyManager manager = BountyManager.get(player.serverLevel());
|
|
|
|
// Check bounty limit
|
|
if (!manager.canCreateBounty(player, player.serverLevel())) {
|
|
int max = SettingsAccessor.getMaxBounties(
|
|
player.serverLevel().getGameRules()
|
|
);
|
|
source.sendFailure(
|
|
Component.translatable(
|
|
"command.tiedup.bounty.max_reached", max
|
|
).withStyle(ChatFormatting.RED)
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
// Must hold an item as reward
|
|
ItemStack heldItem = player.getMainHandItem();
|
|
if (heldItem.isEmpty()) {
|
|
source.sendFailure(
|
|
Component.translatable(
|
|
"command.tiedup.bounty.must_hold_item"
|
|
).withStyle(ChatFormatting.RED)
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
// Get bounty duration
|
|
int duration = SettingsAccessor.getBountyDuration(
|
|
player.serverLevel().getGameRules()
|
|
);
|
|
|
|
// SECURITY FIX: Create reward with count=1 to prevent item duplication
|
|
// Bug: If player held 64 diamonds, bounty would be 64 diamonds but only cost 1
|
|
ItemStack rewardItem = heldItem.copy();
|
|
rewardItem.setCount(1);
|
|
|
|
// Create the bounty
|
|
Bounty bounty = new Bounty(
|
|
player.getUUID(),
|
|
player.getName().getString(),
|
|
target.getUUID(),
|
|
target.getName().getString(),
|
|
rewardItem,
|
|
duration
|
|
);
|
|
|
|
// Consume one item from the stack (not the entire stack!)
|
|
player.getMainHandItem().shrink(1);
|
|
|
|
// Add bounty
|
|
manager.addBounty(bounty);
|
|
|
|
// Notify player
|
|
source.sendSuccess(
|
|
() ->
|
|
Component.translatable(
|
|
"command.tiedup.bounty.created", target.getName().getString()
|
|
).withStyle(ChatFormatting.GREEN),
|
|
false
|
|
);
|
|
|
|
// Broadcast to all players
|
|
player.server
|
|
.getPlayerList()
|
|
.broadcastSystemMessage(
|
|
Component.translatable(
|
|
"command.tiedup.bounty.broadcast",
|
|
player.getName().getString(),
|
|
target.getName().getString()
|
|
).withStyle(ChatFormatting.GOLD),
|
|
false
|
|
);
|
|
|
|
TiedUpMod.LOGGER.info(
|
|
"[BOUNTY] {} created bounty on {} with reward {}",
|
|
player.getName().getString(),
|
|
target.getName().getString(),
|
|
bounty.getRewardDescription()
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
}
|