Compare commits
15 Commits
chore/quic
...
chore/audi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d56024c7e | ||
|
|
8823c671d7 | ||
|
|
6d9d6b4b81 | ||
|
|
706172fb9a | ||
|
|
3aaf92b788 | ||
|
|
69f52eacf3 | ||
| a4fc05b503 | |||
|
|
7444853840 | ||
|
|
22d79a452b | ||
| 70f85b58a6 | |||
|
|
c34bac11b0 | ||
|
|
fa5cfb913c | ||
|
|
70965c2dda | ||
|
|
0662739fe0 | ||
| ac72f6aae7 |
@@ -670,13 +670,7 @@ public class CellRegistryV2 extends SavedData {
|
||||
if (server == null || ownerId == null) return;
|
||||
ServerPlayer owner = server.getPlayerList().getPlayer(ownerId);
|
||||
if (owner != null) {
|
||||
String template = SystemMessageManager.getTemplate(category);
|
||||
String formattedMessage = String.format(template, prisonerName);
|
||||
SystemMessageManager.sendToPlayer(
|
||||
owner,
|
||||
category,
|
||||
formattedMessage
|
||||
);
|
||||
SystemMessageManager.sendTranslatable(owner, category, prisonerName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,8 +69,8 @@ public class BountyCommand {
|
||||
// Cannot bounty yourself
|
||||
if (player.getUUID().equals(target.getUUID())) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
"You cannot put a bounty on yourself!"
|
||||
Component.translatable(
|
||||
"command.tiedup.bounty.cannot_self"
|
||||
).withStyle(ChatFormatting.RED)
|
||||
);
|
||||
return 0;
|
||||
@@ -80,8 +80,8 @@ public class BountyCommand {
|
||||
IBondageState playerState = KidnappedHelper.getKidnappedState(player);
|
||||
if (playerState != null && playerState.isTiedUp()) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
"You cannot create bounties while tied up!"
|
||||
Component.translatable(
|
||||
"command.tiedup.bounty.tied_up"
|
||||
).withStyle(ChatFormatting.RED)
|
||||
);
|
||||
return 0;
|
||||
@@ -96,8 +96,8 @@ public class BountyCommand {
|
||||
player.serverLevel().getGameRules()
|
||||
);
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
"Maximum number (" + max + ") of active bounties reached!"
|
||||
Component.translatable(
|
||||
"command.tiedup.bounty.max_reached", max
|
||||
).withStyle(ChatFormatting.RED)
|
||||
);
|
||||
return 0;
|
||||
@@ -107,8 +107,8 @@ public class BountyCommand {
|
||||
ItemStack heldItem = player.getMainHandItem();
|
||||
if (heldItem.isEmpty()) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
"You must hold an item as the reward!"
|
||||
Component.translatable(
|
||||
"command.tiedup.bounty.must_hold_item"
|
||||
).withStyle(ChatFormatting.RED)
|
||||
);
|
||||
return 0;
|
||||
@@ -143,8 +143,8 @@ public class BountyCommand {
|
||||
// Notify player
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Bounty created on " + target.getName().getString() + "!"
|
||||
Component.translatable(
|
||||
"command.tiedup.bounty.created", target.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
false
|
||||
);
|
||||
@@ -153,12 +153,10 @@ public class BountyCommand {
|
||||
player.server
|
||||
.getPlayerList()
|
||||
.broadcastSystemMessage(
|
||||
Component.literal(
|
||||
"[Bounty] " +
|
||||
player.getName().getString() +
|
||||
" has put a bounty on " +
|
||||
target.getName().getString() +
|
||||
"!"
|
||||
Component.translatable(
|
||||
"command.tiedup.bounty.broadcast",
|
||||
player.getName().getString(),
|
||||
target.getName().getString()
|
||||
).withStyle(ChatFormatting.GOLD),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -117,9 +117,9 @@ public class CaptivityDebugCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"=== Captivity Debug Info ===\n" + debugInfo
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
Component.translatable(
|
||||
"command.tiedup.debug.prisoner_header"
|
||||
).append(Component.literal("\n" + debugInfo)).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -128,7 +128,7 @@ public class CaptivityDebugCommand {
|
||||
ctx
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("Error: " + e.getMessage()).withStyle(
|
||||
Component.translatable("command.tiedup.debug.error", e.getMessage()).withStyle(
|
||||
ChatFormatting.RED
|
||||
)
|
||||
);
|
||||
@@ -149,8 +149,8 @@ public class CaptivityDebugCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Checking captivity system..."
|
||||
Component.translatable(
|
||||
"command.tiedup.debug.validate_checking"
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
true
|
||||
);
|
||||
@@ -163,9 +163,7 @@ public class CaptivityDebugCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(debugInfo).withStyle(
|
||||
ChatFormatting.GREEN
|
||||
),
|
||||
Component.literal(debugInfo).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
|
||||
@@ -174,8 +172,8 @@ public class CaptivityDebugCommand {
|
||||
ctx
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
"Error during validation: " + e.getMessage()
|
||||
Component.translatable(
|
||||
"command.tiedup.debug.error", e.getMessage()
|
||||
).withStyle(ChatFormatting.RED)
|
||||
);
|
||||
return 0; // Failure
|
||||
@@ -193,8 +191,8 @@ public class CaptivityDebugCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Repair functionality has been simplified with the new PrisonerManager system."
|
||||
Component.translatable(
|
||||
"command.tiedup.debug.repair_simplified"
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
true
|
||||
);
|
||||
@@ -203,8 +201,8 @@ public class CaptivityDebugCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"The new system maintains consistency automatically."
|
||||
Component.translatable(
|
||||
"command.tiedup.debug.repair_auto"
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
@@ -214,7 +212,7 @@ public class CaptivityDebugCommand {
|
||||
ctx
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("Error: " + e.getMessage()).withStyle(
|
||||
Component.translatable("command.tiedup.debug.error", e.getMessage()).withStyle(
|
||||
ChatFormatting.RED
|
||||
)
|
||||
);
|
||||
@@ -251,8 +249,8 @@ public class CaptivityDebugCommand {
|
||||
ctx
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
"No camp found with ID prefix: " + campIdPrefix
|
||||
Component.translatable(
|
||||
"command.tiedup.debug.camp_not_found", campIdPrefix
|
||||
).withStyle(ChatFormatting.RED)
|
||||
);
|
||||
return 0;
|
||||
@@ -322,7 +320,7 @@ public class CaptivityDebugCommand {
|
||||
ctx
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("Error: " + e.getMessage()).withStyle(
|
||||
Component.translatable("command.tiedup.debug.error", e.getMessage()).withStyle(
|
||||
ChatFormatting.RED
|
||||
)
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.tiedup.remake.items.ItemAdminWand;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
@@ -110,7 +111,7 @@ public class CellCommand {
|
||||
|
||||
// Must be a player
|
||||
if (!(source.getEntity() instanceof ServerPlayer player)) {
|
||||
source.sendFailure(Component.literal("Must be a player"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.error.must_be_player"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -121,9 +122,7 @@ public class CellCommand {
|
||||
UUID selectedCellId = getSelectedCellFromWand(player);
|
||||
if (selectedCellId == null) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
"No cell selected. Use the Admin Wand on a Cell Core first."
|
||||
)
|
||||
Component.translatable("command.tiedup.cell.no_selection")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -131,7 +130,7 @@ public class CellCommand {
|
||||
CellDataV2 cell = registry.getCell(selectedCellId);
|
||||
if (cell == null) {
|
||||
source.sendFailure(
|
||||
Component.literal("Selected cell no longer exists")
|
||||
Component.translatable("command.tiedup.cell.no_longer_exists")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -142,7 +141,7 @@ public class CellCommand {
|
||||
existingCell != null && !existingCell.getId().equals(selectedCellId)
|
||||
) {
|
||||
source.sendFailure(
|
||||
Component.literal("Cell name '" + name + "' already exists")
|
||||
Component.translatable("command.tiedup.cell.name_exists", name)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -161,9 +160,7 @@ public class CellCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Named cell '" + name + "' and linked to you"
|
||||
),
|
||||
Component.translatable("command.tiedup.cell.named", name).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -182,20 +179,20 @@ public class CellCommand {
|
||||
Collection<CellDataV2> cells = registry.getAllCells();
|
||||
if (cells.isEmpty()) {
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("No cells registered"),
|
||||
() -> Component.translatable("command.tiedup.cell.none_registered"),
|
||||
false
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("=== Cells (" + cells.size() + ") ==="),
|
||||
() -> Component.translatable("command.tiedup.cell.list_header", cells.size()).withStyle(ChatFormatting.GOLD),
|
||||
false
|
||||
);
|
||||
|
||||
for (CellDataV2 cell : cells) {
|
||||
String info = formatCellInfo(cell, serverLevel);
|
||||
source.sendSuccess(() -> Component.literal(info), false);
|
||||
source.sendSuccess(() -> Component.literal(info).withStyle(ChatFormatting.GRAY), false);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -217,9 +214,7 @@ public class CellCommand {
|
||||
if (cells.isEmpty()) {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
owner.getName().getString() + " has no cells"
|
||||
),
|
||||
Component.translatable("command.tiedup.cell.no_cells_for_owner", owner.getName().getString()),
|
||||
false
|
||||
);
|
||||
return 1;
|
||||
@@ -227,19 +222,17 @@ public class CellCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"=== Cells owned by " +
|
||||
owner.getName().getString() +
|
||||
" (" +
|
||||
cells.size() +
|
||||
") ==="
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.cell.list_owner_header",
|
||||
owner.getName().getString(),
|
||||
cells.size()
|
||||
).withStyle(ChatFormatting.GOLD),
|
||||
false
|
||||
);
|
||||
|
||||
for (CellDataV2 cell : cells) {
|
||||
String info = formatCellInfo(cell, serverLevel);
|
||||
source.sendSuccess(() -> Component.literal(info), false);
|
||||
source.sendSuccess(() -> Component.literal(info).withStyle(ChatFormatting.GRAY), false);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -257,7 +250,7 @@ public class CellCommand {
|
||||
|
||||
// Must be a player
|
||||
if (!(source.getEntity() instanceof ServerPlayer player)) {
|
||||
source.sendFailure(Component.literal("Must be a player"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.error.must_be_player"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -267,9 +260,7 @@ public class CellCommand {
|
||||
UUID selectedCellId = getSelectedCellFromWand(player);
|
||||
if (selectedCellId == null) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
"No cell selected. Use the Admin Wand on a Cell Core first."
|
||||
)
|
||||
Component.translatable("command.tiedup.cell.no_selection")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -277,7 +268,7 @@ public class CellCommand {
|
||||
CellDataV2 cell = registry.getCell(selectedCellId);
|
||||
if (cell == null) {
|
||||
source.sendFailure(
|
||||
Component.literal("Selected cell no longer exists")
|
||||
Component.translatable("command.tiedup.cell.no_longer_exists")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -300,7 +291,7 @@ public class CellCommand {
|
||||
CellDataV2 cell = registry.getCellByName(name);
|
||||
if (cell == null) {
|
||||
source.sendFailure(
|
||||
Component.literal("Cell '" + name + "' not found")
|
||||
Component.translatable("command.tiedup.cell.not_found", name)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -321,7 +312,7 @@ public class CellCommand {
|
||||
|
||||
// Must be a player
|
||||
if (!(source.getEntity() instanceof ServerPlayer player)) {
|
||||
source.sendFailure(Component.literal("Must be a player"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.error.must_be_player"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -331,9 +322,7 @@ public class CellCommand {
|
||||
UUID selectedCellId = getSelectedCellFromWand(player);
|
||||
if (selectedCellId == null) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
"No cell selected. Use the Admin Wand on a Cell Core first."
|
||||
)
|
||||
Component.translatable("command.tiedup.cell.no_selection")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -341,7 +330,7 @@ public class CellCommand {
|
||||
CellDataV2 cell = registry.getCell(selectedCellId);
|
||||
if (cell == null) {
|
||||
source.sendFailure(
|
||||
Component.literal("Selected cell no longer exists")
|
||||
Component.translatable("command.tiedup.cell.no_longer_exists")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -365,7 +354,7 @@ public class CellCommand {
|
||||
}
|
||||
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("Deleted cell '" + cellName + "'"),
|
||||
() -> Component.translatable("command.tiedup.cell.deleted", cellName).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -438,24 +427,16 @@ public class CellCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Reset " +
|
||||
finalResetCount +
|
||||
" spawn markers (found " +
|
||||
finalSpawnMarkerCount +
|
||||
" total spawn markers in " +
|
||||
radius +
|
||||
" block radius)"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.cell.reset_spawns", finalResetCount, finalSpawnMarkerCount, radius
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
|
||||
if (resetCount > 0) {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"You can now save the structure - NPCs will spawn when it's placed."
|
||||
),
|
||||
Component.translatable("command.tiedup.cell.reset_spawns_hint").withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -541,25 +522,25 @@ public class CellCommand {
|
||||
String nameDisplay =
|
||||
cell.getName() != null ? cell.getName() : "(unnamed)";
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("=== Cell: " + nameDisplay + " ==="),
|
||||
() -> Component.translatable("command.tiedup.cell.info_header", nameDisplay).withStyle(ChatFormatting.GOLD),
|
||||
false
|
||||
);
|
||||
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("ID: " + cell.getId().toString()),
|
||||
() -> Component.translatable("command.tiedup.cell.info_id", cell.getId().toString()).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("State: " + cell.getState()),
|
||||
() -> Component.translatable("command.tiedup.cell.info_state", cell.getState().toString()).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Core Position: " + cell.getCorePos().toShortString()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.cell.info_core_pos", cell.getCorePos().toShortString()
|
||||
).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -567,9 +548,9 @@ public class CellCommand {
|
||||
if (cell.getSpawnPoint() != null) {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Spawn Point: " + cell.getSpawnPoint().toShortString()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.cell.info_spawn_point", cell.getSpawnPoint().toShortString()
|
||||
).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -584,18 +565,16 @@ public class CellCommand {
|
||||
owner != null ? owner.getName().getString() : "(offline)";
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Owner: " +
|
||||
ownerName +
|
||||
" (" +
|
||||
cell.getOwnerId().toString().substring(0, 8) +
|
||||
"...)"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.cell.info_owner",
|
||||
ownerName,
|
||||
cell.getOwnerId().toString().substring(0, 8) + "..."
|
||||
).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
} else {
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("Owner: (world-generated)"),
|
||||
() -> Component.translatable("command.tiedup.cell.info_owner_world").withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -603,16 +582,16 @@ public class CellCommand {
|
||||
// Geometry
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Interior blocks: " + cell.getInteriorBlocks().size()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.cell.info_interior", cell.getInteriorBlocks().size()
|
||||
).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Wall blocks: " + cell.getWallBlocks().size()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.cell.info_walls", cell.getWallBlocks().size()
|
||||
).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -620,16 +599,11 @@ public class CellCommand {
|
||||
if (!cell.getBreachedPositions().isEmpty()) {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Breaches: " +
|
||||
cell.getBreachedPositions().size() +
|
||||
" (" +
|
||||
String.format(
|
||||
"%.1f",
|
||||
cell.getBreachPercentage() * 100
|
||||
) +
|
||||
"%)"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.cell.info_breaches",
|
||||
cell.getBreachedPositions().size(),
|
||||
String.format("%.1f", cell.getBreachPercentage() * 100)
|
||||
).withStyle(ChatFormatting.RED),
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -637,19 +611,19 @@ public class CellCommand {
|
||||
// Features
|
||||
if (!cell.getBeds().isEmpty()) {
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("Beds: " + cell.getBeds().size()),
|
||||
() -> Component.translatable("command.tiedup.cell.info_beds", cell.getBeds().size()).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
}
|
||||
if (!cell.getAnchors().isEmpty()) {
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("Anchors: " + cell.getAnchors().size()),
|
||||
() -> Component.translatable("command.tiedup.cell.info_anchors", cell.getAnchors().size()).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
}
|
||||
if (!cell.getDoors().isEmpty()) {
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("Doors: " + cell.getDoors().size()),
|
||||
() -> Component.translatable("command.tiedup.cell.info_doors", cell.getDoors().size()).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -657,9 +631,9 @@ public class CellCommand {
|
||||
// Prisoners
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Prisoners: " + cell.getPrisonerCount() + "/4"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.cell.info_prisoners", cell.getPrisonerCount()
|
||||
).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
for (UUID prisonerId : cell.getPrisonerIds()) {
|
||||
@@ -670,7 +644,7 @@ public class CellCommand {
|
||||
String prisonerName =
|
||||
prisoner != null ? prisoner.getName().getString() : "(offline)";
|
||||
source.sendSuccess(
|
||||
() -> Component.literal(" - " + prisonerName),
|
||||
() -> Component.literal(" - " + prisonerName).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.tiedup.remake.v2.bondage.CollarHelper;
|
||||
import com.tiedup.remake.util.teleport.Position;
|
||||
import com.tiedup.remake.util.teleport.TeleportHelper;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
@@ -152,9 +153,7 @@ public class CollarCommand {
|
||||
ItemStack collar = getPlayerCollar(target);
|
||||
if (collar.isEmpty()) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " does not have a collar"
|
||||
)
|
||||
Component.translatable("command.tiedup.collar_cmd.no_collar", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -164,18 +163,16 @@ public class CollarCommand {
|
||||
CollarHelper.addOwner(collar, executor);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aClaimed " +
|
||||
target.getName().getString() +
|
||||
"'s collar"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar_cmd.claimed", target.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
source.sendFailure(Component.literal("Failed to claim collar"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.collar_cmd.claim_failed"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -187,9 +184,7 @@ public class CollarCommand {
|
||||
ItemStack collar = getPlayerCollar(target);
|
||||
if (collar.isEmpty()) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " does not have a collar"
|
||||
)
|
||||
Component.translatable("command.tiedup.collar_cmd.no_collar", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -199,18 +194,16 @@ public class CollarCommand {
|
||||
CollarHelper.removeOwner(collar, executor.getUUID());
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aRemoved your ownership from " +
|
||||
target.getName().getString() +
|
||||
"'s collar"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar_cmd.unclaimed", target.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
source.sendFailure(Component.literal("Failed to unclaim collar"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.collar_cmd.unclaim_failed"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -223,9 +216,7 @@ public class CollarCommand {
|
||||
ItemStack collar = getPlayerCollar(target);
|
||||
if (collar.isEmpty()) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " does not have a collar"
|
||||
)
|
||||
Component.translatable("command.tiedup.collar_cmd.no_collar", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -234,9 +225,9 @@ public class CollarCommand {
|
||||
CollarHelper.setNickname(collar, name);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aSet collar nickname to '" + name + "'"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar_cmd.renamed", name
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -254,9 +245,7 @@ public class CollarCommand {
|
||||
ItemStack collar = getPlayerCollar(target);
|
||||
if (collar.isEmpty()) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " does not have a collar"
|
||||
)
|
||||
Component.translatable("command.tiedup.collar_cmd.no_collar", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -265,13 +254,11 @@ public class CollarCommand {
|
||||
CollarHelper.addOwner(collar, owner);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aAdded " +
|
||||
owner.getName().getString() +
|
||||
" as owner of " +
|
||||
target.getName().getString() +
|
||||
"'s collar"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar_cmd.owner_added",
|
||||
owner.getName().getString(),
|
||||
target.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -289,9 +276,7 @@ public class CollarCommand {
|
||||
ItemStack collar = getPlayerCollar(target);
|
||||
if (collar.isEmpty()) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " does not have a collar"
|
||||
)
|
||||
Component.translatable("command.tiedup.collar_cmd.no_collar", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -300,13 +285,11 @@ public class CollarCommand {
|
||||
CollarHelper.removeOwner(collar, owner.getUUID());
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aRemoved " +
|
||||
owner.getName().getString() +
|
||||
" as owner of " +
|
||||
target.getName().getString() +
|
||||
"'s collar"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar_cmd.owner_removed",
|
||||
owner.getName().getString(),
|
||||
target.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -329,9 +312,7 @@ public class CollarCommand {
|
||||
ItemStack collar = getPlayerCollar(target);
|
||||
if (collar.isEmpty()) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " does not have a collar"
|
||||
)
|
||||
Component.translatable("command.tiedup.collar_cmd.no_collar", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -343,7 +324,7 @@ public class CollarCommand {
|
||||
|
||||
if (cell == null) {
|
||||
source.sendFailure(
|
||||
Component.literal("Cell '" + cellName + "' not found")
|
||||
Component.translatable("command.tiedup.collar_cmd.cell_not_found", cellName)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -352,13 +333,11 @@ public class CollarCommand {
|
||||
CollarHelper.setCellId(collar, cell.getId());
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aAssigned cell '" +
|
||||
cellName +
|
||||
"' to " +
|
||||
target.getName().getString() +
|
||||
"'s collar"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar_cmd.cell_assigned",
|
||||
cellName,
|
||||
target.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -381,9 +360,7 @@ public class CollarCommand {
|
||||
ItemStack collar = getPlayerCollar(target);
|
||||
if (collar.isEmpty()) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " does not have a collar"
|
||||
)
|
||||
Component.translatable("command.tiedup.collar_cmd.no_collar", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -391,7 +368,7 @@ public class CollarCommand {
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
if (!CollarHelper.hasCellAssigned(collar)) {
|
||||
source.sendFailure(
|
||||
Component.literal("No cell assigned to collar")
|
||||
Component.translatable("command.tiedup.collar_cmd.no_cell_assigned")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -403,7 +380,7 @@ public class CollarCommand {
|
||||
|
||||
if (cell == null) {
|
||||
source.sendFailure(
|
||||
Component.literal("Assigned cell no longer exists")
|
||||
Component.translatable("command.tiedup.collar_cmd.cell_deleted")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -420,12 +397,11 @@ public class CollarCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aTeleported " +
|
||||
target.getName().getString() +
|
||||
" to cell at " +
|
||||
cell.getCorePos().toShortString()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar_cmd.teleported",
|
||||
target.getName().getString(),
|
||||
cell.getCorePos().toShortString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -442,9 +418,7 @@ public class CollarCommand {
|
||||
ItemStack collar = getPlayerCollar(target);
|
||||
if (collar.isEmpty()) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " does not have a collar"
|
||||
)
|
||||
Component.translatable("command.tiedup.collar_cmd.no_collar", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -452,28 +426,23 @@ public class CollarCommand {
|
||||
if (CollarHelper.isCollar(collar)) {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§6=== Collar Info for " +
|
||||
target.getName().getString() +
|
||||
" ==="
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar_cmd.info_header",
|
||||
target.getName().getString()
|
||||
).withStyle(ChatFormatting.GOLD),
|
||||
false
|
||||
);
|
||||
|
||||
String nickname = CollarHelper.getNickname(collar);
|
||||
String nicknameDisplay = (nickname == null || nickname.isEmpty()) ? "None" : nickname;
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§7Nickname: §f" +
|
||||
(nickname == null || nickname.isEmpty() ? "None" : nickname)
|
||||
),
|
||||
Component.translatable("command.tiedup.collar_cmd.info_nickname", nicknameDisplay).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§7Has Owner: §f" + CollarHelper.hasOwner(collar)
|
||||
),
|
||||
Component.translatable("command.tiedup.collar_cmd.info_has_owner", String.valueOf(CollarHelper.hasOwner(collar))).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
// Cell assignment
|
||||
@@ -489,23 +458,22 @@ public class CollarCommand {
|
||||
: cellId.toString().substring(0, 8) + "...";
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§7Assigned Cell: §a" +
|
||||
cellDisplay +
|
||||
" §7@ " +
|
||||
cell.getCorePos().toShortString()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar_cmd.info_cell",
|
||||
cellDisplay,
|
||||
cell.getCorePos().toShortString()
|
||||
).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
} else {
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("§7Assigned Cell: §c(deleted)"),
|
||||
() -> Component.translatable("command.tiedup.collar_cmd.info_cell_deleted").withStyle(ChatFormatting.RED),
|
||||
false
|
||||
);
|
||||
}
|
||||
} else {
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("§7Assigned Cell: §fNone"),
|
||||
() -> Component.translatable("command.tiedup.collar_cmd.info_cell_none").withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -514,9 +482,9 @@ public class CollarCommand {
|
||||
&& lockable.isLocked(collar);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§7Locked: §f" + locked
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar_cmd.info_locked", String.valueOf(locked)
|
||||
).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ public final class CommandHelper {
|
||||
if (source.getEntity() instanceof ServerPlayer player) {
|
||||
return Optional.of(player);
|
||||
}
|
||||
source.sendFailure(Component.literal("Must be a player"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.error.must_be_player"));
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
@@ -89,7 +90,7 @@ public class KeyCommand {
|
||||
|
||||
ItemStack key = getHeldKey(player);
|
||||
if (key.isEmpty()) {
|
||||
source.sendFailure(Component.literal("You must hold a collar key"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.key.must_hold_key"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -101,7 +102,7 @@ public class KeyCommand {
|
||||
!tag.getUUID(TAG_OWNER).equals(player.getUUID())
|
||||
) {
|
||||
source.sendFailure(
|
||||
Component.literal("This key is already claimed by someone else")
|
||||
Component.translatable("command.tiedup.key.already_claimed")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -110,7 +111,7 @@ public class KeyCommand {
|
||||
tag.putString(TAG_OWNER_NAME, player.getName().getString());
|
||||
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("§aYou have claimed this key"),
|
||||
() -> Component.translatable("command.tiedup.key.claimed").withStyle(ChatFormatting.GREEN),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -129,19 +130,19 @@ public class KeyCommand {
|
||||
|
||||
ItemStack key = getHeldKey(player);
|
||||
if (key.isEmpty()) {
|
||||
source.sendFailure(Component.literal("You must hold a collar key"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.key.must_hold_key"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CompoundTag tag = key.getOrCreateTag();
|
||||
|
||||
if (!tag.hasUUID(TAG_OWNER)) {
|
||||
source.sendFailure(Component.literal("This key is not claimed"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.key.not_claimed"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!tag.getUUID(TAG_OWNER).equals(player.getUUID())) {
|
||||
source.sendFailure(Component.literal("You do not own this key"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.key.not_owner"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -149,7 +150,7 @@ public class KeyCommand {
|
||||
tag.remove(TAG_OWNER_NAME);
|
||||
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("§aYou have unclaimed this key"),
|
||||
() -> Component.translatable("command.tiedup.key.unclaimed").withStyle(ChatFormatting.GREEN),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -170,7 +171,7 @@ public class KeyCommand {
|
||||
|
||||
ItemStack key = getHeldKey(player);
|
||||
if (key.isEmpty()) {
|
||||
source.sendFailure(Component.literal("You must hold a collar key"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.key.must_hold_key"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -181,7 +182,7 @@ public class KeyCommand {
|
||||
tag.hasUUID(TAG_OWNER) &&
|
||||
!tag.getUUID(TAG_OWNER).equals(player.getUUID())
|
||||
) {
|
||||
source.sendFailure(Component.literal("You do not own this key"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.key.not_owner"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -190,9 +191,9 @@ public class KeyCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aAssigned key to " + target.getName().getString()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.key.assigned", target.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -211,7 +212,7 @@ public class KeyCommand {
|
||||
|
||||
ItemStack key = getHeldKey(player);
|
||||
if (key.isEmpty()) {
|
||||
source.sendFailure(Component.literal("You must hold a collar key"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.key.must_hold_key"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -222,7 +223,7 @@ public class KeyCommand {
|
||||
tag.hasUUID(TAG_OWNER) &&
|
||||
!tag.getUUID(TAG_OWNER).equals(player.getUUID())
|
||||
) {
|
||||
source.sendFailure(Component.literal("You do not own this key"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.key.not_owner"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -231,9 +232,9 @@ public class KeyCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aKey is now " + (isPublic ? "public" : "private")
|
||||
),
|
||||
Component.translatable(
|
||||
isPublic ? "command.tiedup.key.now_public" : "command.tiedup.key.now_private"
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -252,40 +253,40 @@ public class KeyCommand {
|
||||
|
||||
ItemStack key = getHeldKey(player);
|
||||
if (key.isEmpty()) {
|
||||
source.sendFailure(Component.literal("You must hold a collar key"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.key.must_hold_key"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
CompoundTag tag = key.getOrCreateTag();
|
||||
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("§6=== Key Info ==="),
|
||||
() -> Component.translatable("command.tiedup.key.info_header").withStyle(ChatFormatting.GOLD),
|
||||
false
|
||||
);
|
||||
|
||||
String ownerName = tag.getString(TAG_OWNER_NAME);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§7Owner: §f" +
|
||||
(ownerName.isEmpty() ? "Not claimed" : ownerName)
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.key.info_owner",
|
||||
ownerName.isEmpty() ? "Not claimed" : ownerName
|
||||
).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
|
||||
String targetName = tag.getString(TAG_TARGET_NAME);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§7Assigned to: §f" +
|
||||
(targetName.isEmpty() ? "Not assigned" : targetName)
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.key.info_assigned",
|
||||
targetName.isEmpty() ? "Not assigned" : targetName
|
||||
).withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
|
||||
boolean isPublic = tag.getBoolean(TAG_PUBLIC);
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("§7Public: §f" + (isPublic ? "Yes" : "No")),
|
||||
() -> Component.translatable("command.tiedup.key.info_public", isPublic ? "Yes" : "No").withStyle(ChatFormatting.GRAY),
|
||||
false
|
||||
);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.tiedup.remake.items.base.KnifeVariant;
|
||||
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -147,9 +148,9 @@ public class KidnapSetCommand {
|
||||
int finalGiven = given;
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aGave kidnap set (" + finalGiven + " item stacks)"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.kidnapset.gave", finalGiven
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
|
||||
@@ -205,13 +206,11 @@ public class KidnapSetCommand {
|
||||
int finalReloaded = reloaded;
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aReloaded " +
|
||||
(type.equals("all") ? "all data files" : type) +
|
||||
" (" +
|
||||
finalReloaded +
|
||||
" files)"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.kidnapreload.reloaded",
|
||||
type.equals("all") ? "all data files" : type,
|
||||
finalReloaded
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
@@ -199,7 +200,7 @@ public class NPCCommand {
|
||||
z = player.getZ();
|
||||
} else {
|
||||
source.sendFailure(
|
||||
Component.literal("Must specify a player or be a player")
|
||||
Component.translatable("command.tiedup.error.must_be_player")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -219,15 +220,15 @@ public class NPCCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aSpawned Kidnapper at " + formatPos(x, y, z)
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.npc.spawned_kidnapper", formatPos(x, y, z)
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
source.sendFailure(Component.literal("Failed to spawn Kidnapper"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.npc.spawn_failed_kidnapper"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -245,11 +246,7 @@ public class NPCCommand {
|
||||
);
|
||||
if (variant == null) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
"Unknown elite variant: " +
|
||||
name +
|
||||
". Available: suki, carol, athena, evelyn"
|
||||
)
|
||||
Component.translatable("command.tiedup.npc.unknown_variant", name)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -266,7 +263,7 @@ public class NPCCommand {
|
||||
z = player.getZ();
|
||||
} else {
|
||||
source.sendFailure(
|
||||
Component.literal("Must specify a player or be a player")
|
||||
Component.translatable("command.tiedup.error.must_be_player")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -289,19 +286,16 @@ public class NPCCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aSpawned Elite Kidnapper '" +
|
||||
variant.defaultName() +
|
||||
"' at " +
|
||||
formatPos(x, y, z)
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.npc.spawned_elite", variant.defaultName(), formatPos(x, y, z)
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
source.sendFailure(
|
||||
Component.literal("Failed to spawn Elite Kidnapper")
|
||||
Component.translatable("command.tiedup.npc.spawn_failed_elite")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -325,7 +319,7 @@ public class NPCCommand {
|
||||
z = player.getZ();
|
||||
} else {
|
||||
source.sendFailure(
|
||||
Component.literal("Must specify a player or be a player")
|
||||
Component.translatable("command.tiedup.error.must_be_player")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -346,16 +340,16 @@ public class NPCCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aSpawned Archer Kidnapper at " + formatPos(x, y, z)
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.npc.spawned_archer", formatPos(x, y, z)
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
source.sendFailure(
|
||||
Component.literal("Failed to spawn Archer Kidnapper")
|
||||
Component.translatable("command.tiedup.npc.spawn_failed_archer")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -379,7 +373,7 @@ public class NPCCommand {
|
||||
z = player.getZ();
|
||||
} else {
|
||||
source.sendFailure(
|
||||
Component.literal("Must specify a player or be a player")
|
||||
Component.translatable("command.tiedup.error.must_be_player")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -399,15 +393,15 @@ public class NPCCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aSpawned Damsel at " + formatPos(x, y, z)
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.npc.spawned_damsel", formatPos(x, y, z)
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
source.sendFailure(Component.literal("Failed to spawn Damsel"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.npc.spawn_failed_damsel"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -441,9 +435,9 @@ public class NPCCommand {
|
||||
int finalKilled = killed;
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aKilled " + finalKilled + " mod NPCs in radius " + radius
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.npc.killed", finalKilled, radius
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
|
||||
@@ -491,7 +485,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("No mod NPC found within 10 blocks")
|
||||
Component.translatable("command.tiedup.npc.no_npc_nearby")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -499,7 +493,7 @@ public class NPCCommand {
|
||||
if (npc.isTiedUp()) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("NPC is already tied up"));
|
||||
.sendFailure(Component.translatable("command.tiedup.npc.already_tied"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -510,7 +504,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() -> Component.literal("§aTied up " + npc.getKidnappedName()),
|
||||
() -> Component.translatable("command.tiedup.npc.tied", npc.getKidnappedName()).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -522,7 +516,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("No mod NPC found within 10 blocks")
|
||||
Component.translatable("command.tiedup.npc.no_npc_nearby")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -530,7 +524,7 @@ public class NPCCommand {
|
||||
if (npc.isGagged()) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("NPC is already gagged"));
|
||||
.sendFailure(Component.translatable("command.tiedup.npc.already_gagged"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -541,7 +535,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() -> Component.literal("§aGagged " + npc.getKidnappedName()),
|
||||
() -> Component.translatable("command.tiedup.npc.gagged", npc.getKidnappedName()).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -555,7 +549,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("No mod NPC found within 10 blocks")
|
||||
Component.translatable("command.tiedup.npc.no_npc_nearby")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -563,7 +557,7 @@ public class NPCCommand {
|
||||
if (npc.isBlindfolded()) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("NPC is already blindfolded"));
|
||||
.sendFailure(Component.translatable("command.tiedup.npc.already_blindfolded"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -575,9 +569,8 @@ public class NPCCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aBlindfolded " + npc.getKidnappedName()
|
||||
),
|
||||
Component.translatable("command.tiedup.npc.blindfolded", npc.getKidnappedName()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -589,7 +582,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("No mod NPC found within 10 blocks")
|
||||
Component.translatable("command.tiedup.npc.no_npc_nearby")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -597,7 +590,7 @@ public class NPCCommand {
|
||||
if (npc.hasCollar()) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("NPC already has a collar"));
|
||||
.sendFailure(Component.translatable("command.tiedup.npc.already_collared"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -608,7 +601,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() -> Component.literal("§aCollared " + npc.getKidnappedName()),
|
||||
() -> Component.translatable("command.tiedup.npc.collared", npc.getKidnappedName()).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -620,7 +613,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("No mod NPC found within 10 blocks")
|
||||
Component.translatable("command.tiedup.npc.no_npc_nearby")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -629,7 +622,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() -> Component.literal("§aUntied " + npc.getKidnappedName()),
|
||||
() -> Component.translatable("command.tiedup.npc.untied", npc.getKidnappedName()).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -643,7 +636,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("No mod NPC found within 10 blocks")
|
||||
Component.translatable("command.tiedup.npc.no_npc_nearby")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -667,9 +660,9 @@ public class NPCCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aFully restrained " + npc.getKidnappedName()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.npc.fully_restrained", npc.getKidnappedName()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -683,7 +676,7 @@ public class NPCCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("No mod NPC found within 10 blocks")
|
||||
Component.translatable("command.tiedup.npc.no_npc_nearby")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -692,68 +685,67 @@ public class NPCCommand {
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§6=== NPC State: " + npc.getKidnappedName() + " ==="
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.npc.state_header", npc.getKidnappedName()
|
||||
).withStyle(ChatFormatting.GOLD),
|
||||
false
|
||||
);
|
||||
|
||||
if (npc instanceof EntityDamsel damsel) {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal("§eVariant: §f" + damsel.getVariantId()),
|
||||
Component.translatable("command.tiedup.npc.state_variant", damsel.getVariantId()).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§eSlim Arms: " +
|
||||
(damsel.hasSlimArms() ? "§aYes" : "§7No")
|
||||
),
|
||||
Component.translatable("command.tiedup.npc.state_slim_arms",
|
||||
Component.translatable(damsel.hasSlimArms() ? "command.tiedup.yes" : "command.tiedup.no")
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§eTied Up: " + (npc.isTiedUp() ? "§aYes" : "§7No")
|
||||
),
|
||||
Component.translatable("command.tiedup.npc.state_tied",
|
||||
Component.translatable(npc.isTiedUp() ? "command.tiedup.yes" : "command.tiedup.no")
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§eGagged: " + (npc.isGagged() ? "§aYes" : "§7No")
|
||||
),
|
||||
Component.translatable("command.tiedup.npc.state_gagged",
|
||||
Component.translatable(npc.isGagged() ? "command.tiedup.yes" : "command.tiedup.no")
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§eBlindfolded: " + (npc.isBlindfolded() ? "§aYes" : "§7No")
|
||||
),
|
||||
Component.translatable("command.tiedup.npc.state_blindfolded",
|
||||
Component.translatable(npc.isBlindfolded() ? "command.tiedup.yes" : "command.tiedup.no")
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§eHas Collar: " + (npc.hasCollar() ? "§aYes" : "§7No")
|
||||
),
|
||||
Component.translatable("command.tiedup.npc.state_collar",
|
||||
Component.translatable(npc.hasCollar() ? "command.tiedup.yes" : "command.tiedup.no")
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§eHas Earplugs: " + (npc.hasEarplugs() ? "§aYes" : "§7No")
|
||||
),
|
||||
Component.translatable("command.tiedup.npc.state_earplugs",
|
||||
Component.translatable(npc.hasEarplugs() ? "command.tiedup.yes" : "command.tiedup.no")
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§eIs Captive: " + (npc.isCaptive() ? "§aYes" : "§7No")
|
||||
),
|
||||
Component.translatable("command.tiedup.npc.state_captive",
|
||||
Component.translatable(npc.isCaptive() ? "command.tiedup.yes" : "command.tiedup.no")
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ public class SocialCommand {
|
||||
ServerPlayer target = EntityArgument.getPlayer(context, "player");
|
||||
|
||||
if (player.getUUID().equals(target.getUUID())) {
|
||||
source.sendFailure(Component.literal("You cannot block yourself"));
|
||||
source.sendFailure(Component.translatable("command.tiedup.social.cannot_block_self"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -170,9 +170,7 @@ public class SocialCommand {
|
||||
|
||||
if (data.isBlocked(player.getUUID(), target.getUUID())) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " is already blocked"
|
||||
)
|
||||
Component.translatable("command.tiedup.social.already_blocked", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -180,7 +178,7 @@ public class SocialCommand {
|
||||
data.addBlock(player.getUUID(), target.getUUID());
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal("§aBlocked " + target.getName().getString()),
|
||||
Component.translatable("command.tiedup.social.blocked", target.getName().getString()).withStyle(ChatFormatting.GREEN),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -208,9 +206,7 @@ public class SocialCommand {
|
||||
|
||||
if (!data.isBlocked(player.getUUID(), target.getUUID())) {
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " is not blocked"
|
||||
)
|
||||
Component.translatable("command.tiedup.social.not_blocked", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -218,9 +214,7 @@ public class SocialCommand {
|
||||
data.removeBlock(player.getUUID(), target.getUUID());
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aUnblocked " + target.getName().getString()
|
||||
),
|
||||
Component.translatable("command.tiedup.social.unblocked", target.getName().getString()).withStyle(ChatFormatting.GREEN),
|
||||
false
|
||||
);
|
||||
|
||||
@@ -245,19 +239,13 @@ public class SocialCommand {
|
||||
if (blocked) {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§c" + target.getName().getString() + " has blocked you"
|
||||
),
|
||||
Component.translatable("command.tiedup.social.has_blocked_you", target.getName().getString()).withStyle(ChatFormatting.RED),
|
||||
false
|
||||
);
|
||||
} else {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§a" +
|
||||
target.getName().getString() +
|
||||
" has not blocked you"
|
||||
),
|
||||
Component.translatable("command.tiedup.social.has_not_blocked_you", target.getName().getString()).withStyle(ChatFormatting.GREEN),
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -300,11 +288,7 @@ public class SocialCommand {
|
||||
if (lastUse != null && now - lastUse < NORP_COOLDOWN_MS) {
|
||||
long remaining = (NORP_COOLDOWN_MS - (now - lastUse)) / 1000;
|
||||
source.sendFailure(
|
||||
Component.literal(
|
||||
"Please wait " +
|
||||
remaining +
|
||||
" seconds before using /norp again"
|
||||
)
|
||||
Component.translatable("command.tiedup.social.norp_cooldown", remaining)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -315,7 +299,7 @@ public class SocialCommand {
|
||||
// Broadcast to all players
|
||||
Component message = Component.literal("")
|
||||
.append(
|
||||
Component.literal("[NoRP] ").withStyle(
|
||||
Component.translatable("command.tiedup.social.norp_prefix").withStyle(
|
||||
ChatFormatting.RED,
|
||||
ChatFormatting.BOLD
|
||||
)
|
||||
@@ -326,8 +310,8 @@ public class SocialCommand {
|
||||
)
|
||||
)
|
||||
.append(
|
||||
Component.literal(
|
||||
" has announced non-consent to current RP"
|
||||
Component.translatable(
|
||||
"command.tiedup.social.norp_announcement"
|
||||
).withStyle(ChatFormatting.RED)
|
||||
);
|
||||
|
||||
@@ -411,7 +395,7 @@ public class SocialCommand {
|
||||
SocialData data = SocialData.get(sender.serverLevel());
|
||||
if (data.isBlocked(target.getUUID(), sender.getUUID())) {
|
||||
source.sendFailure(
|
||||
Component.literal("This player has blocked you")
|
||||
Component.translatable("command.tiedup.social.pm_blocked")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -419,8 +403,8 @@ public class SocialCommand {
|
||||
// Send to target (earplug-aware)
|
||||
Component toTarget = Component.literal("")
|
||||
.append(
|
||||
Component.literal(
|
||||
"[PM from " + sender.getName().getString() + "] "
|
||||
Component.translatable(
|
||||
"command.tiedup.social.pm_from", sender.getName().getString()
|
||||
).withStyle(ChatFormatting.LIGHT_PURPLE)
|
||||
)
|
||||
.append(Component.literal(message).withStyle(ChatFormatting.WHITE));
|
||||
@@ -429,8 +413,8 @@ public class SocialCommand {
|
||||
// Confirm to sender (always show - they're the one sending)
|
||||
Component toSender = Component.literal("")
|
||||
.append(
|
||||
Component.literal(
|
||||
"[PM to " + target.getName().getString() + "] "
|
||||
Component.translatable(
|
||||
"command.tiedup.social.pm_to", target.getName().getString()
|
||||
).withStyle(ChatFormatting.GRAY)
|
||||
)
|
||||
.append(Component.literal(message).withStyle(ChatFormatting.WHITE));
|
||||
@@ -458,15 +442,13 @@ public class SocialCommand {
|
||||
|
||||
if (distance == 0) {
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("§aTalk area disabled (global chat)"),
|
||||
() -> Component.translatable("command.tiedup.social.talkarea_disabled").withStyle(ChatFormatting.GREEN),
|
||||
false
|
||||
);
|
||||
} else {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"§aTalk area set to " + distance + " blocks"
|
||||
),
|
||||
Component.translatable("command.tiedup.social.talkarea_set", distance).withStyle(ChatFormatting.GREEN),
|
||||
false
|
||||
);
|
||||
}
|
||||
@@ -490,12 +472,12 @@ public class SocialCommand {
|
||||
if (talkArea == 0) {
|
||||
source.sendSuccess(
|
||||
() ->
|
||||
Component.literal("Talk area: §edisabled §7(global chat)"),
|
||||
Component.translatable("command.tiedup.social.talkinfo_disabled").withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
} else {
|
||||
source.sendSuccess(
|
||||
() -> Component.literal("Talk area: §e" + talkArea + " blocks"),
|
||||
() -> Component.translatable("command.tiedup.social.talkinfo_distance", talkArea).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ 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.ChatFormatting;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
@@ -111,7 +112,7 @@ public class AccessoryCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -119,9 +120,9 @@ public class AccessoryCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" already has earplugs"
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.already_earplugs",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -135,11 +136,10 @@ public class AccessoryCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
" has been given earplugs"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.earplugs_on",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendToTarget(
|
||||
@@ -159,7 +159,7 @@ public class AccessoryCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -167,9 +167,9 @@ public class AccessoryCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" does not have earplugs"
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.no_earplugs",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -182,11 +182,10 @@ public class AccessoryCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
"'s earplugs have been removed"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.earplugs_removed",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendToPlayer(
|
||||
@@ -206,7 +205,7 @@ public class AccessoryCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -214,9 +213,9 @@ public class AccessoryCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" already has clothes"
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.already_clothes",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -230,11 +229,10 @@ public class AccessoryCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
" has been given clothes"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.clothes_on",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
|
||||
@@ -249,7 +247,7 @@ public class AccessoryCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -257,9 +255,9 @@ public class AccessoryCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" is not wearing clothes"
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.no_clothes",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -276,10 +274,10 @@ public class AccessoryCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Removed clothes from " +
|
||||
targetPlayer.getName().getString()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.clothes_removed",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -293,7 +291,7 @@ public class AccessoryCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -338,9 +336,9 @@ public class AccessoryCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" is already fully restrained"
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.already_restrained",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -353,13 +351,11 @@ public class AccessoryCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
" has been fully restrained (" +
|
||||
finalApplied +
|
||||
" items applied)"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.fully_restrained",
|
||||
targetPlayer.getName().getString(),
|
||||
finalApplied
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendToPlayer(
|
||||
@@ -381,7 +377,7 @@ public class AccessoryCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -416,9 +412,7 @@ public class AccessoryCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
"Invalid type. Use: gag, blindfold, or all"
|
||||
)
|
||||
Component.translatable("command.tiedup.accessory.adjust_invalid_type")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -427,11 +421,10 @@ public class AccessoryCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" has no " +
|
||||
type +
|
||||
" to adjust"
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.nothing_to_adjust",
|
||||
targetPlayer.getName().getString(),
|
||||
type
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -451,15 +444,12 @@ public class AccessoryCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7aAdjusted " +
|
||||
items +
|
||||
" for " +
|
||||
targetPlayer.getName().getString() +
|
||||
" to " +
|
||||
valueStr +
|
||||
" pixels"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.accessory.adjusted",
|
||||
items,
|
||||
targetPlayer.getName().getString(),
|
||||
valueStr
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ 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.ChatFormatting;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
@@ -51,7 +52,7 @@ public class BindCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -59,9 +60,9 @@ public class BindCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" is already tied up"
|
||||
Component.translatable(
|
||||
"command.tiedup.bind.already_tied",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -76,11 +77,10 @@ public class BindCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
" has been tied up"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.bind.tied",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendTiedUp(
|
||||
@@ -99,7 +99,7 @@ public class BindCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -112,9 +112,9 @@ public class BindCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" is not restrained"
|
||||
Component.translatable(
|
||||
"command.tiedup.bind.not_restrained",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -149,11 +149,10 @@ public class BindCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
" has been freed from all restraints"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.bind.freed",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendFreed(targetPlayer);
|
||||
|
||||
@@ -6,6 +6,7 @@ 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.ChatFormatting;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
@@ -51,7 +52,7 @@ public class BlindfoldCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -59,9 +60,9 @@ public class BlindfoldCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" is already blindfolded"
|
||||
Component.translatable(
|
||||
"command.tiedup.blindfold.already_blindfolded",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -76,11 +77,10 @@ public class BlindfoldCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
" has been blindfolded"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.blindfold.blindfolded",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendToTarget(
|
||||
@@ -100,7 +100,7 @@ public class BlindfoldCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -108,9 +108,9 @@ public class BlindfoldCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" is not blindfolded"
|
||||
Component.translatable(
|
||||
"command.tiedup.blindfold.not_blindfolded",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -123,11 +123,10 @@ public class BlindfoldCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
"'s blindfold has been removed"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.blindfold.removed",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendToTarget(
|
||||
|
||||
@@ -9,6 +9,7 @@ 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.ChatFormatting;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
@@ -76,7 +77,7 @@ public class CollarCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -84,9 +85,9 @@ public class CollarCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" already has a collar"
|
||||
Component.translatable(
|
||||
"command.tiedup.collar.already_collared",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -106,11 +107,10 @@ public class CollarCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
" has been collared"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar.collared",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendToTarget(
|
||||
@@ -130,7 +130,7 @@ public class CollarCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -138,9 +138,9 @@ public class CollarCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" does not have a collar"
|
||||
Component.translatable(
|
||||
"command.tiedup.collar.no_collar",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -153,11 +153,10 @@ public class CollarCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
"'s collar has been removed"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar.removed",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendToTarget(
|
||||
@@ -177,7 +176,7 @@ public class CollarCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -222,11 +221,10 @@ public class CollarCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
" has been enslaved"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar.enslaved",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendEnslaved(
|
||||
@@ -245,7 +243,7 @@ public class CollarCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -253,8 +251,9 @@ public class CollarCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() + " is not captured"
|
||||
Component.translatable(
|
||||
"command.tiedup.collar.not_captured",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -273,11 +272,10 @@ public class CollarCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
" has been freed from slavery"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.collar.freed",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendFreed(targetPlayer);
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.tiedup.remake.commands.CommandHelper;
|
||||
import com.tiedup.remake.prison.PrisonerManager;
|
||||
import com.tiedup.remake.prison.RansomRecord;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
@@ -77,9 +78,8 @@ public class DebtSubCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
target.getName().getString() +
|
||||
" has no debt record."
|
||||
Component.translatable(
|
||||
"command.tiedup.debt.no_record", target.getName().getString()
|
||||
),
|
||||
false
|
||||
);
|
||||
@@ -94,16 +94,11 @@ public class DebtSubCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
target.getName().getString() +
|
||||
" \u2014 Debt: " +
|
||||
total +
|
||||
" | Paid: " +
|
||||
paid +
|
||||
" | Remaining: " +
|
||||
remaining +
|
||||
" emeralds"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.debt.show",
|
||||
target.getName().getString(),
|
||||
total, paid, remaining
|
||||
).withStyle(ChatFormatting.YELLOW),
|
||||
false
|
||||
);
|
||||
return 1;
|
||||
@@ -121,9 +116,7 @@ public class DebtSubCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " has no debt record."
|
||||
)
|
||||
Component.translatable("command.tiedup.debt.no_record", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -133,13 +126,10 @@ public class DebtSubCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Set " +
|
||||
target.getName().getString() +
|
||||
"'s total debt to " +
|
||||
amount +
|
||||
" emeralds."
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.debt.set",
|
||||
target.getName().getString(), amount
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -157,9 +147,7 @@ public class DebtSubCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " has no debt record."
|
||||
)
|
||||
Component.translatable("command.tiedup.debt.no_record", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -169,14 +157,10 @@ public class DebtSubCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Added " +
|
||||
amount +
|
||||
" emeralds to " +
|
||||
target.getName().getString() +
|
||||
"'s debt. Remaining: " +
|
||||
ransom.getRemainingDebt()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.debt.added",
|
||||
amount, target.getName().getString(), ransom.getRemainingDebt()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
@@ -194,9 +178,7 @@ public class DebtSubCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
target.getName().getString() + " has no debt record."
|
||||
)
|
||||
Component.translatable("command.tiedup.debt.no_record", target.getName().getString())
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -207,15 +189,10 @@ public class DebtSubCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Removed " +
|
||||
amount +
|
||||
" emeralds from " +
|
||||
target.getName().getString() +
|
||||
"'s debt. Remaining: " +
|
||||
ransom.getRemainingDebt() +
|
||||
(paid ? " (PAID OFF!)" : "")
|
||||
),
|
||||
Component.translatable(
|
||||
paid ? "command.tiedup.debt.removed_paid" : "command.tiedup.debt.removed",
|
||||
amount, target.getName().getString(), ransom.getRemainingDebt()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
return 1;
|
||||
|
||||
@@ -6,6 +6,7 @@ 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.ChatFormatting;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.tiedup.remake.state.PlayerBindState;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
@@ -51,7 +52,7 @@ public class GagCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -59,9 +60,9 @@ public class GagCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" is already gagged"
|
||||
Component.translatable(
|
||||
"command.tiedup.gag.already_gagged",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -76,11 +77,10 @@ public class GagCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
" has been gagged"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.gag.gagged",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendGagged(
|
||||
@@ -99,7 +99,7 @@ public class GagCommands {
|
||||
if (state == null) {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(Component.literal("Failed to get player state"));
|
||||
.sendFailure(Component.translatable("command.tiedup.error.no_state"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -107,8 +107,9 @@ public class GagCommands {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() + " is not gagged"
|
||||
Component.translatable(
|
||||
"command.tiedup.gag.not_gagged",
|
||||
targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -121,11 +122,10 @@ public class GagCommands {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7a" +
|
||||
targetPlayer.getName().getString() +
|
||||
"'s gag has been removed"
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.gag.removed",
|
||||
targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendToTarget(
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.tiedup.remake.cells.ConfiscatedInventoryRegistry;
|
||||
import com.tiedup.remake.commands.CommandHelper;
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.commands.arguments.EntityArgument;
|
||||
@@ -56,9 +57,8 @@ public class InventorySubCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
targetPlayer.getName().getString() +
|
||||
" has no confiscated inventory to restore"
|
||||
Component.translatable(
|
||||
"command.tiedup.inventory.no_confiscated", targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
@@ -71,10 +71,9 @@ public class InventorySubCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"\u00a7aRestored confiscated inventory to " +
|
||||
targetPlayer.getName().getString()
|
||||
),
|
||||
Component.translatable(
|
||||
"command.tiedup.inventory.restored", targetPlayer.getName().getString()
|
||||
).withStyle(ChatFormatting.GREEN),
|
||||
true
|
||||
);
|
||||
SystemMessageManager.sendToPlayer(
|
||||
@@ -88,9 +87,8 @@ public class InventorySubCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal(
|
||||
"Failed to restore inventory for " +
|
||||
targetPlayer.getName().getString()
|
||||
Component.translatable(
|
||||
"command.tiedup.inventory.restore_failed", targetPlayer.getName().getString()
|
||||
)
|
||||
);
|
||||
return 0;
|
||||
|
||||
@@ -77,7 +77,7 @@ public class MasterTestSubCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("Failed to create Master entity")
|
||||
Component.translatable("command.tiedup.master.spawn_failed")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -108,10 +108,8 @@ public class MasterTestSubCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Spawned Master '" +
|
||||
finalName +
|
||||
"' \u2014 you are now their pet."
|
||||
Component.translatable(
|
||||
"command.tiedup.master.spawned", finalName
|
||||
),
|
||||
true
|
||||
);
|
||||
@@ -133,7 +131,7 @@ public class MasterTestSubCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("No Master NPC found within 20 blocks")
|
||||
Component.translatable("command.tiedup.master.no_master_nearby")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -157,8 +155,8 @@ public class MasterTestSubCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Forced " + finalName + " into HUMAN_CHAIR state"
|
||||
Component.translatable(
|
||||
"command.tiedup.master.forced_state", finalName, "HUMAN_CHAIR"
|
||||
),
|
||||
true
|
||||
);
|
||||
@@ -183,7 +181,7 @@ public class MasterTestSubCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("Unknown MasterState: " + taskName)
|
||||
Component.translatable("command.tiedup.master.unknown_state", taskName)
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -193,7 +191,7 @@ public class MasterTestSubCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendFailure(
|
||||
Component.literal("No Master NPC found within 20 blocks")
|
||||
Component.translatable("command.tiedup.master.no_master_nearby")
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -217,12 +215,8 @@ public class MasterTestSubCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Forced " +
|
||||
finalName +
|
||||
" into " +
|
||||
targetState.name() +
|
||||
" state"
|
||||
Component.translatable(
|
||||
"command.tiedup.master.forced_state", finalName, targetState.name()
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
@@ -87,8 +87,8 @@ public class TestAnimSubCommand {
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() ->
|
||||
Component.literal(
|
||||
"Playing animation '" + anim + "' on " + name
|
||||
Component.translatable(
|
||||
"command.tiedup.testanim.playing", anim, name
|
||||
),
|
||||
false
|
||||
);
|
||||
@@ -116,7 +116,7 @@ public class TestAnimSubCommand {
|
||||
context
|
||||
.getSource()
|
||||
.sendSuccess(
|
||||
() -> Component.literal("Stopped animation on " + name),
|
||||
() -> Component.translatable("command.tiedup.testanim.stopped", name),
|
||||
false
|
||||
);
|
||||
return 1;
|
||||
|
||||
@@ -138,120 +138,17 @@ public class SystemMessageManager {
|
||||
ERROR, // Generic error
|
||||
}
|
||||
|
||||
// MESSAGE TEMPLATES
|
||||
// TRANSLATION KEYS
|
||||
|
||||
/**
|
||||
* Get the raw message template for a category.
|
||||
* Use this when you need to customize the message.
|
||||
* Get the translation key for a category.
|
||||
* Keys follow the pattern: msg.tiedup.system.<category_lowercase>
|
||||
*
|
||||
* @param category The message category
|
||||
* @return The template string (may contain %s placeholders)
|
||||
* @return The translation key (for use with Component.translatable)
|
||||
*/
|
||||
public static String getTemplate(MessageCategory category) {
|
||||
return getMessageTemplate(category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get message template for a category.
|
||||
* Use %s for entity name placeholder.
|
||||
*/
|
||||
private static String getMessageTemplate(MessageCategory category) {
|
||||
return switch (category) {
|
||||
// Restraint actions
|
||||
case BEING_TIED -> "%s is tying you up!";
|
||||
case TIED_UP -> "%s tied you up, you can't move!";
|
||||
case BEING_GAGGED -> "%s is gagging you!";
|
||||
case GAGGED -> "%s gagged you, you can't speak!";
|
||||
case BEING_BLINDFOLDED -> "%s is blindfolding you!";
|
||||
case BLINDFOLDED -> "%s blindfolded you, you can't see!";
|
||||
case BEING_COLLARED -> "%s is putting a collar on you!";
|
||||
case COLLARED -> "%s collared you!";
|
||||
case EARPLUGS_ON -> "%s put earplugs on you!";
|
||||
case MITTENS_ON -> "%s put mittens on you!";
|
||||
case ENSLAVED -> "You have been enslaved by %s!";
|
||||
// Restraint actions (kidnapper's perspective)
|
||||
case TYING_TARGET -> "You are tying %s...";
|
||||
case TIED_TARGET -> "You tied %s!";
|
||||
case GAGGING_TARGET -> "You are gagging %s...";
|
||||
case GAGGED_TARGET -> "You gagged %s!";
|
||||
case BLINDFOLDING_TARGET -> "You are blindfolding %s...";
|
||||
case BLINDFOLDED_TARGET -> "You blindfolded %s!";
|
||||
case COLLARING_TARGET -> "You are collaring %s...";
|
||||
case COLLARED_TARGET -> "You collared %s!";
|
||||
// Release actions
|
||||
case UNTIED -> "%s untied you!";
|
||||
case UNGAGGED -> "%s removed your gag!";
|
||||
case UNBLINDFOLDED -> "%s removed your blindfold!";
|
||||
case UNCOLLARED -> "%s removed your collar!";
|
||||
case FREED -> "You have been freed!";
|
||||
// Struggle
|
||||
case STRUGGLE_SUCCESS -> "You feel the ropes loosening...";
|
||||
case STRUGGLE_FAIL -> "You struggle against the ropes, but they hold tight.";
|
||||
case STRUGGLE_BROKE_FREE -> "You broke free!";
|
||||
case STRUGGLE_SHOCKED -> "You were shocked for struggling!";
|
||||
case STRUGGLE_COLLAR_SUCCESS -> "You manage to damage the lock!";
|
||||
case STRUGGLE_COLLAR_FAIL -> "You try to reach the lock, but can't get a good grip.";
|
||||
// Restrictions (Tied)
|
||||
case CANT_MOVE -> "You can't move while tied!";
|
||||
case CANT_ATTACK_TIED -> "You can't attack while tied!";
|
||||
case CANT_USE_ITEM_TIED -> "You can't use items while tied!";
|
||||
case CANT_OPEN_INVENTORY -> "You can't open inventory while tied!";
|
||||
case CANT_INTERACT_TIED -> "You can't interact while tied!";
|
||||
case CANT_SPEAK -> "You can't speak while gagged!";
|
||||
case CANT_SEE -> "You can't see while blindfolded!";
|
||||
case CANT_BREAK_TIED -> "You can't break blocks while tied!";
|
||||
case CANT_PLACE_TIED -> "You can't place blocks while tied!";
|
||||
case NO_ELYTRA -> "You can't fly with elytra while tied!";
|
||||
// Restrictions (Mittens)
|
||||
case CANT_ATTACK_MITTENS -> "You can't attack with mittens on!";
|
||||
case CANT_USE_ITEM_MITTENS -> "You can't use items with mittens on!";
|
||||
case CANT_INTERACT_MITTENS -> "You can't interact with mittens on!";
|
||||
case CANT_BREAK_MITTENS -> "You can't break blocks with mittens on!";
|
||||
case CANT_PLACE_MITTENS -> "You can't place blocks with mittens on!";
|
||||
// Slave system
|
||||
case SLAVE_COMMAND -> "Your master commands: %s";
|
||||
case SLAVE_SHOCK -> "You've been shocked!";
|
||||
case GPS_ZONE_VIOLATION -> "You've been shocked! Return back to your allowed area!";
|
||||
case GPS_OWNER_ALERT -> "ALERT: %s is outside the safe zone!";
|
||||
case SLAVE_JOB_ASSIGNED -> "Job assigned: bring %s";
|
||||
case SLAVE_JOB_COMPLETE -> "Job complete! You are free.";
|
||||
case SLAVE_JOB_FAILED -> "Job failed!";
|
||||
case SLAVE_JOB_LAST_CHANCE -> "LAST CHANCE! Next failure means death!";
|
||||
case SLAVE_JOB_KILLED -> "You were executed for failing your task.";
|
||||
// Tighten
|
||||
case BINDS_TIGHTENED -> "%s tightened your binds!";
|
||||
// Tools & Items
|
||||
case KEY_CLAIMED -> "Key claimed and linked to %s!";
|
||||
case KEY_NOT_OWNER -> "You don't own this key!";
|
||||
case KEY_WRONG_TARGET -> "This key doesn't fit this collar!";
|
||||
case LOCATOR_CLAIMED -> "Locator claimed!";
|
||||
case LOCATOR_NOT_OWNER -> "You don't own this locator!";
|
||||
case LOCATOR_DETECTED -> "Target detected: %s";
|
||||
case SHOCKER_CLAIMED -> "Shocker claimed!";
|
||||
case SHOCKER_NOT_OWNER -> "You don't own this shocker!";
|
||||
case SHOCKER_MODE_SET -> "Shocker mode: %s";
|
||||
case SHOCKER_TRIGGERED -> "Shocked %s!";
|
||||
case RAG_DRY -> "The rag is dry - soak it first";
|
||||
case RAG_SOAKED -> "You soaked the rag with chloroform";
|
||||
case RAG_EVAPORATED -> "The chloroform has evaporated";
|
||||
// Bounty
|
||||
case BOUNTY_CREATED -> "Bounty created on %s!";
|
||||
case BOUNTY_CLAIMED -> "You claimed the bounty on %s!";
|
||||
case BOUNTY_EXPIRED -> "Bounty on %s expired";
|
||||
// Cell System
|
||||
case PRISONER_ARRIVED -> "%s has been placed in your cell";
|
||||
case PRISONER_ESCAPED -> "%s has escaped from your cell!";
|
||||
case PRISONER_RELEASED -> "%s has been released from your cell";
|
||||
case CELL_BREACH -> "Your cell wall has been breached!";
|
||||
case CELL_ASSIGNED -> "You have been assigned to %s's cell";
|
||||
case CELL_CREATED -> "Cell created successfully";
|
||||
case CELL_DELETED -> "Cell deleted";
|
||||
case CELL_RENAMED -> "Cell renamed to: %s";
|
||||
// Generic
|
||||
case INFO -> "%s";
|
||||
case WARNING -> "%s";
|
||||
case ERROR -> "%s";
|
||||
};
|
||||
public static String getTranslationKey(MessageCategory category) {
|
||||
return "msg.tiedup.system." + category.name().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,11 +270,11 @@ public class SystemMessageManager {
|
||||
|
||||
/**
|
||||
* Send a system message to a player's action bar.
|
||||
* Uses category template with entity name.
|
||||
* Uses translatable category with entity name as argument.
|
||||
*
|
||||
* @param player The player to send to
|
||||
* @param category The message category
|
||||
* @param actor The entity performing the action (for %s replacement)
|
||||
* @param actor The entity performing the action (for %1$s replacement)
|
||||
*/
|
||||
public static void sendToPlayer(
|
||||
Player player,
|
||||
@@ -387,14 +284,23 @@ public class SystemMessageManager {
|
||||
if (player == null) return;
|
||||
|
||||
String actorName = actor != null ? getEntityName(actor) : "Someone";
|
||||
String message = String.format(getMessageTemplate(category), actorName);
|
||||
MutableComponent component = Component.translatable(
|
||||
getTranslationKey(category), actorName
|
||||
).withStyle(style -> style.withColor(getCategoryColor(category)));
|
||||
|
||||
sendToPlayer(player, message, getCategoryColor(category));
|
||||
player.displayClientMessage(component, true);
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[SystemMessage] -> {}: {} ({})",
|
||||
player.getName().getString(),
|
||||
getTranslationKey(category),
|
||||
actorName
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a system message to a player's action bar.
|
||||
* Uses category template without entity (for messages that don't need one).
|
||||
* Uses translatable category without arguments.
|
||||
*
|
||||
* @param player The player to send to
|
||||
* @param category The message category
|
||||
@@ -402,12 +308,22 @@ public class SystemMessageManager {
|
||||
public static void sendToPlayer(Player player, MessageCategory category) {
|
||||
if (player == null) return;
|
||||
|
||||
String message = getMessageTemplate(category);
|
||||
sendToPlayer(player, message, getCategoryColor(category));
|
||||
MutableComponent component = Component.translatable(
|
||||
getTranslationKey(category)
|
||||
).withStyle(style -> style.withColor(getCategoryColor(category)));
|
||||
|
||||
player.displayClientMessage(component, true);
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[SystemMessage] -> {}: {}",
|
||||
player.getName().getString(),
|
||||
getTranslationKey(category)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a custom system message to a player's action bar.
|
||||
* Uses literal text (for dynamic/non-translatable messages).
|
||||
*
|
||||
* @param player The player to send to
|
||||
* @param message The message to send
|
||||
@@ -420,12 +336,10 @@ public class SystemMessageManager {
|
||||
) {
|
||||
if (player == null || message == null) return;
|
||||
|
||||
// Works on both client and server
|
||||
MutableComponent component = Component.literal(message).withStyle(
|
||||
style -> style.withColor(color)
|
||||
);
|
||||
|
||||
// true = action bar (above hotbar), false = chat
|
||||
player.displayClientMessage(component, true);
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
@@ -437,6 +351,7 @@ public class SystemMessageManager {
|
||||
|
||||
/**
|
||||
* Send a custom system message to a player's CHAT.
|
||||
* Uses literal text (for dynamic/non-translatable messages).
|
||||
*
|
||||
* @param player The player to send to
|
||||
* @param message The message to send
|
||||
@@ -453,7 +368,6 @@ public class SystemMessageManager {
|
||||
style -> style.withColor(color)
|
||||
);
|
||||
|
||||
// false = chat
|
||||
player.displayClientMessage(component, false);
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
@@ -464,19 +378,21 @@ public class SystemMessageManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a system message to a player's CHAT using a category.
|
||||
* Send a system message to a player's CHAT using a translatable category.
|
||||
*/
|
||||
public static void sendChatToPlayer(
|
||||
Player player,
|
||||
MessageCategory category
|
||||
) {
|
||||
if (player == null) return;
|
||||
String message = getMessageTemplate(category);
|
||||
sendChatToPlayer(player, message, getCategoryColor(category));
|
||||
MutableComponent component = Component.translatable(
|
||||
getTranslationKey(category)
|
||||
).withStyle(style -> style.withColor(getCategoryColor(category)));
|
||||
player.displayClientMessage(component, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a system message to a player's CHAT using a category and actor.
|
||||
* Send a system message to a player's CHAT using a translatable category and actor.
|
||||
*/
|
||||
public static void sendChatToPlayer(
|
||||
Player player,
|
||||
@@ -485,8 +401,10 @@ public class SystemMessageManager {
|
||||
) {
|
||||
if (player == null) return;
|
||||
String actorName = actor != null ? getEntityName(actor) : "Someone";
|
||||
String message = String.format(getMessageTemplate(category), actorName);
|
||||
sendChatToPlayer(player, message, getCategoryColor(category));
|
||||
MutableComponent component = Component.translatable(
|
||||
getTranslationKey(category), actorName
|
||||
).withStyle(style -> style.withColor(getCategoryColor(category)));
|
||||
player.displayClientMessage(component, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -494,7 +412,7 @@ public class SystemMessageManager {
|
||||
*
|
||||
* @param player The player to send to
|
||||
* @param category The category (for color)
|
||||
* @param customMessage The custom message text
|
||||
* @param customMessage The custom message text (literal, not translatable)
|
||||
*/
|
||||
public static void sendToPlayer(
|
||||
Player player,
|
||||
@@ -505,7 +423,27 @@ public class SystemMessageManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message with resistance info appended.
|
||||
* Send a translatable message with string arguments.
|
||||
* Uses the category's translation key and color.
|
||||
*
|
||||
* @param player The player to send to
|
||||
* @param category The message category
|
||||
* @param args Arguments for the translation (replaces %1$s, %2$s, etc.)
|
||||
*/
|
||||
public static void sendTranslatable(
|
||||
Player player,
|
||||
MessageCategory category,
|
||||
Object... args
|
||||
) {
|
||||
if (player == null) return;
|
||||
MutableComponent component = Component.translatable(
|
||||
getTranslationKey(category), args
|
||||
).withStyle(style -> style.withColor(getCategoryColor(category)));
|
||||
player.displayClientMessage(component, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a translatable message with resistance info appended.
|
||||
*
|
||||
* @param player The player to send to
|
||||
* @param category The message category
|
||||
@@ -518,9 +456,13 @@ public class SystemMessageManager {
|
||||
) {
|
||||
if (player == null) return;
|
||||
|
||||
String message =
|
||||
getMessageTemplate(category) + " (Resistance: " + resistance + ")";
|
||||
sendToPlayer(player, message, getCategoryColor(category));
|
||||
MutableComponent component = Component.translatable(
|
||||
getTranslationKey(category)
|
||||
).append(
|
||||
Component.translatable("msg.tiedup.system.resistance_suffix", resistance)
|
||||
).withStyle(style -> style.withColor(getCategoryColor(category)));
|
||||
|
||||
player.displayClientMessage(component, true);
|
||||
}
|
||||
|
||||
// SEND METHODS - TO NEARBY PLAYERS
|
||||
@@ -695,15 +637,10 @@ public class SystemMessageManager {
|
||||
*/
|
||||
public static void sendJobAssigned(Player player, String itemName) {
|
||||
if (player == null) return;
|
||||
String message = String.format(
|
||||
getMessageTemplate(MessageCategory.SLAVE_JOB_ASSIGNED),
|
||||
itemName
|
||||
);
|
||||
sendToPlayer(
|
||||
player,
|
||||
message,
|
||||
getCategoryColor(MessageCategory.SLAVE_JOB_ASSIGNED)
|
||||
);
|
||||
MutableComponent component = Component.translatable(
|
||||
getTranslationKey(MessageCategory.SLAVE_JOB_ASSIGNED), itemName
|
||||
).withStyle(style -> style.withColor(getCategoryColor(MessageCategory.SLAVE_JOB_ASSIGNED)));
|
||||
player.displayClientMessage(component, true);
|
||||
}
|
||||
|
||||
// UTILITY
|
||||
|
||||
@@ -581,6 +581,14 @@ public class TiedUpMod {
|
||||
LOGGER.info(
|
||||
"Registered FurnitureServerReloadListener for data-driven furniture definitions"
|
||||
);
|
||||
|
||||
// Data-driven room theme definitions (server-side, from data/<namespace>/tiedup_room_themes/)
|
||||
event.addListener(
|
||||
new com.tiedup.remake.worldgen.RoomThemeReloadListener()
|
||||
);
|
||||
LOGGER.info(
|
||||
"Registered RoomThemeReloadListener for data-driven room themes"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.tiedup.remake.state.components.PlayerClothesPermission;
|
||||
import com.tiedup.remake.state.components.PlayerDataRetrieval;
|
||||
import com.tiedup.remake.state.components.PlayerEquipment;
|
||||
import com.tiedup.remake.state.components.PlayerLifecycle;
|
||||
import com.tiedup.remake.state.components.PlayerMovement;
|
||||
import com.tiedup.remake.state.components.PlayerSale;
|
||||
import com.tiedup.remake.state.components.PlayerShockCollar;
|
||||
import com.tiedup.remake.state.components.PlayerSpecialActions;
|
||||
@@ -49,8 +50,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
* - Enslavement lifecycle (can be enslaved, act as master)
|
||||
* - Advanced collar features (shocks, GPS tracking)
|
||||
*
|
||||
* Thread Safety: This class is accessed from both server and client threads.
|
||||
* Use appropriate synchronization when accessing the instances map.
|
||||
* Thread Safety: All mutating methods must be called from the server thread
|
||||
* (via enqueueWork for packet handlers). The only cross-thread access is
|
||||
* the StruggleSnapshot (atomic volatile record) and the Player reference (volatile).
|
||||
* The instances map uses ConcurrentHashMap for thread-safe lookup.
|
||||
*
|
||||
* Refactoring: Component-Host pattern
|
||||
*/
|
||||
@@ -138,89 +141,27 @@ public class PlayerBindState implements IRestrainable, IPlayerBindStateHost {
|
||||
private PlayerCaptorManager captorManager;
|
||||
// Note: transport field removed - now using IPlayerLeashAccess mixin
|
||||
|
||||
// Struggle animation state
|
||||
// volatile: accessed from multiple threads (network, tick, render)
|
||||
private volatile boolean isStruggling = false;
|
||||
private volatile long struggleStartTick = 0;
|
||||
// Struggle animation state — atomic snapshot for cross-thread visibility.
|
||||
// Server thread writes via setStruggling(), render thread reads via isStruggling()/getStruggleStartTick().
|
||||
// Single volatile reference guarantees both fields are visible atomically.
|
||||
private record StruggleSnapshot(boolean struggling, long startTick) {}
|
||||
private volatile StruggleSnapshot struggleState = new StruggleSnapshot(false, 0);
|
||||
|
||||
// ========== Movement Style State (Phase: Movement Styles) ==========
|
||||
// Managed exclusively by MovementStyleManager. Stored here to piggyback
|
||||
// on existing lifecycle cleanup hooks (death, logout, dimension change).
|
||||
// ========== Movement Style State ==========
|
||||
// Encapsulated in PlayerMovement component. Managed exclusively by MovementStyleManager.
|
||||
private final PlayerMovement movement;
|
||||
|
||||
/** Currently active movement style, or null if none. */
|
||||
@Nullable
|
||||
private com.tiedup.remake.v2.bondage.movement.MovementStyle activeMovementStyle;
|
||||
|
||||
/** Resolved speed multiplier for the active style (0.0-1.0). */
|
||||
private float resolvedMovementSpeed = 1.0f;
|
||||
|
||||
/** Whether jumping is currently disabled by the active style. */
|
||||
private boolean resolvedJumpDisabled = false;
|
||||
|
||||
@Nullable
|
||||
public com.tiedup.remake.v2.bondage.movement.MovementStyle getActiveMovementStyle() {
|
||||
return activeMovementStyle;
|
||||
/** Access movement style state (hop, crawl, position tracking, etc.). */
|
||||
public PlayerMovement getMovement() {
|
||||
return movement;
|
||||
}
|
||||
|
||||
public void setActiveMovementStyle(
|
||||
@Nullable com.tiedup.remake.v2.bondage.movement.MovementStyle style
|
||||
) {
|
||||
this.activeMovementStyle = style;
|
||||
}
|
||||
|
||||
public float getResolvedMovementSpeed() {
|
||||
return resolvedMovementSpeed;
|
||||
}
|
||||
|
||||
public void setResolvedMovementSpeed(float speed) {
|
||||
this.resolvedMovementSpeed = speed;
|
||||
}
|
||||
|
||||
public boolean isResolvedJumpDisabled() {
|
||||
return resolvedJumpDisabled;
|
||||
}
|
||||
|
||||
public void setResolvedJumpDisabled(boolean disabled) {
|
||||
this.resolvedJumpDisabled = disabled;
|
||||
}
|
||||
|
||||
/** Ticks until next hop is allowed (HOP style). */
|
||||
public int hopCooldown = 0;
|
||||
|
||||
/** True during the 4-tick startup delay before the first hop. */
|
||||
public boolean hopStartupPending = false;
|
||||
|
||||
/** Countdown ticks for the hop startup delay. */
|
||||
public int hopStartupTicks = 0;
|
||||
|
||||
/** True if crawl deactivated but player can't stand yet (space blocked). */
|
||||
public boolean pendingPoseRestore = false;
|
||||
|
||||
/** Last known X position for movement detection (position delta). */
|
||||
public double lastX;
|
||||
|
||||
/** Last known Y position for movement detection (position delta). */
|
||||
public double lastY;
|
||||
|
||||
/** Last known Z position for movement detection (position delta). */
|
||||
public double lastZ;
|
||||
|
||||
/** Consecutive non-moving ticks counter for hop startup reset. */
|
||||
public int hopNotMovingTicks = 0;
|
||||
|
||||
/**
|
||||
* Resets all movement style state to defaults.
|
||||
* Called on death, logout, and dimension change to ensure clean re-activation.
|
||||
*/
|
||||
public void clearMovementState() {
|
||||
this.activeMovementStyle = null;
|
||||
this.resolvedMovementSpeed = 1.0f;
|
||||
this.resolvedJumpDisabled = false;
|
||||
this.hopCooldown = 0;
|
||||
this.hopStartupPending = false;
|
||||
this.hopStartupTicks = 0;
|
||||
this.pendingPoseRestore = false;
|
||||
this.hopNotMovingTicks = 0;
|
||||
movement.clear();
|
||||
}
|
||||
|
||||
// ========== Constructor ==========
|
||||
@@ -249,6 +190,9 @@ public class PlayerBindState implements IRestrainable, IPlayerBindStateHost {
|
||||
this.lifecycle = new PlayerLifecycle(this);
|
||||
this.captivity = new PlayerCaptivity(this);
|
||||
|
||||
// Initialize movement component
|
||||
this.movement = new PlayerMovement();
|
||||
|
||||
this.captor = null;
|
||||
this.captorManager = new PlayerCaptorManager(player);
|
||||
}
|
||||
@@ -578,47 +522,42 @@ public class PlayerBindState implements IRestrainable, IPlayerBindStateHost {
|
||||
|
||||
/**
|
||||
* Check if player is currently playing struggle animation.
|
||||
* Thread-safe (volatile field).
|
||||
* IPlayerBindStateHost implementation.
|
||||
* Thread-safe: reads from atomic StruggleSnapshot.
|
||||
*/
|
||||
@Override
|
||||
public boolean isStruggling() {
|
||||
return isStruggling;
|
||||
return struggleState.struggling();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tick when struggle animation started.
|
||||
* Thread-safe (volatile field).
|
||||
* IPlayerBindStateHost implementation.
|
||||
* Thread-safe: reads from atomic StruggleSnapshot.
|
||||
*/
|
||||
@Override
|
||||
public long getStruggleStartTick() {
|
||||
return struggleStartTick;
|
||||
return struggleState.startTick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set struggle animation state (server-side).
|
||||
* IPlayerBindStateHost implementation.
|
||||
* @param struggling True to start struggle animation, false to stop
|
||||
* @param currentTick Current game time tick (for timer)
|
||||
* Single volatile write ensures both fields are visible atomically to render thread.
|
||||
*/
|
||||
@Override
|
||||
public void setStruggling(boolean struggling, long currentTick) {
|
||||
this.isStruggling = struggling;
|
||||
if (struggling) {
|
||||
this.struggleStartTick = currentTick;
|
||||
}
|
||||
this.struggleState = new StruggleSnapshot(
|
||||
struggling,
|
||||
struggling ? currentTick : struggleState.startTick()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set struggle animation flag (client-side only).
|
||||
* IPlayerBindStateHost implementation.
|
||||
* Used by network sync - does NOT update timer (server manages timer).
|
||||
* @param struggling True if struggling, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public void setStrugglingClient(boolean struggling) {
|
||||
this.isStruggling = struggling;
|
||||
StruggleSnapshot current = this.struggleState;
|
||||
this.struggleState = new StruggleSnapshot(struggling, current.startTick());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -635,31 +574,34 @@ public class PlayerBindState implements IRestrainable, IPlayerBindStateHost {
|
||||
* Delegated to PlayerEquipment component
|
||||
*/
|
||||
@Override
|
||||
public synchronized int getCurrentBindResistance() {
|
||||
public int getCurrentBindResistance() {
|
||||
return equipment.getCurrentBindResistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegated to PlayerEquipment component
|
||||
* Delegated to PlayerEquipment component.
|
||||
* Thread safety: must be called from the server thread.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void setCurrentBindResistance(int resistance) {
|
||||
public void setCurrentBindResistance(int resistance) {
|
||||
equipment.setCurrentBindResistance(resistance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegated to PlayerEquipment component
|
||||
* Delegated to PlayerEquipment component.
|
||||
* Thread safety: must be called from the server thread.
|
||||
*/
|
||||
@Override
|
||||
public synchronized int getCurrentCollarResistance() {
|
||||
public int getCurrentCollarResistance() {
|
||||
return equipment.getCurrentCollarResistance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegated to PlayerEquipment component
|
||||
* Delegated to PlayerEquipment component.
|
||||
* Thread safety: must be called from the server thread.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void setCurrentCollarResistance(int resistance) {
|
||||
public void setCurrentCollarResistance(int resistance) {
|
||||
equipment.setCurrentCollarResistance(resistance);
|
||||
}
|
||||
|
||||
@@ -1006,12 +948,12 @@ public class PlayerBindState implements IRestrainable, IPlayerBindStateHost {
|
||||
// Delegated to PlayerEquipment component (except TORSO - handled by PlayerClothesPermission)
|
||||
|
||||
@Override
|
||||
public synchronized ItemStack replaceEquipment(
|
||||
public ItemStack replaceEquipment(
|
||||
BodyRegionV2 region,
|
||||
ItemStack newStack,
|
||||
boolean force
|
||||
) {
|
||||
// MEDIUM FIX: Synchronized to prevent race condition during equipment replacement
|
||||
// Thread safety: must be called from the server thread.
|
||||
return switch (region) {
|
||||
case ARMS -> equipment.replaceBind(newStack, force);
|
||||
case MOUTH -> equipment.replaceGag(newStack, force);
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.tiedup.remake.state.components;
|
||||
|
||||
import com.tiedup.remake.v2.bondage.movement.MovementStyle;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Component responsible for movement style state management.
|
||||
* Stores all per-player movement fields used exclusively by MovementStyleManager.
|
||||
*
|
||||
* <p>Thread safety: All fields are server-tick-only (PlayerTickEvent + LivingJumpEvent,
|
||||
* both on the server thread). No synchronization needed.</p>
|
||||
*/
|
||||
public class PlayerMovement {
|
||||
|
||||
// --- Resolved style state ---
|
||||
@Nullable
|
||||
private MovementStyle activeMovementStyle;
|
||||
private float resolvedMovementSpeed = 1.0f;
|
||||
private boolean resolvedJumpDisabled = false;
|
||||
|
||||
// --- Hop state ---
|
||||
private int hopCooldown = 0;
|
||||
private boolean hopStartupPending = false;
|
||||
private int hopStartupTicks = 0;
|
||||
private int hopNotMovingTicks = 0;
|
||||
|
||||
// --- Crawl state ---
|
||||
private boolean pendingPoseRestore = false;
|
||||
|
||||
// --- Position tracking for movement detection ---
|
||||
private double lastX;
|
||||
private double lastY;
|
||||
private double lastZ;
|
||||
|
||||
public PlayerMovement() {
|
||||
}
|
||||
|
||||
// --- Resolved style ---
|
||||
|
||||
@Nullable
|
||||
public MovementStyle getActiveMovementStyle() {
|
||||
return activeMovementStyle;
|
||||
}
|
||||
|
||||
public void setActiveMovementStyle(@Nullable MovementStyle style) {
|
||||
this.activeMovementStyle = style;
|
||||
}
|
||||
|
||||
public float getResolvedMovementSpeed() {
|
||||
return resolvedMovementSpeed;
|
||||
}
|
||||
|
||||
public void setResolvedMovementSpeed(float speed) {
|
||||
this.resolvedMovementSpeed = speed;
|
||||
}
|
||||
|
||||
public boolean isResolvedJumpDisabled() {
|
||||
return resolvedJumpDisabled;
|
||||
}
|
||||
|
||||
public void setResolvedJumpDisabled(boolean disabled) {
|
||||
this.resolvedJumpDisabled = disabled;
|
||||
}
|
||||
|
||||
// --- Hop ---
|
||||
|
||||
public int getHopCooldown() {
|
||||
return hopCooldown;
|
||||
}
|
||||
|
||||
public void setHopCooldown(int hopCooldown) {
|
||||
this.hopCooldown = hopCooldown;
|
||||
}
|
||||
|
||||
public boolean isHopStartupPending() {
|
||||
return hopStartupPending;
|
||||
}
|
||||
|
||||
public void setHopStartupPending(boolean hopStartupPending) {
|
||||
this.hopStartupPending = hopStartupPending;
|
||||
}
|
||||
|
||||
public int getHopStartupTicks() {
|
||||
return hopStartupTicks;
|
||||
}
|
||||
|
||||
public void setHopStartupTicks(int hopStartupTicks) {
|
||||
this.hopStartupTicks = hopStartupTicks;
|
||||
}
|
||||
|
||||
public int getHopNotMovingTicks() {
|
||||
return hopNotMovingTicks;
|
||||
}
|
||||
|
||||
public void setHopNotMovingTicks(int hopNotMovingTicks) {
|
||||
this.hopNotMovingTicks = hopNotMovingTicks;
|
||||
}
|
||||
|
||||
// --- Crawl ---
|
||||
|
||||
public boolean isPendingPoseRestore() {
|
||||
return pendingPoseRestore;
|
||||
}
|
||||
|
||||
public void setPendingPoseRestore(boolean pendingPoseRestore) {
|
||||
this.pendingPoseRestore = pendingPoseRestore;
|
||||
}
|
||||
|
||||
// --- Position tracking ---
|
||||
|
||||
public double getLastX() {
|
||||
return lastX;
|
||||
}
|
||||
|
||||
public void setLastX(double lastX) {
|
||||
this.lastX = lastX;
|
||||
}
|
||||
|
||||
public double getLastY() {
|
||||
return lastY;
|
||||
}
|
||||
|
||||
public void setLastY(double lastY) {
|
||||
this.lastY = lastY;
|
||||
}
|
||||
|
||||
public double getLastZ() {
|
||||
return lastZ;
|
||||
}
|
||||
|
||||
public void setLastZ(double lastZ) {
|
||||
this.lastZ = lastZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all movement style state to defaults.
|
||||
* Called on death, logout, and dimension change to ensure clean re-activation.
|
||||
* Note: lastX/Y/Z are NOT reset — they track position, not style state.
|
||||
*/
|
||||
public void clear() {
|
||||
this.activeMovementStyle = null;
|
||||
this.resolvedMovementSpeed = 1.0f;
|
||||
this.resolvedJumpDisabled = false;
|
||||
this.hopCooldown = 0;
|
||||
this.hopStartupPending = false;
|
||||
this.hopStartupTicks = 0;
|
||||
this.pendingPoseRestore = false;
|
||||
this.hopNotMovingTicks = 0;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -54,7 +56,8 @@ public class PlayerShockCollar {
|
||||
* Triggers a visual and auditory shock effect.
|
||||
* Damage is applied (shock can kill).
|
||||
*
|
||||
* @param messageAddon Optional message addon for HUD (e.g., GPS violation)
|
||||
* @param messageAddon Optional message addon for HUD (e.g., GPS violation).
|
||||
* For translatable messages, prefer calling shockKidnapped() then sending the message separately.
|
||||
* @param damage Shock damage amount
|
||||
*/
|
||||
public void shockKidnapped(@Nullable String messageAddon, float damage) {
|
||||
@@ -98,13 +101,12 @@ public class PlayerShockCollar {
|
||||
|
||||
// HUD Message via SystemMessageManager
|
||||
if (messageAddon != null) {
|
||||
// Custom message with addon (e.g., GPS violation)
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
MessageCategory.SLAVE_SHOCK,
|
||||
SystemMessageManager.getTemplate(MessageCategory.SLAVE_SHOCK) +
|
||||
messageAddon
|
||||
);
|
||||
// Legacy addon path: append literal text to SLAVE_SHOCK message
|
||||
MutableComponent msg = Component.translatable(
|
||||
SystemMessageManager.getTranslationKey(MessageCategory.SLAVE_SHOCK)
|
||||
).append(Component.literal(messageAddon));
|
||||
msg.withStyle(style -> style.withColor(ChatFormatting.RED));
|
||||
player.displayClientMessage(msg, true);
|
||||
} else {
|
||||
SystemMessageManager.sendToPlayer(
|
||||
player,
|
||||
@@ -196,9 +198,11 @@ public class PlayerShockCollar {
|
||||
|
||||
if (shouldShockGPS && gpsStackCopy != null) {
|
||||
this.shockKidnapped(
|
||||
" Return back to your allowed area!",
|
||||
null,
|
||||
GameConstants.DEFAULT_SHOCK_DAMAGE
|
||||
);
|
||||
// GPS-specific message (replaces hardcoded " Return back to your allowed area!")
|
||||
SystemMessageManager.sendToPlayer(host.getPlayer(), MessageCategory.GPS_ZONE_VIOLATION);
|
||||
warnOwnersGPSViolation(gpsStackCopy);
|
||||
}
|
||||
}
|
||||
@@ -239,11 +243,8 @@ public class PlayerShockCollar {
|
||||
|
||||
if (!CollarHelper.shouldWarnMasters(stack)) return;
|
||||
|
||||
// Format: "ALERT: <player name> is outside the safe zone!"
|
||||
String alertMessage = String.format(
|
||||
SystemMessageManager.getTemplate(MessageCategory.GPS_OWNER_ALERT),
|
||||
player.getName().getString()
|
||||
);
|
||||
// Translatable: "ALERT: <player name> is outside the safe zone!"
|
||||
String playerName = player.getName().getString();
|
||||
|
||||
for (UUID ownerId : CollarHelper.getOwners(stack)) {
|
||||
ServerPlayer owner = player
|
||||
@@ -253,8 +254,8 @@ public class PlayerShockCollar {
|
||||
if (owner != null) {
|
||||
SystemMessageManager.sendChatToPlayer(
|
||||
owner,
|
||||
alertMessage,
|
||||
ChatFormatting.RED
|
||||
MessageCategory.GPS_OWNER_ALERT,
|
||||
player
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +42,9 @@ public class PlayerStruggle {
|
||||
* Entry point for the Struggle logic (Key R).
|
||||
* Distributes effort between Binds and Collar.
|
||||
*
|
||||
* Thread Safety: Synchronized to prevent lost updates when multiple struggle
|
||||
* packets arrive simultaneously (e.g., from macro/rapid keypresses).
|
||||
* Thread Safety: Must be called from the server thread (packet handlers use enqueueWork).
|
||||
*/
|
||||
public synchronized void struggle() {
|
||||
public void struggle() {
|
||||
if (struggleBindState != null) struggleBindState.struggle(state);
|
||||
if (struggleCollarState != null) struggleCollarState.struggle(state);
|
||||
}
|
||||
@@ -53,9 +52,9 @@ public class PlayerStruggle {
|
||||
/**
|
||||
* Restores resistance to base values when a master tightens the ties.
|
||||
*
|
||||
* Thread Safety: Synchronized to prevent race with struggle operations.
|
||||
* Thread Safety: Must be called from the server thread.
|
||||
*/
|
||||
public synchronized void tighten(Player tightener) {
|
||||
public void tighten(Player tightener) {
|
||||
if (struggleBindState != null) struggleBindState.tighten(
|
||||
tightener,
|
||||
state
|
||||
|
||||
@@ -129,14 +129,14 @@ public class MovementStyleManager {
|
||||
player.isDeadOrDying() ||
|
||||
state.isStruggling()
|
||||
) {
|
||||
state.lastX = player.getX();
|
||||
state.lastY = player.getY();
|
||||
state.lastZ = player.getZ();
|
||||
state.getMovement().setLastX(player.getX());
|
||||
state.getMovement().setLastY(player.getY());
|
||||
state.getMovement().setLastZ(player.getZ());
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Pending pose restore (crawl deactivated but can't stand) ---
|
||||
if (state.pendingPoseRestore) {
|
||||
if (state.getMovement().isPendingPoseRestore()) {
|
||||
tryRestoreStandingPose(player, state);
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ public class MovementStyleManager {
|
||||
|
||||
// --- Compare with current active style ---
|
||||
MovementStyle newStyle = resolved.style();
|
||||
MovementStyle oldStyle = state.getActiveMovementStyle();
|
||||
MovementStyle oldStyle = state.getMovement().getActiveMovementStyle();
|
||||
|
||||
if (newStyle != oldStyle) {
|
||||
// Style changed: deactivate old, activate new
|
||||
@@ -161,14 +161,14 @@ public class MovementStyleManager {
|
||||
onDeactivate(player, state, oldStyle);
|
||||
}
|
||||
if (newStyle != null) {
|
||||
state.setResolvedMovementSpeed(resolved.speedMultiplier());
|
||||
state.setResolvedJumpDisabled(resolved.jumpDisabled());
|
||||
state.getMovement().setResolvedMovementSpeed(resolved.speedMultiplier());
|
||||
state.getMovement().setResolvedJumpDisabled(resolved.jumpDisabled());
|
||||
onActivate(player, state, newStyle);
|
||||
} else {
|
||||
state.setResolvedMovementSpeed(1.0f);
|
||||
state.setResolvedJumpDisabled(false);
|
||||
state.getMovement().setResolvedMovementSpeed(1.0f);
|
||||
state.getMovement().setResolvedJumpDisabled(false);
|
||||
}
|
||||
state.setActiveMovementStyle(newStyle);
|
||||
state.getMovement().setActiveMovementStyle(newStyle);
|
||||
|
||||
// Sync to all tracking clients (animation + crawl pose)
|
||||
ModNetwork.sendToAllTrackingAndSelf(
|
||||
@@ -178,13 +178,13 @@ public class MovementStyleManager {
|
||||
}
|
||||
|
||||
// --- Per-style tick ---
|
||||
if (state.getActiveMovementStyle() != null) {
|
||||
if (state.getMovement().getActiveMovementStyle() != null) {
|
||||
// Ladder suspension: skip style tick when on ladder
|
||||
// (ladder movement is controlled by BondageItemRestrictionHandler)
|
||||
if (player.onClimbable()) {
|
||||
state.lastX = player.getX();
|
||||
state.lastY = player.getY();
|
||||
state.lastZ = player.getZ();
|
||||
state.getMovement().setLastX(player.getX());
|
||||
state.getMovement().setLastY(player.getY());
|
||||
state.getMovement().setLastZ(player.getZ());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -192,9 +192,9 @@ public class MovementStyleManager {
|
||||
}
|
||||
|
||||
// Update last position for next tick's movement detection
|
||||
state.lastX = player.getX();
|
||||
state.lastY = player.getY();
|
||||
state.lastZ = player.getZ();
|
||||
state.getMovement().setLastX(player.getX());
|
||||
state.getMovement().setLastY(player.getY());
|
||||
state.getMovement().setLastZ(player.getZ());
|
||||
}
|
||||
|
||||
// ==================== Jump Suppression ====================
|
||||
@@ -216,7 +216,7 @@ public class MovementStyleManager {
|
||||
}
|
||||
|
||||
PlayerBindState state = PlayerBindState.getInstance(player);
|
||||
if (state == null || !state.isResolvedJumpDisabled()) {
|
||||
if (state == null || !state.getMovement().isResolvedJumpDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ public class MovementStyleManager {
|
||||
}
|
||||
|
||||
private static void tickStyle(ServerPlayer player, PlayerBindState state) {
|
||||
switch (state.getActiveMovementStyle()) {
|
||||
switch (state.getMovement().getActiveMovementStyle()) {
|
||||
case WADDLE -> tickWaddle(player, state);
|
||||
case SHUFFLE -> tickShuffle(player, state);
|
||||
case HOP -> tickHop(player, state);
|
||||
@@ -292,7 +292,7 @@ public class MovementStyleManager {
|
||||
player,
|
||||
WADDLE_SPEED_UUID,
|
||||
"tiedup.waddle_speed",
|
||||
state.getResolvedMovementSpeed()
|
||||
state.getMovement().getResolvedMovementSpeed()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ public class MovementStyleManager {
|
||||
player,
|
||||
SHUFFLE_SPEED_UUID,
|
||||
"tiedup.shuffle_speed",
|
||||
state.getResolvedMovementSpeed()
|
||||
state.getMovement().getResolvedMovementSpeed()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -347,11 +347,11 @@ public class MovementStyleManager {
|
||||
player,
|
||||
HOP_SPEED_UUID,
|
||||
"tiedup.hop_speed",
|
||||
state.getResolvedMovementSpeed()
|
||||
state.getMovement().getResolvedMovementSpeed()
|
||||
);
|
||||
state.hopCooldown = 0;
|
||||
state.hopStartupPending = true;
|
||||
state.hopStartupTicks = HOP_STARTUP_DELAY_TICKS;
|
||||
state.getMovement().setHopCooldown(0);
|
||||
state.getMovement().setHopStartupPending(true);
|
||||
state.getMovement().setHopStartupTicks(HOP_STARTUP_DELAY_TICKS);
|
||||
}
|
||||
|
||||
private static void deactivateHop(
|
||||
@@ -359,10 +359,10 @@ public class MovementStyleManager {
|
||||
PlayerBindState state
|
||||
) {
|
||||
removeSpeedModifier(player, HOP_SPEED_UUID);
|
||||
state.hopCooldown = 0;
|
||||
state.hopStartupPending = false;
|
||||
state.hopStartupTicks = 0;
|
||||
state.hopNotMovingTicks = 0;
|
||||
state.getMovement().setHopCooldown(0);
|
||||
state.getMovement().setHopStartupPending(false);
|
||||
state.getMovement().setHopStartupTicks(0);
|
||||
state.getMovement().setHopNotMovingTicks(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,43 +375,44 @@ public class MovementStyleManager {
|
||||
* </ul>
|
||||
*/
|
||||
private static void tickHop(ServerPlayer player, PlayerBindState state) {
|
||||
var mov = state.getMovement();
|
||||
boolean isMoving =
|
||||
player.distanceToSqr(state.lastX, state.lastY, state.lastZ) >
|
||||
player.distanceToSqr(mov.getLastX(), mov.getLastY(), mov.getLastZ()) >
|
||||
MOVEMENT_THRESHOLD_SQ;
|
||||
|
||||
// Decrement cooldown
|
||||
if (state.hopCooldown > 0) {
|
||||
state.hopCooldown--;
|
||||
if (mov.getHopCooldown() > 0) {
|
||||
mov.setHopCooldown(mov.getHopCooldown() - 1);
|
||||
}
|
||||
|
||||
if (isMoving && player.onGround() && state.hopCooldown <= 0) {
|
||||
if (state.hopStartupPending) {
|
||||
if (isMoving && player.onGround() && mov.getHopCooldown() <= 0) {
|
||||
if (mov.isHopStartupPending()) {
|
||||
// Startup delay: decrement and wait (latched: completes even if
|
||||
// player briefly releases input during these 4 ticks)
|
||||
state.hopStartupTicks--;
|
||||
if (state.hopStartupTicks <= 0) {
|
||||
mov.setHopStartupTicks(mov.getHopStartupTicks() - 1);
|
||||
if (mov.getHopStartupTicks() <= 0) {
|
||||
// Startup complete: execute first hop
|
||||
state.hopStartupPending = false;
|
||||
mov.setHopStartupPending(false);
|
||||
executeHop(player, state);
|
||||
}
|
||||
} else {
|
||||
// Normal hop
|
||||
executeHop(player, state);
|
||||
}
|
||||
state.hopNotMovingTicks = 0;
|
||||
mov.setHopNotMovingTicks(0);
|
||||
} else if (!isMoving) {
|
||||
state.hopNotMovingTicks++;
|
||||
mov.setHopNotMovingTicks(mov.getHopNotMovingTicks() + 1);
|
||||
// Reset startup if not moving for >= 2 consecutive ticks
|
||||
if (
|
||||
state.hopNotMovingTicks >= HOP_STARTUP_RESET_TICKS &&
|
||||
!state.hopStartupPending
|
||||
mov.getHopNotMovingTicks() >= HOP_STARTUP_RESET_TICKS &&
|
||||
!mov.isHopStartupPending()
|
||||
) {
|
||||
state.hopStartupPending = true;
|
||||
state.hopStartupTicks = HOP_STARTUP_DELAY_TICKS;
|
||||
mov.setHopStartupPending(true);
|
||||
mov.setHopStartupTicks(HOP_STARTUP_DELAY_TICKS);
|
||||
}
|
||||
} else {
|
||||
// Moving but not on ground or cooldown active — reset not-moving counter
|
||||
state.hopNotMovingTicks = 0;
|
||||
mov.setHopNotMovingTicks(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +432,7 @@ public class MovementStyleManager {
|
||||
currentMotion.z + forward.z * HOP_FORWARD_IMPULSE
|
||||
);
|
||||
|
||||
state.hopCooldown = HOP_COOLDOWN_TICKS;
|
||||
state.getMovement().setHopCooldown(HOP_COOLDOWN_TICKS);
|
||||
|
||||
// Sync velocity to client to prevent rubber-banding
|
||||
player.connection.send(new ClientboundSetEntityMotionPacket(player));
|
||||
@@ -447,11 +448,11 @@ public class MovementStyleManager {
|
||||
player,
|
||||
CRAWL_SPEED_UUID,
|
||||
"tiedup.crawl_speed",
|
||||
state.getResolvedMovementSpeed()
|
||||
state.getMovement().getResolvedMovementSpeed()
|
||||
);
|
||||
player.setForcedPose(Pose.SWIMMING);
|
||||
player.refreshDimensions();
|
||||
state.pendingPoseRestore = false;
|
||||
state.getMovement().setPendingPoseRestore(false);
|
||||
}
|
||||
|
||||
private static void deactivateCrawl(
|
||||
@@ -470,7 +471,7 @@ public class MovementStyleManager {
|
||||
player.refreshDimensions();
|
||||
} else {
|
||||
// Can't stand yet -- flag for periodic retry in tick flow (step 2)
|
||||
state.pendingPoseRestore = true;
|
||||
state.getMovement().setPendingPoseRestore(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,7 +502,7 @@ public class MovementStyleManager {
|
||||
if (canStand) {
|
||||
player.setForcedPose(null);
|
||||
player.refreshDimensions();
|
||||
state.pendingPoseRestore = false;
|
||||
state.getMovement().setPendingPoseRestore(false);
|
||||
LOGGER.debug(
|
||||
"Restored standing pose for {} (pending pose restore cleared)",
|
||||
player.getName().getString()
|
||||
|
||||
74
src/main/java/com/tiedup/remake/worldgen/BlockPalette.java
Normal file
74
src/main/java/com/tiedup/remake/worldgen/BlockPalette.java
Normal file
@@ -0,0 +1,74 @@
|
||||
package com.tiedup.remake.worldgen;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
/**
|
||||
* Weighted random block selection with named condition variants.
|
||||
*
|
||||
* <p>Each palette maps condition names (e.g. "default", "bottom_row", "edge",
|
||||
* "corner", "cap") to a list of weighted block state entries. When a block
|
||||
* is requested for a condition that has no entries, falls back to "default".</p>
|
||||
*/
|
||||
public final class BlockPalette {
|
||||
|
||||
/**
|
||||
* A single block state with its selection weight.
|
||||
*
|
||||
* @param state the block state to place
|
||||
* @param weight relative weight (higher = more likely)
|
||||
*/
|
||||
public record WeightedEntry(BlockState state, float weight) {}
|
||||
|
||||
private final Map<String, List<WeightedEntry>> variants;
|
||||
|
||||
/**
|
||||
* @param variants condition name to weighted entries map (defensively copied)
|
||||
*/
|
||||
public BlockPalette(Map<String, List<WeightedEntry>> variants) {
|
||||
this.variants = Map.copyOf(variants);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a random block state for the given condition.
|
||||
* Falls back to "default" if the condition has no entries.
|
||||
*
|
||||
* @param random the random source
|
||||
* @param condition the condition name (e.g. "default", "bottom_row")
|
||||
* @return a randomly selected block state
|
||||
*/
|
||||
public BlockState pick(RandomSource random, String condition) {
|
||||
List<WeightedEntry> entries = variants.getOrDefault(
|
||||
condition, variants.get("default")
|
||||
);
|
||||
if (entries == null || entries.isEmpty()) {
|
||||
// Should never happen if parsed correctly -- safety fallback
|
||||
return Blocks.STONE.defaultBlockState();
|
||||
}
|
||||
|
||||
float totalWeight = 0;
|
||||
for (WeightedEntry e : entries) {
|
||||
totalWeight += e.weight();
|
||||
}
|
||||
|
||||
float roll = random.nextFloat() * totalWeight;
|
||||
float cumulative = 0;
|
||||
for (WeightedEntry e : entries) {
|
||||
cumulative += e.weight();
|
||||
if (roll < cumulative) return e.state();
|
||||
}
|
||||
|
||||
// Floating-point edge case fallback
|
||||
return entries.get(entries.size() - 1).state();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return unmodifiable map of all condition variants
|
||||
*/
|
||||
public Map<String, List<WeightedEntry>> variants() {
|
||||
return variants;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.tiedup.remake.worldgen;
|
||||
|
||||
import java.util.List;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Configuration for decorative blocks placed in a room theme.
|
||||
*
|
||||
* <p>Captures corner decorations, wall midpoint blocks, optional special
|
||||
* first-corner placement, furniture cluster, and lighting/chain flags.</p>
|
||||
*
|
||||
* @param cornerDecorations blocks placed at room corners (each with a Y offset)
|
||||
* @param wallMidpointBlocks blocks placed at wall midpoints
|
||||
* @param firstCornerSpecial optional special block for the first corner only
|
||||
* @param furnitureCluster blocks placed as a furniture group
|
||||
* @param useTorchLighting whether to place torches for lighting
|
||||
* @param hasCeilingChain whether to place ceiling chains
|
||||
*/
|
||||
public record DecorationConfig(
|
||||
List<PositionedBlock> cornerDecorations,
|
||||
List<PositionedBlock> wallMidpointBlocks,
|
||||
@Nullable PositionedBlock firstCornerSpecial,
|
||||
List<PositionedBlock> furnitureCluster,
|
||||
boolean useTorchLighting,
|
||||
boolean hasCeilingChain
|
||||
) {
|
||||
|
||||
/**
|
||||
* A block state with positional offsets for placement.
|
||||
*
|
||||
* @param state the block state to place
|
||||
* @param xOffset horizontal X offset from base position (0 = no offset)
|
||||
* @param yOffset vertical offset from the base position
|
||||
* @param zOffset horizontal Z offset from base position (0 = no offset)
|
||||
*/
|
||||
public record PositionedBlock(BlockState state, int xOffset, int yOffset, int zOffset) {
|
||||
/** Convenience constructor for Y-offset-only blocks (most common case). */
|
||||
public PositionedBlock(BlockState state, int yOffset) {
|
||||
this(state, 0, yOffset, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.ChestBlock;
|
||||
import net.minecraft.world.level.block.LanternBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
@@ -279,7 +280,7 @@ public class HangingCagePiece extends StructurePiece {
|
||||
|
||||
/**
|
||||
* Fallback: carve a 13x13x12 dungeon room and place the cage inside.
|
||||
* Randomly selects a theme (Oubliette/Inferno) and layout (Square/Octagonal).
|
||||
* Randomly selects a data-driven theme and layout (Square/Octagonal).
|
||||
*/
|
||||
private void carveAndPlaceRoom(
|
||||
WorldGenLevel level,
|
||||
@@ -288,9 +289,7 @@ public class HangingCagePiece extends StructurePiece {
|
||||
int centerZ,
|
||||
BoundingBox chunkBB
|
||||
) {
|
||||
RoomTheme theme = RoomTheme.values()[random.nextInt(
|
||||
RoomTheme.values().length
|
||||
)];
|
||||
RoomThemeDefinition theme = RoomThemeRegistry.pickRandomOrFallback(random);
|
||||
RoomLayout layout = RoomLayout.values()[random.nextInt(
|
||||
RoomLayout.values().length
|
||||
)];
|
||||
@@ -358,62 +357,24 @@ public class HangingCagePiece extends StructurePiece {
|
||||
}
|
||||
}
|
||||
|
||||
theme.placeDecorations(
|
||||
level,
|
||||
random,
|
||||
baseX,
|
||||
baseZ,
|
||||
floorY,
|
||||
layout,
|
||||
chunkBB
|
||||
placeThemeDecorations(
|
||||
level, random, baseX, baseZ, floorY, layout, chunkBB, theme
|
||||
);
|
||||
|
||||
RoomTheme.placeSharedPillars(
|
||||
level,
|
||||
random,
|
||||
baseX,
|
||||
baseZ,
|
||||
floorY,
|
||||
layout,
|
||||
theme,
|
||||
chunkBB
|
||||
placeSharedPillars(
|
||||
level, random, baseX, baseZ, floorY, layout, theme, chunkBB
|
||||
);
|
||||
RoomTheme.placeSharedFloorScatter(
|
||||
level,
|
||||
random,
|
||||
baseX,
|
||||
baseZ,
|
||||
floorY,
|
||||
layout,
|
||||
theme,
|
||||
chunkBB
|
||||
placeSharedFloorScatter(
|
||||
level, random, baseX, baseZ, floorY, layout, theme, chunkBB
|
||||
);
|
||||
RoomTheme.placeSharedCeilingDecor(
|
||||
level,
|
||||
random,
|
||||
baseX,
|
||||
baseZ,
|
||||
floorY,
|
||||
layout,
|
||||
chunkBB
|
||||
placeSharedCeilingDecor(
|
||||
level, random, baseX, baseZ, floorY, layout, chunkBB
|
||||
);
|
||||
RoomTheme.placeSharedWallLighting(
|
||||
level,
|
||||
random,
|
||||
baseX,
|
||||
baseZ,
|
||||
floorY,
|
||||
layout,
|
||||
chunkBB
|
||||
placeSharedWallLighting(
|
||||
level, random, baseX, baseZ, floorY, layout, chunkBB
|
||||
);
|
||||
RoomTheme.placeSharedWallBands(
|
||||
level,
|
||||
baseX,
|
||||
baseZ,
|
||||
floorY,
|
||||
layout,
|
||||
theme,
|
||||
chunkBB
|
||||
placeSharedWallBands(
|
||||
level, baseX, baseZ, floorY, layout, theme, chunkBB
|
||||
);
|
||||
|
||||
placeVanillaChest(level, random, baseX, baseZ, floorY, layout, chunkBB);
|
||||
@@ -451,7 +412,7 @@ public class HangingCagePiece extends StructurePiece {
|
||||
|
||||
TiedUpMod.LOGGER.info(
|
||||
"[HangingCage] Placed cage in carved room (theme={}, layout={}) at {}",
|
||||
theme.name(),
|
||||
theme.id(),
|
||||
layout.name(),
|
||||
masterPos.toShortString()
|
||||
);
|
||||
@@ -655,4 +616,331 @@ public class HangingCagePiece extends StructurePiece {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Data-driven theme decorations ───────────────────────────────
|
||||
|
||||
/**
|
||||
* Place decorations from the data-driven {@link DecorationConfig}.
|
||||
* Replaces the old per-enum {@code RoomTheme.placeDecorations()} method.
|
||||
*/
|
||||
private static void placeThemeDecorations(
|
||||
WorldGenLevel level,
|
||||
RandomSource random,
|
||||
int baseX,
|
||||
int baseZ,
|
||||
int floorY,
|
||||
RoomLayout layout,
|
||||
BoundingBox chunkBB,
|
||||
RoomThemeDefinition theme
|
||||
) {
|
||||
DecorationConfig deco = theme.decorations();
|
||||
int[][] corners = layout.innerCorners();
|
||||
|
||||
// Corner decorations (e.g. cobwebs low+high, soul fire, snow layers)
|
||||
// Note: only y_offset is used here — x/z offsets are ignored because corners
|
||||
// are placed at all 4 inner corners and direction logic would differ per corner.
|
||||
for (DecorationConfig.PositionedBlock pb : deco.cornerDecorations()) {
|
||||
for (int[] c : corners) {
|
||||
if (layout.isInShape(c[0], c[1]) && !layout.isWall(c[0], c[1])) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + c[0], floorY + pb.yOffset(), baseZ + c[1]),
|
||||
pb.state(), chunkBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wall midpoint decorations (e.g. soul lanterns, crying obsidian, sculk veins)
|
||||
int[][] wallMidpoints = {{6, 1}, {6, 11}, {1, 6}, {11, 6}};
|
||||
for (DecorationConfig.PositionedBlock pb : deco.wallMidpointBlocks()) {
|
||||
for (int[] wp : wallMidpoints) {
|
||||
if (layout.isWall(wp[0], wp[1])) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + wp[0], floorY + pb.yOffset(), baseZ + wp[1]),
|
||||
pb.state(), chunkBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// First corner special (e.g. water cauldron, skull, TNT, sculk catalyst)
|
||||
// x/z offsets are multiplied by inward direction (toward room center)
|
||||
if (deco.firstCornerSpecial() != null && corners.length > 0) {
|
||||
int[] sc = corners[0];
|
||||
int dirX = sc[0] < 6 ? 1 : -1;
|
||||
int dirZ = sc[1] < 6 ? 1 : -1;
|
||||
int scx = sc[0] + deco.firstCornerSpecial().xOffset() * dirX;
|
||||
int scz = sc[1] + deco.firstCornerSpecial().zOffset() * dirZ;
|
||||
if (layout.isInShape(scx, scz) && !layout.isWall(scx, scz)) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + scx, floorY + deco.firstCornerSpecial().yOffset(), baseZ + scz),
|
||||
deco.firstCornerSpecial().state(), chunkBB);
|
||||
}
|
||||
}
|
||||
|
||||
// Furniture cluster at corners[1] — each item has x/z offsets relative to the
|
||||
// base furniture position. x_offset/z_offset values are multiplied by the
|
||||
// inward direction (toward room center) to handle all 4 corner orientations.
|
||||
if (corners.length > 1) {
|
||||
int[] fc = corners[1];
|
||||
int dirX = fc[0] < 6 ? 1 : -1;
|
||||
int dirZ = fc[1] < 6 ? 1 : -1;
|
||||
int fcx = fc[0] + dirX;
|
||||
int fcz = fc[1] + dirZ;
|
||||
for (DecorationConfig.PositionedBlock pb : deco.furnitureCluster()) {
|
||||
int px = fcx + pb.xOffset() * dirX;
|
||||
int pz = fcz + pb.zOffset() * dirZ;
|
||||
if (layout.isInShape(px, pz) && !layout.isWall(px, pz)) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + px, floorY + pb.yOffset(), baseZ + pz),
|
||||
pb.state(), chunkBB);
|
||||
}
|
||||
}
|
||||
|
||||
// Ceiling chain above furniture
|
||||
if (deco.hasCeilingChain()) {
|
||||
for (int cy = 8; cy <= 10; cy++) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + fcx, floorY + cy, baseZ + fcz),
|
||||
Blocks.CHAIN.defaultBlockState(), chunkBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Torch lighting (wall torches at outer wall midpoints) -- used by Crypt, Sandstone
|
||||
if (deco.useTorchLighting()) {
|
||||
for (int[] wallPos : new int[][] {{6, 0}, {6, 12}, {0, 6}, {12, 6}}) {
|
||||
if (layout.isWall(wallPos[0], wallPos[1])) {
|
||||
Direction torchDir = getTorchDirection(wallPos[0], wallPos[1]);
|
||||
if (torchDir != null) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + wallPos[0], floorY + 3, baseZ + wallPos[1]),
|
||||
Blocks.WALL_TORCH.defaultBlockState().setValue(
|
||||
net.minecraft.world.level.block.WallTorchBlock.FACING, torchDir),
|
||||
chunkBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Side chains + hanging lanterns (shared by all themes)
|
||||
placeSharedChains(level, baseX, baseZ, floorY, chunkBB);
|
||||
placeSharedHangingLanterns(level, baseX, baseZ, floorY, chunkBB);
|
||||
}
|
||||
|
||||
// ── Shared structural features (moved from RoomTheme) ──────────
|
||||
|
||||
/** Pillar positions -- verified to be inside all 4 layouts. */
|
||||
private static final int[][] PILLAR_POSITIONS = {
|
||||
{4, 4}, {4, 8}, {8, 4}, {8, 8},
|
||||
};
|
||||
|
||||
/** Place 4 full-height pillars at verified positions. */
|
||||
private static void placeSharedPillars(
|
||||
WorldGenLevel level,
|
||||
RandomSource random,
|
||||
int baseX,
|
||||
int baseZ,
|
||||
int floorY,
|
||||
RoomLayout layout,
|
||||
RoomThemeDefinition theme,
|
||||
BoundingBox chunkBB
|
||||
) {
|
||||
for (int[] p : PILLAR_POSITIONS) {
|
||||
if (!layout.isInShape(p[0], p[1]) || layout.isWall(p[0], p[1])) continue;
|
||||
for (int ry = 1; ry <= 10; ry++) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + p[0], floorY + ry, baseZ + p[1]),
|
||||
theme.pillarBlock(random, ry), chunkBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Place random floor scatter (~12% of interior positions). */
|
||||
private static void placeSharedFloorScatter(
|
||||
WorldGenLevel level,
|
||||
RandomSource random,
|
||||
int baseX,
|
||||
int baseZ,
|
||||
int floorY,
|
||||
RoomLayout layout,
|
||||
RoomThemeDefinition theme,
|
||||
BoundingBox chunkBB
|
||||
) {
|
||||
for (int rx = 1; rx <= 11; rx++) {
|
||||
for (int rz = 1; rz <= 11; rz++) {
|
||||
if (!layout.isInShape(rx, rz) || layout.isWall(rx, rz)) continue;
|
||||
// Skip pillar positions
|
||||
if ((rx == 4 || rx == 8) && (rz == 4 || rz == 8)) continue;
|
||||
// Skip cage center area (5-7, 5-7)
|
||||
if (rx >= 5 && rx <= 7 && rz >= 5 && rz <= 7) continue;
|
||||
// Skip corner positions (used by decorations/chests)
|
||||
if ((rx <= 2 || rx >= 10) && (rz <= 2 || rz >= 10)) continue;
|
||||
if (random.nextFloat() < 0.12f) {
|
||||
BlockState scatter = theme.scatterBlock(random);
|
||||
if (scatter != null) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + rx, floorY + 1, baseZ + rz),
|
||||
scatter, chunkBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Place ceiling cobwebs and extra hanging chains. */
|
||||
private static void placeSharedCeilingDecor(
|
||||
WorldGenLevel level,
|
||||
RandomSource random,
|
||||
int baseX,
|
||||
int baseZ,
|
||||
int floorY,
|
||||
RoomLayout layout,
|
||||
BoundingBox chunkBB
|
||||
) {
|
||||
// Cobwebs along walls at ceiling level
|
||||
int[][] cobwebCandidates = {
|
||||
{2, 1}, {4, 1}, {8, 1}, {10, 1},
|
||||
{2, 11}, {4, 11}, {8, 11}, {10, 11},
|
||||
{1, 4}, {1, 8}, {11, 4}, {11, 8},
|
||||
};
|
||||
for (int[] pos : cobwebCandidates) {
|
||||
if (layout.isInShape(pos[0], pos[1])
|
||||
&& !layout.isWall(pos[0], pos[1])
|
||||
&& random.nextFloat() < 0.45f) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + pos[0], floorY + 10, baseZ + pos[1]),
|
||||
Blocks.COBWEB.defaultBlockState(), chunkBB);
|
||||
}
|
||||
}
|
||||
// Extra hanging chains at random interior positions
|
||||
int[][] chainCandidates = {{5, 3}, {7, 9}, {3, 7}};
|
||||
for (int[] pos : chainCandidates) {
|
||||
if (layout.isInShape(pos[0], pos[1])
|
||||
&& !layout.isWall(pos[0], pos[1])
|
||||
&& random.nextFloat() < 0.6f) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + pos[0], floorY + 10, baseZ + pos[1]),
|
||||
Blocks.CHAIN.defaultBlockState(), chunkBB);
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + pos[0], floorY + 9, baseZ + pos[1]),
|
||||
Blocks.CHAIN.defaultBlockState(), chunkBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Place additional wall-mounted lighting. */
|
||||
private static void placeSharedWallLighting(
|
||||
WorldGenLevel level,
|
||||
RandomSource random,
|
||||
int baseX,
|
||||
int baseZ,
|
||||
int floorY,
|
||||
RoomLayout layout,
|
||||
BoundingBox chunkBB
|
||||
) {
|
||||
// Lanterns on pillar sides (facing center) at ry=1 (on the floor)
|
||||
int[][] pillarLanternPositions = {
|
||||
{5, 4}, {4, 7}, {8, 5}, {7, 8},
|
||||
};
|
||||
for (int[] pos : pillarLanternPositions) {
|
||||
if (layout.isInShape(pos[0], pos[1])
|
||||
&& !layout.isWall(pos[0], pos[1])) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + pos[0], floorY + 1, baseZ + pos[1]),
|
||||
Blocks.LANTERN.defaultBlockState(), chunkBB);
|
||||
}
|
||||
}
|
||||
// Extra wall sconces at quarter-points
|
||||
int[][] wallSconces = {
|
||||
{4, 0}, {8, 0}, {4, 12}, {8, 12},
|
||||
{0, 4}, {0, 8}, {12, 4}, {12, 8},
|
||||
};
|
||||
for (int[] pos : wallSconces) {
|
||||
if (layout.isWall(pos[0], pos[1]) && random.nextFloat() < 0.5f) {
|
||||
Direction torchDir = getTorchDirection(pos[0], pos[1]);
|
||||
if (torchDir != null) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + pos[0], floorY + 3, baseZ + pos[1]),
|
||||
Blocks.WALL_TORCH.defaultBlockState().setValue(
|
||||
net.minecraft.world.level.block.WallTorchBlock.FACING, torchDir),
|
||||
chunkBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Place wall accent bands at ry=5 and ry=8. */
|
||||
private static void placeSharedWallBands(
|
||||
WorldGenLevel level,
|
||||
int baseX,
|
||||
int baseZ,
|
||||
int floorY,
|
||||
RoomLayout layout,
|
||||
RoomThemeDefinition theme,
|
||||
BoundingBox chunkBB
|
||||
) {
|
||||
int[][] bandPositions = {
|
||||
{6, 1}, {6, 11}, {1, 6}, {11, 6},
|
||||
{4, 1}, {8, 1}, {4, 11}, {8, 11},
|
||||
{1, 4}, {1, 8}, {11, 4}, {11, 8},
|
||||
};
|
||||
for (int[] pos : bandPositions) {
|
||||
if (layout.isWall(pos[0], pos[1])) {
|
||||
// Band at ry=5
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + pos[0], floorY + 5, baseZ + pos[1]),
|
||||
theme.wallAccentBlock(), chunkBB);
|
||||
// Optional band at ry=8
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + pos[0], floorY + 8, baseZ + pos[1]),
|
||||
theme.wallAccentBlock(), chunkBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Chains on cage flanks and ceiling corners -- shared by all themes. */
|
||||
private static void placeSharedChains(
|
||||
WorldGenLevel level,
|
||||
int baseX,
|
||||
int baseZ,
|
||||
int floorY,
|
||||
BoundingBox chunkBB
|
||||
) {
|
||||
for (int[] chainPos : new int[][] {{3, 6}, {9, 6}}) {
|
||||
for (int ry = 5; ry <= 10; ry++) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + chainPos[0], floorY + ry, baseZ + chainPos[1]),
|
||||
Blocks.CHAIN.defaultBlockState(), chunkBB);
|
||||
}
|
||||
}
|
||||
for (int[] chainPos : new int[][] {{3, 3}, {3, 9}, {9, 3}, {9, 9}}) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + chainPos[0], floorY + 10, baseZ + chainPos[1]),
|
||||
Blocks.CHAIN.defaultBlockState(), chunkBB);
|
||||
}
|
||||
}
|
||||
|
||||
/** Determine torch facing direction for a wall position (torch faces inward). */
|
||||
private static Direction getTorchDirection(int rx, int rz) {
|
||||
if (rz == 0) return Direction.SOUTH;
|
||||
if (rz == 12) return Direction.NORTH;
|
||||
if (rx == 0) return Direction.EAST;
|
||||
if (rx == 12) return Direction.WEST;
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Hanging lanterns -- shared by all themes. */
|
||||
private static void placeSharedHangingLanterns(
|
||||
WorldGenLevel level,
|
||||
int baseX,
|
||||
int baseZ,
|
||||
int floorY,
|
||||
BoundingBox chunkBB
|
||||
) {
|
||||
for (int[] pos : new int[][] {{3, 3}, {9, 9}}) {
|
||||
safeSetBlock(level,
|
||||
new BlockPos(baseX + pos[0], floorY + 9, baseZ + pos[1]),
|
||||
Blocks.LANTERN.defaultBlockState().setValue(LanternBlock.HANGING, true),
|
||||
chunkBB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
package com.tiedup.remake.worldgen;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Immutable data-driven room theme definition, replacing the old {@link RoomTheme} enum.
|
||||
*
|
||||
* <p>Loaded from {@code data/<namespace>/tiedup_room_themes/*.json} and stored
|
||||
* in {@link RoomThemeRegistry}. Convenience methods mirror the old
|
||||
* abstract API so that {@code HangingCagePiece} can swap with minimal changes.</p>
|
||||
*
|
||||
* @param id unique identifier (e.g. "tiedup:oubliette")
|
||||
* @param weight random selection weight (higher = more likely)
|
||||
* @param wallPalette palette for wall blocks (supports "default" and "bottom_row")
|
||||
* @param floorPalette palette for floor blocks (supports "default", "edge", "corner")
|
||||
* @param ceilingPalette palette for ceiling blocks
|
||||
* @param wallShellBlock single block used for the outer wall shell
|
||||
* @param wallAccentBlock single accent block (e.g. for frames, trims)
|
||||
* @param pillarPalette palette for pillars (supports "default" and "cap")
|
||||
* @param scatterPalette optional palette for scatter decorations (cobwebs, etc.)
|
||||
* @param decorations decoration configuration (corners, midpoints, furniture, etc.)
|
||||
*/
|
||||
public record RoomThemeDefinition(
|
||||
ResourceLocation id,
|
||||
int weight,
|
||||
BlockPalette wallPalette,
|
||||
BlockPalette floorPalette,
|
||||
BlockPalette ceilingPalette,
|
||||
BlockState wallShellBlock,
|
||||
BlockState wallAccentBlock,
|
||||
BlockPalette pillarPalette,
|
||||
@Nullable BlockPalette scatterPalette,
|
||||
DecorationConfig decorations
|
||||
) {
|
||||
|
||||
/**
|
||||
* Pick a wall block. Uses "bottom_row" condition for ry==1, "default" otherwise.
|
||||
*/
|
||||
public BlockState wallBlock(RandomSource random, int ry) {
|
||||
return wallPalette.pick(random, ry == 1 ? "bottom_row" : "default");
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a floor block. Uses "corner" for corner positions, "edge" for edges,
|
||||
* "default" for interior positions.
|
||||
*/
|
||||
public BlockState floorBlock(RandomSource random, int rx, int rz, boolean isEdge) {
|
||||
boolean isCorner = (rx == 1 || rx == 11) && (rz == 1 || rz == 11);
|
||||
return floorPalette.pick(
|
||||
random, isCorner ? "corner" : isEdge ? "edge" : "default"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a ceiling block (always "default" condition).
|
||||
*/
|
||||
public BlockState ceilingBlock(RandomSource random) {
|
||||
return ceilingPalette.pick(random, "default");
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a pillar block. Uses "cap" for top/bottom rows, "default" for the shaft.
|
||||
*/
|
||||
public BlockState pillarBlock(RandomSource random, int ry) {
|
||||
return pillarPalette.pick(random, (ry == 1 || ry == 10) ? "cap" : "default");
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a scatter block, or null if no scatter palette is defined.
|
||||
*/
|
||||
@Nullable
|
||||
public BlockState scatterBlock(RandomSource random) {
|
||||
return scatterPalette != null ? scatterPalette.pick(random, "default") : null;
|
||||
}
|
||||
}
|
||||
496
src/main/java/com/tiedup/remake/worldgen/RoomThemeParser.java
Normal file
496
src/main/java/com/tiedup/remake/worldgen/RoomThemeParser.java
Normal file
@@ -0,0 +1,496 @@
|
||||
package com.tiedup.remake.worldgen;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.minecraft.commands.arguments.blocks.BlockStateParser;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Parses JSON files into {@link RoomThemeDefinition} instances.
|
||||
*
|
||||
* <p>Uses manual field extraction (not Gson deserialization) for strict
|
||||
* validation control. Invalid required fields cause the entire definition
|
||||
* to be rejected; optional fields use safe defaults.</p>
|
||||
*
|
||||
* <p>Expected JSON files in {@code data/<namespace>/tiedup_room_themes/}.</p>
|
||||
*/
|
||||
public final class RoomThemeParser {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("TiedUpWorldgen");
|
||||
private static final String TAG = "[RoomThemeParser]";
|
||||
|
||||
private RoomThemeParser() {}
|
||||
|
||||
/**
|
||||
* Parse a JSON input stream into a RoomThemeDefinition.
|
||||
*
|
||||
* @param input the JSON input stream
|
||||
* @param fileId the resource location derived from the file path
|
||||
* @return the parsed definition, or null if the file is invalid
|
||||
*/
|
||||
@Nullable
|
||||
public static RoomThemeDefinition parse(InputStream input, ResourceLocation fileId) {
|
||||
try {
|
||||
JsonObject root = JsonParser.parseReader(
|
||||
new InputStreamReader(input, StandardCharsets.UTF_8)
|
||||
).getAsJsonObject();
|
||||
|
||||
return parseRoot(root, fileId);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("{} Failed to parse JSON {}: {}", TAG, fileId, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the root JSON object into a RoomThemeDefinition.
|
||||
*/
|
||||
@Nullable
|
||||
private static RoomThemeDefinition parseRoot(JsonObject root, ResourceLocation fileId) {
|
||||
// --- Required: weight (default 10 if missing, clamped [1, 1000]) ---
|
||||
int weight = clampInt(getIntOrDefault(root, "weight", 10), 1, 1000);
|
||||
|
||||
// --- Required: wall_palette ---
|
||||
BlockPalette wallPalette = parsePaletteField(root, "wall_palette", fileId);
|
||||
if (wallPalette == null) {
|
||||
LOGGER.error("{} Skipping {}: missing or invalid 'wall_palette'", TAG, fileId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Required: floor_palette ---
|
||||
BlockPalette floorPalette = parsePaletteField(root, "floor_palette", fileId);
|
||||
if (floorPalette == null) {
|
||||
LOGGER.error("{} Skipping {}: missing or invalid 'floor_palette'", TAG, fileId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Required: ceiling_palette ---
|
||||
BlockPalette ceilingPalette = parsePaletteField(root, "ceiling_palette", fileId);
|
||||
if (ceilingPalette == null) {
|
||||
LOGGER.error("{} Skipping {}: missing or invalid 'ceiling_palette'", TAG, fileId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Required: wall_shell (single block string) ---
|
||||
BlockState wallShell = parseBlockStateField(root, "wall_shell", fileId);
|
||||
if (wallShell == null) {
|
||||
LOGGER.error("{} Skipping {}: missing or invalid 'wall_shell'", TAG, fileId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Required: wall_accent (single block string) ---
|
||||
BlockState wallAccent = parseBlockStateField(root, "wall_accent", fileId);
|
||||
if (wallAccent == null) {
|
||||
LOGGER.error("{} Skipping {}: missing or invalid 'wall_accent'", TAG, fileId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Required: pillar_palette ---
|
||||
BlockPalette pillarPalette = parsePaletteField(root, "pillar_palette", fileId);
|
||||
if (pillarPalette == null) {
|
||||
LOGGER.error("{} Skipping {}: missing or invalid 'pillar_palette'", TAG, fileId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// --- Optional: scatter_palette ---
|
||||
BlockPalette scatterPalette = null;
|
||||
if (root.has("scatter_palette") && root.get("scatter_palette").isJsonObject()) {
|
||||
scatterPalette = parsePaletteField(root, "scatter_palette", fileId);
|
||||
// If scatter_palette is present but invalid, warn but don't reject
|
||||
if (scatterPalette == null) {
|
||||
LOGGER.warn(
|
||||
"{} In {}: 'scatter_palette' present but invalid, ignoring", TAG, fileId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Optional: decorations ---
|
||||
DecorationConfig decorations = new DecorationConfig(
|
||||
List.of(), List.of(), null, List.of(), false, false
|
||||
);
|
||||
if (root.has("decorations") && root.get("decorations").isJsonObject()) {
|
||||
DecorationConfig parsed = parseDecorations(
|
||||
root.getAsJsonObject("decorations"), fileId
|
||||
);
|
||||
if (parsed != null) {
|
||||
decorations = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return new RoomThemeDefinition(
|
||||
fileId, weight,
|
||||
wallPalette, floorPalette, ceilingPalette,
|
||||
wallShell, wallAccent,
|
||||
pillarPalette, scatterPalette,
|
||||
decorations
|
||||
);
|
||||
}
|
||||
|
||||
// ===== Palette Parsing =====
|
||||
|
||||
/**
|
||||
* Parse a palette field from the root object. The field value must be a JSON object
|
||||
* mapping condition names to arrays of weighted entries.
|
||||
*
|
||||
* <p>Example:
|
||||
* <pre>{
|
||||
* "default": [{"block": "minecraft:stone_bricks", "weight": 0.8}],
|
||||
* "bottom_row": [{"block": "minecraft:mossy_stone_bricks", "weight": 1.0}]
|
||||
* }</pre>
|
||||
*/
|
||||
@Nullable
|
||||
private static BlockPalette parsePaletteField(
|
||||
JsonObject root, String fieldName, ResourceLocation fileId
|
||||
) {
|
||||
if (!root.has(fieldName) || !root.get(fieldName).isJsonObject()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JsonObject paletteObj = root.getAsJsonObject(fieldName);
|
||||
Map<String, List<BlockPalette.WeightedEntry>> variants = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, JsonElement> condEntry : paletteObj.entrySet()) {
|
||||
String condition = condEntry.getKey();
|
||||
if (!condEntry.getValue().isJsonArray()) {
|
||||
LOGGER.warn(
|
||||
"{} In {} palette '{}': condition '{}' is not an array, skipping",
|
||||
TAG, fileId, fieldName, condition
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
List<BlockPalette.WeightedEntry> entries = parseWeightedEntries(
|
||||
condEntry.getValue().getAsJsonArray(), fileId, fieldName, condition
|
||||
);
|
||||
if (!entries.isEmpty()) {
|
||||
variants.put(condition, List.copyOf(entries));
|
||||
}
|
||||
}
|
||||
|
||||
if (variants.isEmpty() || !variants.containsKey("default")) {
|
||||
LOGGER.warn(
|
||||
"{} In {} palette '{}': no valid 'default' condition found",
|
||||
TAG, fileId, fieldName
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new BlockPalette(variants);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an array of weighted block entries for a single palette condition.
|
||||
*
|
||||
* <p>Each entry is a JSON object with:
|
||||
* <ul>
|
||||
* <li>{@code "block"} — block state string (e.g. "minecraft:candle[lit=true]")</li>
|
||||
* <li>{@code "weight"} — float weight (default 1.0)</li>
|
||||
* <li>{@code "random_property"} — optional object with {@code "name"}, {@code "min"}, {@code "max"}
|
||||
* to expand a single entry into multiple entries with varying integer property values</li>
|
||||
* </ul>
|
||||
*/
|
||||
private static List<BlockPalette.WeightedEntry> parseWeightedEntries(
|
||||
JsonArray array, ResourceLocation fileId, String paletteName, String condition
|
||||
) {
|
||||
List<BlockPalette.WeightedEntry> entries = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
if (!array.get(i).isJsonObject()) {
|
||||
LOGGER.warn(
|
||||
"{} In {} palette '{}' condition '{}': entry [{}] is not an object, skipping",
|
||||
TAG, fileId, paletteName, condition, i
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonObject entryObj = array.get(i).getAsJsonObject();
|
||||
|
||||
String blockStr = getStringOrNull(entryObj, "block");
|
||||
if (blockStr == null || blockStr.isEmpty()) {
|
||||
LOGGER.warn(
|
||||
"{} In {} palette '{}' condition '{}': entry [{}] missing 'block', skipping",
|
||||
TAG, fileId, paletteName, condition, i
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockState baseState = parseBlockState(blockStr, fileId);
|
||||
if (baseState == null) {
|
||||
continue; // parseBlockState already logged warning
|
||||
}
|
||||
|
||||
float weight = getFloatOrDefault(entryObj, "weight", 1.0f);
|
||||
if (weight <= 0) {
|
||||
LOGGER.warn(
|
||||
"{} In {} palette '{}' condition '{}': entry [{}] has non-positive weight, skipping",
|
||||
TAG, fileId, paletteName, condition, i
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle random_property expansion
|
||||
if (entryObj.has("random_property") && entryObj.get("random_property").isJsonObject()) {
|
||||
List<BlockPalette.WeightedEntry> expanded = expandRandomProperty(
|
||||
baseState, weight, entryObj.getAsJsonObject("random_property"),
|
||||
fileId, paletteName, condition, i
|
||||
);
|
||||
entries.addAll(expanded);
|
||||
} else {
|
||||
entries.add(new BlockPalette.WeightedEntry(baseState, weight));
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a single weighted entry into multiple entries by varying an integer
|
||||
* block state property across a range.
|
||||
*
|
||||
* <p>For example, {@code candle[candles=1]} with {@code random_property: {name: "candles", min: 1, max: 3}}
|
||||
* and weight 0.30 produces three entries: candles=1 (0.10), candles=2 (0.10), candles=3 (0.10).</p>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<BlockPalette.WeightedEntry> expandRandomProperty(
|
||||
BlockState baseState, float totalWeight, JsonObject propObj,
|
||||
ResourceLocation fileId, String paletteName, String condition, int entryIndex
|
||||
) {
|
||||
String propName = getStringOrNull(propObj, "name");
|
||||
if (propName == null) {
|
||||
LOGGER.warn(
|
||||
"{} In {} palette '{}' condition '{}': entry [{}] random_property missing 'name', using base state",
|
||||
TAG, fileId, paletteName, condition, entryIndex
|
||||
);
|
||||
return List.of(new BlockPalette.WeightedEntry(baseState, totalWeight));
|
||||
}
|
||||
|
||||
int min = getIntOrDefault(propObj, "min", 1);
|
||||
int max = getIntOrDefault(propObj, "max", min);
|
||||
if (max < min) {
|
||||
LOGGER.warn(
|
||||
"{} In {} palette '{}' condition '{}': entry [{}] random_property max < min, using base state",
|
||||
TAG, fileId, paletteName, condition, entryIndex
|
||||
);
|
||||
return List.of(new BlockPalette.WeightedEntry(baseState, totalWeight));
|
||||
}
|
||||
|
||||
Property<?> property = baseState.getBlock().getStateDefinition().getProperty(propName);
|
||||
if (!(property instanceof IntegerProperty intProp)) {
|
||||
LOGGER.warn(
|
||||
"{} In {} palette '{}' condition '{}': entry [{}] property '{}' is not an IntegerProperty, using base state",
|
||||
TAG, fileId, paletteName, condition, entryIndex, propName
|
||||
);
|
||||
return List.of(new BlockPalette.WeightedEntry(baseState, totalWeight));
|
||||
}
|
||||
|
||||
int count = max - min + 1;
|
||||
float perWeight = totalWeight / count;
|
||||
List<BlockPalette.WeightedEntry> expanded = new ArrayList<>(count);
|
||||
|
||||
for (int val = min; val <= max; val++) {
|
||||
if (intProp.getPossibleValues().contains(val)) {
|
||||
expanded.add(new BlockPalette.WeightedEntry(
|
||||
baseState.setValue(intProp, val), perWeight
|
||||
));
|
||||
} else {
|
||||
LOGGER.warn(
|
||||
"{} In {} palette '{}' condition '{}': entry [{}] property '{}' does not accept value {}, skipping",
|
||||
TAG, fileId, paletteName, condition, entryIndex, propName, val
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (expanded.isEmpty()) {
|
||||
return List.of(new BlockPalette.WeightedEntry(baseState, totalWeight));
|
||||
}
|
||||
|
||||
return expanded;
|
||||
}
|
||||
|
||||
// ===== Block State Parsing =====
|
||||
|
||||
/**
|
||||
* Parse a block state string like "minecraft:stone_bricks" or "minecraft:candle[lit=true]"
|
||||
* using Minecraft's built-in BlockStateParser.
|
||||
*
|
||||
* @return the parsed BlockState, or null on failure (logged as warning)
|
||||
*/
|
||||
@Nullable
|
||||
private static BlockState parseBlockState(String blockString, ResourceLocation fileId) {
|
||||
try {
|
||||
return BlockStateParser.parseForBlock(
|
||||
BuiltInRegistries.BLOCK.asLookup(),
|
||||
blockString,
|
||||
false // don't allow NBT
|
||||
).blockState();
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn(
|
||||
"{} In {}: invalid block state '{}': {}",
|
||||
TAG, fileId, blockString, e.getMessage()
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single block state from a string field in a JSON object.
|
||||
*
|
||||
* @return the parsed BlockState, or null if field is missing or invalid
|
||||
*/
|
||||
@Nullable
|
||||
private static BlockState parseBlockStateField(
|
||||
JsonObject obj, String fieldName, ResourceLocation fileId
|
||||
) {
|
||||
String blockStr = getStringOrNull(obj, fieldName);
|
||||
if (blockStr == null || blockStr.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return parseBlockState(blockStr, fileId);
|
||||
}
|
||||
|
||||
// ===== Decoration Parsing =====
|
||||
|
||||
/**
|
||||
* Parse the "decorations" JSON object into a DecorationConfig.
|
||||
*/
|
||||
@Nullable
|
||||
private static DecorationConfig parseDecorations(JsonObject obj, ResourceLocation fileId) {
|
||||
try {
|
||||
List<DecorationConfig.PositionedBlock> cornerDecos = parsePositionedBlockList(
|
||||
obj, "corner_decorations", fileId
|
||||
);
|
||||
List<DecorationConfig.PositionedBlock> wallMidpoints = parsePositionedBlockList(
|
||||
obj, "wall_midpoint_blocks", fileId
|
||||
);
|
||||
|
||||
DecorationConfig.PositionedBlock firstCornerSpecial = null;
|
||||
if (obj.has("first_corner_special") && obj.get("first_corner_special").isJsonObject()) {
|
||||
firstCornerSpecial = parsePositionedBlock(
|
||||
obj.getAsJsonObject("first_corner_special"), fileId
|
||||
);
|
||||
}
|
||||
|
||||
List<DecorationConfig.PositionedBlock> furnitureCluster = parsePositionedBlockList(
|
||||
obj, "furniture_cluster", fileId
|
||||
);
|
||||
|
||||
boolean useTorchLighting = getBooleanOrDefault(obj, "use_torch_lighting", false);
|
||||
boolean hasCeilingChain = getBooleanOrDefault(obj, "has_ceiling_chain", false);
|
||||
|
||||
return new DecorationConfig(
|
||||
cornerDecos, wallMidpoints, firstCornerSpecial,
|
||||
furnitureCluster, useTorchLighting, hasCeilingChain
|
||||
);
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn(
|
||||
"{} In {}: failed to parse decorations: {}", TAG, fileId, e.getMessage()
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an array of positioned blocks from a field.
|
||||
*/
|
||||
private static List<DecorationConfig.PositionedBlock> parsePositionedBlockList(
|
||||
JsonObject parent, String fieldName, ResourceLocation fileId
|
||||
) {
|
||||
if (!parent.has(fieldName) || !parent.get(fieldName).isJsonArray()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
JsonArray array = parent.getAsJsonArray(fieldName);
|
||||
List<DecorationConfig.PositionedBlock> result = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
if (!array.get(i).isJsonObject()) continue;
|
||||
|
||||
DecorationConfig.PositionedBlock pb = parsePositionedBlock(
|
||||
array.get(i).getAsJsonObject(), fileId
|
||||
);
|
||||
if (pb != null) {
|
||||
result.add(pb);
|
||||
}
|
||||
}
|
||||
|
||||
return List.copyOf(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single positioned block: { "block": "...", "x_offset": 0, "y_offset": 0, "z_offset": 0 }
|
||||
*/
|
||||
@Nullable
|
||||
private static DecorationConfig.PositionedBlock parsePositionedBlock(
|
||||
JsonObject obj, ResourceLocation fileId
|
||||
) {
|
||||
String blockStr = getStringOrNull(obj, "block");
|
||||
if (blockStr == null || blockStr.isEmpty()) return null;
|
||||
|
||||
BlockState state = parseBlockState(blockStr, fileId);
|
||||
if (state == null) return null;
|
||||
|
||||
int xOffset = getIntOrDefault(obj, "x_offset", 0);
|
||||
int yOffset = getIntOrDefault(obj, "y_offset", 0);
|
||||
int zOffset = getIntOrDefault(obj, "z_offset", 0);
|
||||
return new DecorationConfig.PositionedBlock(state, xOffset, yOffset, zOffset);
|
||||
}
|
||||
|
||||
// ===== Primitive Helpers =====
|
||||
|
||||
@Nullable
|
||||
private static String getStringOrNull(JsonObject obj, String key) {
|
||||
if (!obj.has(key) || obj.get(key).isJsonNull()) return null;
|
||||
try {
|
||||
return obj.get(key).getAsString();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getIntOrDefault(JsonObject obj, String key, int defaultValue) {
|
||||
if (!obj.has(key) || obj.get(key).isJsonNull()) return defaultValue;
|
||||
try {
|
||||
return obj.get(key).getAsInt();
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static float getFloatOrDefault(JsonObject obj, String key, float defaultValue) {
|
||||
if (!obj.has(key) || obj.get(key).isJsonNull()) return defaultValue;
|
||||
try {
|
||||
return obj.get(key).getAsFloat();
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean getBooleanOrDefault(JsonObject obj, String key, boolean defaultValue) {
|
||||
if (!obj.has(key) || obj.get(key).isJsonNull()) return defaultValue;
|
||||
try {
|
||||
return obj.get(key).getAsBoolean();
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static int clampInt(int value, int min, int max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
}
|
||||
142
src/main/java/com/tiedup/remake/worldgen/RoomThemeRegistry.java
Normal file
142
src/main/java/com/tiedup/remake/worldgen/RoomThemeRegistry.java
Normal file
@@ -0,0 +1,142 @@
|
||||
package com.tiedup.remake.worldgen;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Thread-safe registry for data-driven room theme definitions.
|
||||
*
|
||||
* <p>Server-authoritative: definitions are loaded from {@code data/<namespace>/tiedup_room_themes/}
|
||||
* JSON files by the server reload listener. Unlike furniture, there is no client sync
|
||||
* because room themes are only used during server-side worldgen.</p>
|
||||
*
|
||||
* <p>Uses volatile atomic swap to ensure threads always see a consistent snapshot.</p>
|
||||
*
|
||||
* @see RoomThemeDefinition
|
||||
*/
|
||||
public final class RoomThemeRegistry {
|
||||
|
||||
/**
|
||||
* Volatile reference to an unmodifiable map. {@link #reload} builds a new map
|
||||
* and swaps atomically; consumer threads always see a consistent snapshot.
|
||||
*/
|
||||
private static volatile Map<
|
||||
ResourceLocation,
|
||||
RoomThemeDefinition
|
||||
> DEFINITIONS = Map.of();
|
||||
|
||||
/** Hardcoded fallback for when the registry is empty (worldgen safety). */
|
||||
private static final RoomThemeDefinition FALLBACK = createFallback();
|
||||
|
||||
private RoomThemeRegistry() {}
|
||||
|
||||
/**
|
||||
* Atomically replace all definitions with a new set.
|
||||
* Called by the reload listener after parsing all JSON files.
|
||||
*
|
||||
* @param newDefs the new definitions map (will be defensively copied)
|
||||
*/
|
||||
public static void reload(Map<ResourceLocation, RoomThemeDefinition> newDefs) {
|
||||
DEFINITIONS = Collections.unmodifiableMap(new HashMap<>(newDefs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a definition by its unique ID.
|
||||
*
|
||||
* @param id the definition ID (e.g. "tiedup:oubliette")
|
||||
* @return the definition, or null if not found
|
||||
*/
|
||||
@Nullable
|
||||
public static RoomThemeDefinition get(ResourceLocation id) {
|
||||
return DEFINITIONS.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered definitions.
|
||||
*
|
||||
* @return unmodifiable collection of all definitions
|
||||
*/
|
||||
public static Collection<RoomThemeDefinition> getAll() {
|
||||
return DEFINITIONS.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a random theme proportionally by weight.
|
||||
*
|
||||
* @param random the random source
|
||||
* @return a randomly selected theme, or null if registry is empty
|
||||
*/
|
||||
@Nullable
|
||||
public static RoomThemeDefinition pickRandom(RandomSource random) {
|
||||
Collection<RoomThemeDefinition> all = DEFINITIONS.values();
|
||||
if (all.isEmpty()) return null;
|
||||
|
||||
int totalWeight = 0;
|
||||
for (RoomThemeDefinition def : all) {
|
||||
totalWeight += def.weight();
|
||||
}
|
||||
|
||||
if (totalWeight <= 0) return null;
|
||||
|
||||
int roll = random.nextInt(totalWeight);
|
||||
int cumulative = 0;
|
||||
for (RoomThemeDefinition def : all) {
|
||||
cumulative += def.weight();
|
||||
if (roll < cumulative) return def;
|
||||
}
|
||||
|
||||
// Fallback (should not happen)
|
||||
return all.iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a random theme, or return a hardcoded stone bricks fallback if the
|
||||
* registry is empty. Guarantees non-null for worldgen safety.
|
||||
*
|
||||
* @param random the random source
|
||||
* @return a theme definition (never null)
|
||||
*/
|
||||
public static RoomThemeDefinition pickRandomOrFallback(RandomSource random) {
|
||||
RoomThemeDefinition picked = pickRandom(random);
|
||||
return picked != null ? picked : FALLBACK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all definitions. Called on world unload or for testing.
|
||||
*/
|
||||
public static void clear() {
|
||||
DEFINITIONS = Map.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a minimal stone bricks fallback theme for when no JSON themes are loaded.
|
||||
*/
|
||||
private static RoomThemeDefinition createFallback() {
|
||||
BlockState stone = Blocks.STONE_BRICKS.defaultBlockState();
|
||||
BlockPalette single = new BlockPalette(
|
||||
Map.of("default", List.of(new BlockPalette.WeightedEntry(stone, 1.0f)))
|
||||
);
|
||||
return new RoomThemeDefinition(
|
||||
new ResourceLocation("tiedup", "fallback"),
|
||||
1,
|
||||
single, // wall
|
||||
single, // floor
|
||||
single, // ceiling
|
||||
stone, // wallShell
|
||||
stone, // wallAccent
|
||||
single, // pillar
|
||||
null, // no scatter
|
||||
new DecorationConfig(
|
||||
List.of(), List.of(), null, List.of(), false, false
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.tiedup.remake.worldgen;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.Resource;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.server.packs.resources.SimplePreparableReloadListener;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Server-side resource reload listener that scans {@code data/<namespace>/tiedup_room_themes/}
|
||||
* for JSON files and populates the {@link RoomThemeRegistry}.
|
||||
*
|
||||
* <p>Room themes are server-authoritative only (used during worldgen). Unlike furniture,
|
||||
* there is no client sync packet -- the registry is atomically replaced via
|
||||
* {@link RoomThemeRegistry#reload(Map)} on each reload.</p>
|
||||
*
|
||||
* <p>Registered via {@link net.minecraftforge.event.AddReloadListenerEvent} in
|
||||
* {@link com.tiedup.remake.core.TiedUpMod.ForgeEvents}.</p>
|
||||
*/
|
||||
public class RoomThemeReloadListener
|
||||
extends SimplePreparableReloadListener<Void>
|
||||
{
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("TiedUpWorldgen");
|
||||
|
||||
/** Resource directory containing room theme definition JSON files (under data/). */
|
||||
private static final String DIRECTORY = "tiedup_room_themes";
|
||||
|
||||
@Override
|
||||
protected Void prepare(
|
||||
ResourceManager resourceManager,
|
||||
ProfilerFiller profiler
|
||||
) {
|
||||
// No preparation needed -- parsing happens in apply phase
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(
|
||||
Void nothing,
|
||||
ResourceManager resourceManager,
|
||||
ProfilerFiller profiler
|
||||
) {
|
||||
Map<ResourceLocation, RoomThemeDefinition> newDefs = new HashMap<>();
|
||||
|
||||
Map<ResourceLocation, Resource> resources =
|
||||
resourceManager.listResources(DIRECTORY, loc ->
|
||||
loc.getPath().endsWith(".json")
|
||||
);
|
||||
|
||||
int skipped = 0;
|
||||
|
||||
for (Map.Entry<ResourceLocation, Resource> entry : resources.entrySet()) {
|
||||
ResourceLocation fileId = entry.getKey();
|
||||
Resource resource = entry.getValue();
|
||||
|
||||
// Derive clean ID from file path: "tiedup:tiedup_room_themes/oubliette.json" -> "tiedup:oubliette"
|
||||
String rawPath = fileId.getPath();
|
||||
String cleanPath = rawPath.substring(rawPath.indexOf(DIRECTORY + "/") + DIRECTORY.length() + 1);
|
||||
if (cleanPath.endsWith(".json")) {
|
||||
cleanPath = cleanPath.substring(0, cleanPath.length() - 5);
|
||||
}
|
||||
ResourceLocation cleanId = new ResourceLocation(fileId.getNamespace(), cleanPath);
|
||||
|
||||
try (InputStream input = resource.open()) {
|
||||
RoomThemeDefinition def = RoomThemeParser.parse(input, cleanId);
|
||||
|
||||
if (def != null) {
|
||||
// Check for duplicate IDs (fileId IS the ID for room themes)
|
||||
if (newDefs.containsKey(def.id())) {
|
||||
LOGGER.warn(
|
||||
"[TiedUpWorldgen] Server: Duplicate room theme ID '{}' from file '{}' -- overwriting previous definition",
|
||||
def.id(),
|
||||
fileId
|
||||
);
|
||||
}
|
||||
|
||||
newDefs.put(def.id(), def);
|
||||
LOGGER.debug(
|
||||
"[TiedUpWorldgen] Server loaded room theme: {}",
|
||||
def.id()
|
||||
);
|
||||
} else {
|
||||
skipped++;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(
|
||||
"[TiedUpWorldgen] Server: Failed to read resource {}: {}",
|
||||
fileId,
|
||||
e.getMessage()
|
||||
);
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
|
||||
// Atomically replace all definitions in the registry
|
||||
RoomThemeRegistry.reload(newDefs);
|
||||
|
||||
LOGGER.info(
|
||||
"[TiedUpWorldgen] Server loaded {} room theme definitions ({} skipped) from {} JSON files",
|
||||
newDefs.size(),
|
||||
skipped,
|
||||
resources.size()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -282,6 +282,8 @@
|
||||
"command.tiedup.clothes.unknown_layer": "Unknown layer: %s",
|
||||
"command.tiedup.clothes.layer_visible": "Wearer's %s layer is now visible",
|
||||
"command.tiedup.clothes.layer_hidden": "Wearer's %s layer is now hidden",
|
||||
"command.tiedup.clothes.keephead_enabled": "Keep head mode enabled",
|
||||
"command.tiedup.clothes.keephead_disabled": "Keep head mode disabled",
|
||||
|
||||
"gui.tiedup.command_wand.title": "Command Wand",
|
||||
"gui.tiedup.command_wand.personality": "Personality",
|
||||
@@ -400,7 +402,6 @@
|
||||
|
||||
"gui.tiedup.slave_trader": "Slave Trader",
|
||||
"gui.tiedup.buy": "Buy",
|
||||
"gui.tiedup.close": "Close",
|
||||
"gui.tiedup.no_captives_available": "No captives available for sale",
|
||||
|
||||
"gui.tiedup.cell_manager": "Cell Manager",
|
||||
@@ -687,5 +688,280 @@
|
||||
"gui.tiedup.status.key_info": "Key: %s",
|
||||
"gui.tiedup.status.no_key": "No key",
|
||||
"gui.tiedup.status.target_info": "Target: %s",
|
||||
"gui.tiedup.status_bar": "Status bar"
|
||||
"gui.tiedup.status_bar": "Status bar",
|
||||
|
||||
"msg.tiedup.system.being_tied": "%1$s is tying you up!",
|
||||
"msg.tiedup.system.tied_up": "%1$s tied you up, you can't move!",
|
||||
"msg.tiedup.system.being_gagged": "%1$s is gagging you!",
|
||||
"msg.tiedup.system.gagged": "%1$s gagged you, you can't speak!",
|
||||
"msg.tiedup.system.being_blindfolded": "%1$s is blindfolding you!",
|
||||
"msg.tiedup.system.blindfolded": "%1$s blindfolded you, you can't see!",
|
||||
"msg.tiedup.system.being_collared": "%1$s is putting a collar on you!",
|
||||
"msg.tiedup.system.collared": "%1$s collared you!",
|
||||
"msg.tiedup.system.earplugs_on": "%1$s put earplugs on you!",
|
||||
"msg.tiedup.system.mittens_on": "%1$s put mittens on you!",
|
||||
"msg.tiedup.system.enslaved": "You have been enslaved by %1$s!",
|
||||
"msg.tiedup.system.tying_target": "You are tying %1$s...",
|
||||
"msg.tiedup.system.tied_target": "You tied %1$s!",
|
||||
"msg.tiedup.system.gagging_target": "You are gagging %1$s...",
|
||||
"msg.tiedup.system.gagged_target": "You gagged %1$s!",
|
||||
"msg.tiedup.system.blindfolding_target": "You are blindfolding %1$s...",
|
||||
"msg.tiedup.system.blindfolded_target": "You blindfolded %1$s!",
|
||||
"msg.tiedup.system.collaring_target": "You are collaring %1$s...",
|
||||
"msg.tiedup.system.collared_target": "You collared %1$s!",
|
||||
"msg.tiedup.system.untied": "%1$s untied you!",
|
||||
"msg.tiedup.system.ungagged": "%1$s removed your gag!",
|
||||
"msg.tiedup.system.unblindfolded": "%1$s removed your blindfold!",
|
||||
"msg.tiedup.system.uncollared": "%1$s removed your collar!",
|
||||
"msg.tiedup.system.freed": "You have been freed!",
|
||||
"msg.tiedup.system.struggle_success": "You feel the ropes loosening...",
|
||||
"msg.tiedup.system.struggle_fail": "You struggle against the ropes, but they hold tight.",
|
||||
"msg.tiedup.system.struggle_broke_free": "You broke free!",
|
||||
"msg.tiedup.system.struggle_shocked": "You were shocked for struggling!",
|
||||
"msg.tiedup.system.struggle_collar_success": "You manage to damage the lock!",
|
||||
"msg.tiedup.system.struggle_collar_fail": "You try to reach the lock, but can't get a good grip.",
|
||||
"msg.tiedup.system.cant_move": "You can't move while tied!",
|
||||
"msg.tiedup.system.cant_attack_tied": "You can't attack while tied!",
|
||||
"msg.tiedup.system.cant_use_item_tied": "You can't use items while tied!",
|
||||
"msg.tiedup.system.cant_open_inventory": "You can't open inventory while tied!",
|
||||
"msg.tiedup.system.cant_interact_tied": "You can't interact while tied!",
|
||||
"msg.tiedup.system.cant_speak": "You can't speak while gagged!",
|
||||
"msg.tiedup.system.cant_see": "You can't see while blindfolded!",
|
||||
"msg.tiedup.system.cant_break_tied": "You can't break blocks while tied!",
|
||||
"msg.tiedup.system.cant_place_tied": "You can't place blocks while tied!",
|
||||
"msg.tiedup.system.no_elytra": "You can't fly with elytra while tied!",
|
||||
"msg.tiedup.system.cant_attack_mittens": "You can't attack with mittens on!",
|
||||
"msg.tiedup.system.cant_use_item_mittens": "You can't use items with mittens on!",
|
||||
"msg.tiedup.system.cant_interact_mittens": "You can't interact with mittens on!",
|
||||
"msg.tiedup.system.cant_break_mittens": "You can't break blocks with mittens on!",
|
||||
"msg.tiedup.system.cant_place_mittens": "You can't place blocks with mittens on!",
|
||||
"msg.tiedup.system.slave_command": "Your master commands: %1$s",
|
||||
"msg.tiedup.system.slave_shock": "You've been shocked!",
|
||||
"msg.tiedup.system.gps_zone_violation": "You've been shocked! Return back to your allowed area!",
|
||||
"msg.tiedup.system.gps_owner_alert": "ALERT: %1$s is outside the safe zone!",
|
||||
"msg.tiedup.system.slave_job_assigned": "Job assigned: bring %1$s",
|
||||
"msg.tiedup.system.slave_job_complete": "Job complete! You are free.",
|
||||
"msg.tiedup.system.slave_job_failed": "Job failed!",
|
||||
"msg.tiedup.system.slave_job_last_chance": "LAST CHANCE! Next failure means death!",
|
||||
"msg.tiedup.system.slave_job_killed": "You were executed for failing your task.",
|
||||
"msg.tiedup.system.binds_tightened": "%1$s tightened your binds!",
|
||||
"msg.tiedup.system.key_claimed": "Key claimed and linked to %1$s!",
|
||||
"msg.tiedup.system.key_not_owner": "You don't own this key!",
|
||||
"msg.tiedup.system.key_wrong_target": "This key doesn't fit this collar!",
|
||||
"msg.tiedup.system.locator_claimed": "Locator claimed!",
|
||||
"msg.tiedup.system.locator_not_owner": "You don't own this locator!",
|
||||
"msg.tiedup.system.locator_detected": "Target detected: %1$s",
|
||||
"msg.tiedup.system.shocker_claimed": "Shocker claimed!",
|
||||
"msg.tiedup.system.shocker_not_owner": "You don't own this shocker!",
|
||||
"msg.tiedup.system.shocker_mode_set": "Shocker mode: %1$s",
|
||||
"msg.tiedup.system.shocker_triggered": "Shocked %1$s!",
|
||||
"msg.tiedup.system.rag_dry": "The rag is dry - soak it first",
|
||||
"msg.tiedup.system.rag_soaked": "You soaked the rag with chloroform",
|
||||
"msg.tiedup.system.rag_evaporated": "The chloroform has evaporated",
|
||||
"msg.tiedup.system.bounty_created": "Bounty created on %1$s!",
|
||||
"msg.tiedup.system.bounty_claimed": "You claimed the bounty on %1$s!",
|
||||
"msg.tiedup.system.bounty_expired": "Bounty on %1$s expired",
|
||||
"msg.tiedup.system.prisoner_arrived": "%1$s has been placed in your cell",
|
||||
"msg.tiedup.system.prisoner_escaped": "%1$s has escaped from your cell!",
|
||||
"msg.tiedup.system.prisoner_released": "%1$s has been released from your cell",
|
||||
"msg.tiedup.system.cell_breach": "Your cell wall has been breached!",
|
||||
"msg.tiedup.system.cell_assigned": "You have been assigned to %1$s's cell",
|
||||
"msg.tiedup.system.cell_created": "Cell created successfully",
|
||||
"msg.tiedup.system.cell_deleted": "Cell deleted",
|
||||
"msg.tiedup.system.cell_renamed": "Cell renamed to: %1$s",
|
||||
"msg.tiedup.system.info": "%1$s",
|
||||
"msg.tiedup.system.warning": "%1$s",
|
||||
"msg.tiedup.system.error": "%1$s",
|
||||
"msg.tiedup.system.resistance_suffix": " (Resistance: %1$s)",
|
||||
|
||||
"command.tiedup.error.no_state": "Failed to get player state",
|
||||
|
||||
"command.tiedup.bind.already_tied": "%1$s is already tied up",
|
||||
"command.tiedup.bind.tied": "%1$s has been tied up",
|
||||
"command.tiedup.bind.not_restrained": "%1$s is not restrained",
|
||||
"command.tiedup.bind.freed": "%1$s has been freed from all restraints",
|
||||
|
||||
"command.tiedup.gag.already_gagged": "%1$s is already gagged",
|
||||
"command.tiedup.gag.gagged": "%1$s has been gagged",
|
||||
"command.tiedup.gag.not_gagged": "%1$s is not gagged",
|
||||
"command.tiedup.gag.removed": "%1$s's gag has been removed",
|
||||
|
||||
"command.tiedup.blindfold.already_blindfolded": "%1$s is already blindfolded",
|
||||
"command.tiedup.blindfold.blindfolded": "%1$s has been blindfolded",
|
||||
"command.tiedup.blindfold.not_blindfolded": "%1$s is not blindfolded",
|
||||
"command.tiedup.blindfold.removed": "%1$s's blindfold has been removed",
|
||||
|
||||
"command.tiedup.collar.already_collared": "%1$s already has a collar",
|
||||
"command.tiedup.collar.collared": "%1$s has been collared",
|
||||
"command.tiedup.collar.no_collar": "%1$s does not have a collar",
|
||||
"command.tiedup.collar.removed": "%1$s's collar has been removed",
|
||||
"command.tiedup.collar.enslaved": "%1$s has been enslaved",
|
||||
"command.tiedup.collar.not_captured": "%1$s is not captured",
|
||||
"command.tiedup.collar.freed": "%1$s has been freed from slavery",
|
||||
|
||||
"command.tiedup.accessory.already_earplugs": "%1$s already has earplugs",
|
||||
"command.tiedup.accessory.earplugs_on": "%1$s has been given earplugs",
|
||||
"command.tiedup.accessory.no_earplugs": "%1$s does not have earplugs",
|
||||
"command.tiedup.accessory.earplugs_removed": "%1$s's earplugs have been removed",
|
||||
"command.tiedup.accessory.already_clothes": "%1$s already has clothes",
|
||||
"command.tiedup.accessory.clothes_on": "%1$s has been given clothes",
|
||||
"command.tiedup.accessory.no_clothes": "%1$s is not wearing clothes",
|
||||
"command.tiedup.accessory.clothes_removed": "Removed clothes from %1$s",
|
||||
"command.tiedup.accessory.already_restrained": "%1$s is already fully restrained",
|
||||
"command.tiedup.accessory.fully_restrained": "%1$s has been fully restrained (%2$s items applied)",
|
||||
"command.tiedup.accessory.adjust_invalid_type": "Invalid type. Use: gag, blindfold, or all",
|
||||
"command.tiedup.accessory.nothing_to_adjust": "%1$s has no %2$s to adjust",
|
||||
"command.tiedup.accessory.adjusted": "Adjusted %1$s for %2$s to %3$s pixels",
|
||||
|
||||
"command.tiedup.error.must_be_player": "Must be a player",
|
||||
"command.tiedup.yes": "Yes",
|
||||
"command.tiedup.no": "No",
|
||||
|
||||
"command.tiedup.npc.spawned_kidnapper": "Spawned Kidnapper at %1$s",
|
||||
"command.tiedup.npc.spawned_elite": "Spawned Elite Kidnapper '%1$s' at %2$s",
|
||||
"command.tiedup.npc.spawned_archer": "Spawned Archer Kidnapper at %1$s",
|
||||
"command.tiedup.npc.spawned_damsel": "Spawned Damsel at %1$s",
|
||||
"command.tiedup.npc.spawn_failed_kidnapper": "Failed to spawn Kidnapper",
|
||||
"command.tiedup.npc.spawn_failed_elite": "Failed to spawn Elite Kidnapper",
|
||||
"command.tiedup.npc.spawn_failed_archer": "Failed to spawn Archer Kidnapper",
|
||||
"command.tiedup.npc.spawn_failed_damsel": "Failed to spawn Damsel",
|
||||
"command.tiedup.npc.unknown_variant": "Unknown elite variant: %1$s. Available: suki, carol, athena, evelyn",
|
||||
"command.tiedup.npc.killed": "Killed %1$s mod NPCs in radius %2$s",
|
||||
"command.tiedup.npc.no_npc_nearby": "No mod NPC found within 10 blocks",
|
||||
"command.tiedup.npc.already_tied": "NPC is already tied up",
|
||||
"command.tiedup.npc.tied": "Tied up %1$s",
|
||||
"command.tiedup.npc.already_gagged": "NPC is already gagged",
|
||||
"command.tiedup.npc.gagged": "Gagged %1$s",
|
||||
"command.tiedup.npc.already_blindfolded": "NPC is already blindfolded",
|
||||
"command.tiedup.npc.blindfolded": "Blindfolded %1$s",
|
||||
"command.tiedup.npc.already_collared": "NPC already has a collar",
|
||||
"command.tiedup.npc.collared": "Collared %1$s",
|
||||
"command.tiedup.npc.untied": "Untied %1$s",
|
||||
"command.tiedup.npc.fully_restrained": "Fully restrained %1$s",
|
||||
"command.tiedup.npc.state_header": "=== NPC State: %1$s ===",
|
||||
"command.tiedup.npc.state_variant": "Variant: %1$s",
|
||||
"command.tiedup.npc.state_slim_arms": "Slim Arms: %1$s",
|
||||
"command.tiedup.npc.state_tied": "Tied Up: %1$s",
|
||||
"command.tiedup.npc.state_gagged": "Gagged: %1$s",
|
||||
"command.tiedup.npc.state_blindfolded": "Blindfolded: %1$s",
|
||||
"command.tiedup.npc.state_collar": "Has Collar: %1$s",
|
||||
"command.tiedup.npc.state_earplugs": "Has Earplugs: %1$s",
|
||||
"command.tiedup.npc.state_captive": "Is Captive: %1$s",
|
||||
|
||||
"command.tiedup.cell.no_selection": "No cell selected. Use the Admin Wand on a Cell Core first.",
|
||||
"command.tiedup.cell.no_longer_exists": "Selected cell no longer exists",
|
||||
"command.tiedup.cell.name_exists": "Cell name '%1$s' already exists",
|
||||
"command.tiedup.cell.named": "Named cell '%1$s' and linked to you",
|
||||
"command.tiedup.cell.none_registered": "No cells registered",
|
||||
"command.tiedup.cell.list_header": "=== Cells (%1$s) ===",
|
||||
"command.tiedup.cell.no_cells_for_owner": "%1$s has no cells",
|
||||
"command.tiedup.cell.list_owner_header": "=== Cells owned by %1$s (%2$s) ===",
|
||||
"command.tiedup.cell.not_found": "Cell '%1$s' not found",
|
||||
"command.tiedup.cell.deleted": "Deleted cell '%1$s'",
|
||||
"command.tiedup.cell.reset_spawns": "Reset %1$s spawn markers (found %2$s total spawn markers in %3$s block radius)",
|
||||
"command.tiedup.cell.reset_spawns_hint": "You can now save the structure - NPCs will spawn when it's placed.",
|
||||
"command.tiedup.cell.info_header": "=== Cell: %1$s ===",
|
||||
"command.tiedup.cell.info_id": "ID: %1$s",
|
||||
"command.tiedup.cell.info_state": "State: %1$s",
|
||||
"command.tiedup.cell.info_core_pos": "Core Position: %1$s",
|
||||
"command.tiedup.cell.info_spawn_point": "Spawn Point: %1$s",
|
||||
"command.tiedup.cell.info_owner": "Owner: %1$s (%2$s)",
|
||||
"command.tiedup.cell.info_owner_world": "Owner: (world-generated)",
|
||||
"command.tiedup.cell.info_interior": "Interior blocks: %1$s",
|
||||
"command.tiedup.cell.info_walls": "Wall blocks: %1$s",
|
||||
"command.tiedup.cell.info_breaches": "Breaches: %1$s (%2$s%%)",
|
||||
"command.tiedup.cell.info_beds": "Beds: %1$s",
|
||||
"command.tiedup.cell.info_anchors": "Anchors: %1$s",
|
||||
"command.tiedup.cell.info_doors": "Doors: %1$s",
|
||||
"command.tiedup.cell.info_prisoners": "Prisoners: %1$s/4",
|
||||
|
||||
"command.tiedup.social.cannot_block_self": "You cannot block yourself",
|
||||
"command.tiedup.social.already_blocked": "%1$s is already blocked",
|
||||
"command.tiedup.social.blocked": "Blocked %1$s",
|
||||
"command.tiedup.social.not_blocked": "%1$s is not blocked",
|
||||
"command.tiedup.social.unblocked": "Unblocked %1$s",
|
||||
"command.tiedup.social.has_blocked_you": "%1$s has blocked you",
|
||||
"command.tiedup.social.has_not_blocked_you": "%1$s has not blocked you",
|
||||
"command.tiedup.social.norp_cooldown": "Please wait %1$s seconds before using /norp again",
|
||||
"command.tiedup.social.norp_prefix": "[NoRP] ",
|
||||
"command.tiedup.social.norp_announcement": " has announced non-consent to current RP",
|
||||
"command.tiedup.social.pm_blocked": "This player has blocked you",
|
||||
"command.tiedup.social.pm_from": "[PM from %1$s] ",
|
||||
"command.tiedup.social.pm_to": "[PM to %1$s] ",
|
||||
"command.tiedup.social.talkarea_disabled": "Talk area disabled (global chat)",
|
||||
"command.tiedup.social.talkarea_set": "Talk area set to %1$s blocks",
|
||||
"command.tiedup.social.talkinfo_disabled": "Talk area: disabled (global chat)",
|
||||
"command.tiedup.social.talkinfo_distance": "Talk area: %1$s blocks",
|
||||
|
||||
"command.tiedup.collar_cmd.no_collar": "%1$s does not have a collar",
|
||||
"command.tiedup.collar_cmd.claimed": "Claimed %1$s's collar",
|
||||
"command.tiedup.collar_cmd.claim_failed": "Failed to claim collar",
|
||||
"command.tiedup.collar_cmd.unclaimed": "Removed your ownership from %1$s's collar",
|
||||
"command.tiedup.collar_cmd.unclaim_failed": "Failed to unclaim collar",
|
||||
"command.tiedup.collar_cmd.renamed": "Set collar nickname to '%1$s'",
|
||||
"command.tiedup.collar_cmd.owner_added": "Added %1$s as owner of %2$s's collar",
|
||||
"command.tiedup.collar_cmd.owner_removed": "Removed %1$s as owner of %2$s's collar",
|
||||
"command.tiedup.collar_cmd.cell_not_found": "Cell '%1$s' not found",
|
||||
"command.tiedup.collar_cmd.cell_assigned": "Assigned cell '%1$s' to %2$s's collar",
|
||||
"command.tiedup.collar_cmd.no_cell_assigned": "No cell assigned to collar",
|
||||
"command.tiedup.collar_cmd.cell_deleted": "Assigned cell no longer exists",
|
||||
"command.tiedup.collar_cmd.teleported": "Teleported %1$s to cell at %2$s",
|
||||
"command.tiedup.collar_cmd.info_header": "=== Collar Info for %1$s ===",
|
||||
"command.tiedup.collar_cmd.info_nickname": "Nickname: %1$s",
|
||||
"command.tiedup.collar_cmd.info_has_owner": "Has Owner: %1$s",
|
||||
"command.tiedup.collar_cmd.info_cell": "Assigned Cell: %1$s @ %2$s",
|
||||
"command.tiedup.collar_cmd.info_cell_deleted": "Assigned Cell: (deleted)",
|
||||
"command.tiedup.collar_cmd.info_cell_none": "Assigned Cell: None",
|
||||
"command.tiedup.collar_cmd.info_locked": "Locked: %1$s",
|
||||
|
||||
"command.tiedup.key.must_hold_key": "You must hold a collar key",
|
||||
"command.tiedup.key.already_claimed": "This key is already claimed by someone else",
|
||||
"command.tiedup.key.claimed": "You have claimed this key",
|
||||
"command.tiedup.key.not_claimed": "This key is not claimed",
|
||||
"command.tiedup.key.not_owner": "You do not own this key",
|
||||
"command.tiedup.key.unclaimed": "You have unclaimed this key",
|
||||
"command.tiedup.key.assigned": "Assigned key to %1$s",
|
||||
"command.tiedup.key.now_public": "Key is now public",
|
||||
"command.tiedup.key.now_private": "Key is now private",
|
||||
"command.tiedup.key.info_header": "=== Key Info ===",
|
||||
"command.tiedup.key.info_owner": "Owner: %1$s",
|
||||
"command.tiedup.key.info_assigned": "Assigned to: %1$s",
|
||||
"command.tiedup.key.info_public": "Public: %1$s",
|
||||
|
||||
"command.tiedup.bounty.cannot_self": "You cannot put a bounty on yourself!",
|
||||
"command.tiedup.bounty.tied_up": "You cannot create bounties while tied up!",
|
||||
"command.tiedup.bounty.max_reached": "Maximum number (%1$s) of active bounties reached!",
|
||||
"command.tiedup.bounty.must_hold_item": "You must hold an item as the reward!",
|
||||
"command.tiedup.bounty.created": "Bounty created on %1$s!",
|
||||
"command.tiedup.bounty.broadcast": "[Bounty] %1$s has put a bounty on %2$s!",
|
||||
|
||||
"command.tiedup.kidnapset.gave": "Gave kidnap set (%1$s item stacks)",
|
||||
"command.tiedup.kidnapreload.reloaded": "Reloaded %1$s (%2$s files)",
|
||||
|
||||
"command.tiedup.debug.prisoner_header": "=== Captivity Debug Info ===",
|
||||
"command.tiedup.debug.error": "Error: %1$s",
|
||||
"command.tiedup.debug.validate_checking": "Checking captivity system...",
|
||||
"command.tiedup.debug.repair_simplified": "Repair functionality has been simplified with the new PrisonerManager system.",
|
||||
"command.tiedup.debug.repair_auto": "The new system maintains consistency automatically.",
|
||||
"command.tiedup.debug.camp_not_found": "No camp found with ID prefix: %1$s",
|
||||
|
||||
"command.tiedup.inventory.no_confiscated": "%1$s has no confiscated inventory to restore",
|
||||
"command.tiedup.inventory.restored": "Restored confiscated inventory to %1$s",
|
||||
"command.tiedup.inventory.restore_failed": "Failed to restore inventory for %1$s",
|
||||
|
||||
"command.tiedup.testanim.playing": "Playing animation '%1$s' on %2$s",
|
||||
"command.tiedup.testanim.stopped": "Stopped animation on %1$s",
|
||||
|
||||
"command.tiedup.master.spawn_failed": "Failed to create Master entity",
|
||||
"command.tiedup.master.spawned": "Spawned Master '%1$s' — you are now their pet.",
|
||||
"command.tiedup.master.no_master_nearby": "No Master NPC found within 20 blocks",
|
||||
"command.tiedup.master.forced_state": "Forced %1$s into %2$s state",
|
||||
"command.tiedup.master.unknown_state": "Unknown MasterState: %1$s",
|
||||
|
||||
"command.tiedup.debt.no_record": "%1$s has no debt record.",
|
||||
"command.tiedup.debt.show": "%1$s — Debt: %2$s | Paid: %3$s | Remaining: %4$s emeralds",
|
||||
"command.tiedup.debt.set": "Set %1$s's total debt to %2$s emeralds.",
|
||||
"command.tiedup.debt.added": "Added %1$s emeralds to %2$s's debt. Remaining: %3$s",
|
||||
"command.tiedup.debt.removed": "Removed %1$s emeralds from %2$s's debt. Remaining: %3$s",
|
||||
"command.tiedup.debt.removed_paid": "Removed %1$s emeralds from %2$s's debt. Remaining: %3$s (PAID OFF!)"
|
||||
}
|
||||
|
||||
60
src/main/resources/data/tiedup/tiedup_room_themes/crypt.json
Normal file
60
src/main/resources/data/tiedup/tiedup_room_themes/crypt.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"weight": 10,
|
||||
"wall_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:mossy_stone_bricks", "weight": 0.15 },
|
||||
{ "block": "minecraft:cracked_stone_bricks", "weight": 0.15 },
|
||||
{ "block": "minecraft:stone_bricks", "weight": 0.70 }
|
||||
],
|
||||
"bottom_row": [
|
||||
{ "block": "minecraft:mossy_cobblestone", "weight": 0.20 },
|
||||
{ "block": "minecraft:cracked_stone_bricks", "weight": 0.10 },
|
||||
{ "block": "minecraft:stone_bricks", "weight": 0.70 }
|
||||
]
|
||||
},
|
||||
"floor_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cobblestone", "weight": 0.20 },
|
||||
{ "block": "minecraft:stone_bricks", "weight": 0.80 }
|
||||
],
|
||||
"corner": [
|
||||
{ "block": "minecraft:mossy_cobblestone", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"ceiling_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cracked_stone_bricks", "weight": 0.20 },
|
||||
{ "block": "minecraft:stone_bricks", "weight": 0.80 }
|
||||
]
|
||||
},
|
||||
"wall_shell": "minecraft:stone_bricks",
|
||||
"wall_accent": "minecraft:mossy_stone_bricks",
|
||||
"pillar_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:stone_brick_wall", "weight": 1.0 }
|
||||
],
|
||||
"cap": [
|
||||
{ "block": "minecraft:chiseled_stone_bricks", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"scatter_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cobweb", "weight": 0.40 },
|
||||
{ "block": "minecraft:candle[lit=true]", "weight": 0.30, "random_property": { "name": "candles", "min": 1, "max": 3 } },
|
||||
{ "block": "minecraft:bone_block", "weight": 0.30 }
|
||||
]
|
||||
},
|
||||
"decorations": {
|
||||
"corner_decorations": [
|
||||
{ "block": "minecraft:cobweb", "y_offset": 1 },
|
||||
{ "block": "minecraft:cobweb", "y_offset": 9 }
|
||||
],
|
||||
"first_corner_special": { "block": "minecraft:skeleton_skull", "y_offset": 1 },
|
||||
"furniture_cluster": [
|
||||
{ "block": "minecraft:lectern", "y_offset": 1 },
|
||||
{ "block": "minecraft:candle[lit=true,candles=4]", "x_offset": 1, "y_offset": 1 }
|
||||
],
|
||||
"use_torch_lighting": true,
|
||||
"has_ceiling_chain": false
|
||||
}
|
||||
}
|
||||
59
src/main/resources/data/tiedup/tiedup_room_themes/ice.json
Normal file
59
src/main/resources/data/tiedup/tiedup_room_themes/ice.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"weight": 10,
|
||||
"wall_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:ice", "weight": 0.10 },
|
||||
{ "block": "minecraft:blue_ice", "weight": 0.20 },
|
||||
{ "block": "minecraft:packed_ice", "weight": 0.70 }
|
||||
]
|
||||
},
|
||||
"floor_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:snow_block", "weight": 0.20 },
|
||||
{ "block": "minecraft:packed_ice", "weight": 0.80 }
|
||||
],
|
||||
"edge": [
|
||||
{ "block": "minecraft:blue_ice", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"ceiling_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:blue_ice", "weight": 0.20 },
|
||||
{ "block": "minecraft:packed_ice", "weight": 0.80 }
|
||||
]
|
||||
},
|
||||
"wall_shell": "minecraft:packed_ice",
|
||||
"wall_accent": "minecraft:blue_ice",
|
||||
"pillar_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:packed_ice", "weight": 0.50 },
|
||||
{ "block": "minecraft:blue_ice", "weight": 0.50 }
|
||||
],
|
||||
"cap": [
|
||||
{ "block": "minecraft:blue_ice", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"scatter_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:snow[layers=1]", "weight": 0.50, "random_property": { "name": "layers", "min": 1, "max": 2 } },
|
||||
{ "block": "minecraft:powder_snow", "weight": 0.20 },
|
||||
{ "block": "minecraft:ice", "weight": 0.30 }
|
||||
]
|
||||
},
|
||||
"decorations": {
|
||||
"corner_decorations": [
|
||||
{ "block": "minecraft:snow[layers=3]", "y_offset": 1 },
|
||||
{ "block": "minecraft:ice", "y_offset": 10 }
|
||||
],
|
||||
"wall_midpoint_blocks": [
|
||||
{ "block": "minecraft:lantern", "y_offset": 3 }
|
||||
],
|
||||
"furniture_cluster": [
|
||||
{ "block": "minecraft:powder_snow_cauldron[level=3]", "y_offset": 1 },
|
||||
{ "block": "minecraft:blue_ice", "x_offset": 1, "y_offset": 1 },
|
||||
{ "block": "minecraft:lantern", "y_offset": 2, "z_offset": 1 }
|
||||
],
|
||||
"use_torch_lighting": false,
|
||||
"has_ceiling_chain": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"weight": 10,
|
||||
"wall_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cracked_nether_bricks", "weight": 0.20 },
|
||||
{ "block": "minecraft:nether_bricks", "weight": 0.80 }
|
||||
]
|
||||
},
|
||||
"floor_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:gilded_blackstone", "weight": 0.08 },
|
||||
{ "block": "minecraft:blackstone", "weight": 0.92 }
|
||||
],
|
||||
"edge": [
|
||||
{ "block": "minecraft:magma_block", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"ceiling_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cracked_nether_bricks", "weight": 0.20 },
|
||||
{ "block": "minecraft:nether_bricks", "weight": 0.80 }
|
||||
]
|
||||
},
|
||||
"wall_shell": "minecraft:nether_bricks",
|
||||
"wall_accent": "minecraft:red_nether_bricks",
|
||||
"pillar_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:nether_brick_wall", "weight": 1.0 }
|
||||
],
|
||||
"cap": [
|
||||
{ "block": "minecraft:quartz_pillar", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"scatter_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:soul_sand", "weight": 0.40 },
|
||||
{ "block": "minecraft:nether_wart_block", "weight": 0.30 },
|
||||
{ "block": "minecraft:bone_block", "weight": 0.30 }
|
||||
]
|
||||
},
|
||||
"decorations": {
|
||||
"corner_decorations": [
|
||||
{ "block": "minecraft:soul_sand", "y_offset": 0 },
|
||||
{ "block": "minecraft:soul_fire", "y_offset": 1 }
|
||||
],
|
||||
"wall_midpoint_blocks": [
|
||||
{ "block": "minecraft:crying_obsidian", "y_offset": 2 },
|
||||
{ "block": "minecraft:soul_lantern", "y_offset": 3 }
|
||||
],
|
||||
"furniture_cluster": [
|
||||
{ "block": "minecraft:soul_campfire", "y_offset": 1 },
|
||||
{ "block": "minecraft:lava_cauldron", "x_offset": 1, "y_offset": 1 },
|
||||
{ "block": "minecraft:gilded_blackstone", "y_offset": 1, "z_offset": 1 }
|
||||
],
|
||||
"use_torch_lighting": false,
|
||||
"has_ceiling_chain": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"weight": 10,
|
||||
"wall_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cracked_deepslate_bricks", "weight": 0.20 },
|
||||
{ "block": "minecraft:deepslate_bricks", "weight": 0.80 }
|
||||
],
|
||||
"bottom_row": [
|
||||
{ "block": "minecraft:mossy_cobblestone", "weight": 0.30 },
|
||||
{ "block": "minecraft:cracked_deepslate_bricks", "weight": 0.14 },
|
||||
{ "block": "minecraft:deepslate_bricks", "weight": 0.56 }
|
||||
]
|
||||
},
|
||||
"floor_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cobblestone", "weight": 0.15 },
|
||||
{ "block": "minecraft:deepslate_tiles", "weight": 0.85 }
|
||||
],
|
||||
"corner": [
|
||||
{ "block": "minecraft:mossy_cobblestone", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"ceiling_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cracked_deepslate_bricks", "weight": 0.20 },
|
||||
{ "block": "minecraft:deepslate_bricks", "weight": 0.80 }
|
||||
]
|
||||
},
|
||||
"wall_shell": "minecraft:deepslate_bricks",
|
||||
"wall_accent": "minecraft:polished_deepslate",
|
||||
"pillar_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:deepslate_brick_wall", "weight": 1.0 }
|
||||
],
|
||||
"cap": [
|
||||
{ "block": "minecraft:polished_deepslate", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"scatter_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cobweb", "weight": 0.40 },
|
||||
{ "block": "minecraft:candle[lit=true]", "weight": 0.30, "random_property": { "name": "candles", "min": 1, "max": 3 } },
|
||||
{ "block": "minecraft:moss_carpet", "weight": 0.30 }
|
||||
]
|
||||
},
|
||||
"decorations": {
|
||||
"corner_decorations": [
|
||||
{ "block": "minecraft:cobweb", "y_offset": 1 },
|
||||
{ "block": "minecraft:cobweb", "y_offset": 9 }
|
||||
],
|
||||
"wall_midpoint_blocks": [
|
||||
{ "block": "minecraft:soul_lantern", "y_offset": 3 }
|
||||
],
|
||||
"first_corner_special": { "block": "minecraft:water_cauldron[level=3]", "x_offset": 1, "y_offset": 1, "z_offset": 1 },
|
||||
"furniture_cluster": [
|
||||
{ "block": "minecraft:barrel", "y_offset": 1 },
|
||||
{ "block": "minecraft:brewing_stand", "x_offset": 1, "y_offset": 1 }
|
||||
],
|
||||
"use_torch_lighting": false,
|
||||
"has_ceiling_chain": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"weight": 10,
|
||||
"wall_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:chiseled_sandstone", "weight": 0.10 },
|
||||
{ "block": "minecraft:sandstone", "weight": 0.20 },
|
||||
{ "block": "minecraft:cut_sandstone", "weight": 0.70 }
|
||||
]
|
||||
},
|
||||
"floor_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:sand", "weight": 0.15 },
|
||||
{ "block": "minecraft:smooth_sandstone", "weight": 0.85 }
|
||||
]
|
||||
},
|
||||
"ceiling_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:sandstone", "weight": 0.20 },
|
||||
{ "block": "minecraft:cut_sandstone", "weight": 0.80 }
|
||||
]
|
||||
},
|
||||
"wall_shell": "minecraft:cut_sandstone",
|
||||
"wall_accent": "minecraft:chiseled_sandstone",
|
||||
"pillar_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:sandstone_wall", "weight": 1.0 }
|
||||
],
|
||||
"cap": [
|
||||
{ "block": "minecraft:chiseled_sandstone", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"scatter_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:sand", "weight": 0.40 },
|
||||
{ "block": "minecraft:dead_bush", "weight": 0.30 },
|
||||
{ "block": "minecraft:candle[lit=true]", "weight": 0.30, "random_property": { "name": "candles", "min": 1, "max": 3 } }
|
||||
]
|
||||
},
|
||||
"decorations": {
|
||||
"wall_midpoint_blocks": [
|
||||
{ "block": "minecraft:orange_terracotta", "y_offset": 2 }
|
||||
],
|
||||
"first_corner_special": { "block": "minecraft:tnt", "y_offset": 1 },
|
||||
"furniture_cluster": [
|
||||
{ "block": "minecraft:barrel", "y_offset": 1 },
|
||||
{ "block": "minecraft:flower_pot", "x_offset": 1, "y_offset": 1 },
|
||||
{ "block": "minecraft:orange_terracotta", "y_offset": 1, "z_offset": 1 }
|
||||
],
|
||||
"use_torch_lighting": true,
|
||||
"has_ceiling_chain": false
|
||||
}
|
||||
}
|
||||
58
src/main/resources/data/tiedup/tiedup_room_themes/sculk.json
Normal file
58
src/main/resources/data/tiedup/tiedup_room_themes/sculk.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"weight": 10,
|
||||
"wall_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cracked_deepslate_bricks", "weight": 0.10 },
|
||||
{ "block": "minecraft:sculk", "weight": 0.30 },
|
||||
{ "block": "minecraft:deepslate_bricks", "weight": 0.60 }
|
||||
]
|
||||
},
|
||||
"floor_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:sculk", "weight": 0.30 },
|
||||
{ "block": "minecraft:deepslate_tiles", "weight": 0.70 }
|
||||
],
|
||||
"edge": [
|
||||
{ "block": "minecraft:sculk", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"ceiling_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:cracked_deepslate_bricks", "weight": 0.10 },
|
||||
{ "block": "minecraft:sculk", "weight": 0.20 },
|
||||
{ "block": "minecraft:deepslate_bricks", "weight": 0.70 }
|
||||
]
|
||||
},
|
||||
"wall_shell": "minecraft:deepslate_bricks",
|
||||
"wall_accent": "minecraft:sculk_catalyst",
|
||||
"pillar_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:sculk", "weight": 0.30 },
|
||||
{ "block": "minecraft:deepslate_tile_wall", "weight": 0.70 }
|
||||
],
|
||||
"cap": [
|
||||
{ "block": "minecraft:sculk", "weight": 1.0 }
|
||||
]
|
||||
},
|
||||
"scatter_palette": {
|
||||
"default": [
|
||||
{ "block": "minecraft:sculk", "weight": 0.50 },
|
||||
{ "block": "minecraft:sculk_vein", "weight": 0.30 },
|
||||
{ "block": "minecraft:moss_carpet", "weight": 0.20 }
|
||||
]
|
||||
},
|
||||
"decorations": {
|
||||
"wall_midpoint_blocks": [
|
||||
{ "block": "minecraft:sculk_vein", "y_offset": 2 },
|
||||
{ "block": "minecraft:soul_lantern", "y_offset": 3 }
|
||||
],
|
||||
"first_corner_special": { "block": "minecraft:sculk_catalyst", "y_offset": 1 },
|
||||
"furniture_cluster": [
|
||||
{ "block": "minecraft:sculk_shrieker", "y_offset": 1 },
|
||||
{ "block": "minecraft:sculk_sensor", "x_offset": 1, "y_offset": 1 },
|
||||
{ "block": "minecraft:candle[lit=true,candles=3]", "y_offset": 1, "z_offset": 1 }
|
||||
],
|
||||
"use_torch_lighting": false,
|
||||
"has_ceiling_chain": false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user