feat(armor): add Tyrant legacy rewards
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
public record ArmorClaimResult(GameState state, ArmorClaimStatus status) {
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
public enum ArmorClaimStatus {
|
||||
CLAIMED,
|
||||
GAME_NOT_RUNNING,
|
||||
NOT_TYRANT,
|
||||
UNLOCKS_INCOMPLETE,
|
||||
NO_CHOICES,
|
||||
ALREADY_CLAIMED
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
public final class BukkitTyrantArmorItemFactory implements TyrantArmorItemFactory {
|
||||
@Override
|
||||
public ItemStack create(TyrantArmorPiece piece, String tyrantName) {
|
||||
TyrantArmorSpec spec = TyrantArmorSpec.forPiece(piece, tyrantName);
|
||||
ItemStack item = new ItemStack(spec.material());
|
||||
ItemMeta meta = Objects.requireNonNull(item.getItemMeta(), "Armor metadata is unavailable");
|
||||
meta.setDisplayName(ChatColor.GOLD + spec.displayName());
|
||||
meta.setLore(spec.lore().stream().map(line -> ChatColor.GRAY + line).toList());
|
||||
meta.addEnchant(Enchantment.PROTECTION, spec.protectionLevel(), true);
|
||||
meta.addEnchant(Enchantment.MENDING, spec.mendingLevel(), true);
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,14 @@ public final class BukkitTyrantControlPanelRenderer
|
||||
String intelligence = intelligenceText(model);
|
||||
inventory.setItem(16, item(Material.SPYGLASS, ChatColor.LIGHT_PURPLE + "Roster Intelligence",
|
||||
List.of(intelligence, "Click to activate when ready.")));
|
||||
inventory.setItem(22, item(Material.NETHERITE_CHESTPLATE,
|
||||
ChatColor.GOLD + "Legacy Armor", List.of(
|
||||
model.allStandardUnlocksPurchased()
|
||||
? "Choose a once-per-reign armor reward."
|
||||
: "Purchase all six standard unlocks first.",
|
||||
"Claimed: " + (model.claimedArmor().isEmpty()
|
||||
? "none" : model.claimedArmor())
|
||||
)));
|
||||
player.openInventory(inventory);
|
||||
}
|
||||
|
||||
@@ -83,6 +91,10 @@ public final class BukkitTyrantControlPanelRenderer
|
||||
case ASSIGN_CONFIRMATION -> clickAssignmentConfirmation(
|
||||
player, event.getRawSlot(), holder.tyrantClass(), holder.targetId()
|
||||
);
|
||||
case ARMOR -> clickArmor(player, event.getRawSlot());
|
||||
case ARMOR_CONFIRMATION -> clickArmorConfirmation(
|
||||
player, event.getRawSlot(), holder.armorPiece()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +105,67 @@ public final class BukkitTyrantControlPanelRenderer
|
||||
openClasses(player);
|
||||
} else if (slot == 16) {
|
||||
openAbilities(player);
|
||||
} else if (slot == 22) {
|
||||
openArmor(player);
|
||||
}
|
||||
}
|
||||
|
||||
private void openArmor(Player player) {
|
||||
TyrantControlPanelModel model = model(player);
|
||||
MenuHolder holder = new MenuHolder(MenuView.ARMOR, null, null);
|
||||
Inventory inventory = create(holder, 27, TITLE + " — Legacy Armor");
|
||||
TyrantArmorPiece[] pieces = TyrantArmorPiece.values();
|
||||
int[] slots = {10, 12, 14, 16};
|
||||
for (int index = 0; index < pieces.length; index++) {
|
||||
TyrantArmorPiece piece = pieces[index];
|
||||
String status = model.claimedArmor().contains(piece) ? "CLAIMED"
|
||||
: model.armorAvailable(piece) ? "AVAILABLE" : "LOCKED";
|
||||
inventory.setItem(slots[index], item(
|
||||
TyrantArmorSpec.forPiece(piece, player.getName()).material(),
|
||||
ChatColor.GOLD + readable(piece.name()),
|
||||
List.of("Status: " + status, "Cost: 1 choice")
|
||||
));
|
||||
}
|
||||
inventory.setItem(22, backItem());
|
||||
player.openInventory(inventory);
|
||||
}
|
||||
|
||||
private void clickArmor(Player player, int slot) {
|
||||
if (slot == 22) {
|
||||
refresh(player);
|
||||
return;
|
||||
}
|
||||
TyrantArmorPiece[] pieces = TyrantArmorPiece.values();
|
||||
int[] slots = {10, 12, 14, 16};
|
||||
for (int index = 0; index < slots.length; index++) {
|
||||
if (slot == slots[index] && model(player).armorAvailable(pieces[index])) {
|
||||
openArmorConfirmation(player, pieces[index]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void openArmorConfirmation(Player player, TyrantArmorPiece piece) {
|
||||
MenuHolder holder = new MenuHolder(
|
||||
MenuView.ARMOR_CONFIRMATION, null, null, null, piece
|
||||
);
|
||||
Inventory inventory = create(holder, 27, TITLE + " — Confirm Armor");
|
||||
inventory.setItem(11, item(Material.RED_WOOL, ChatColor.RED + "Cancel", List.of()));
|
||||
inventory.setItem(13, item(
|
||||
TyrantArmorSpec.forPiece(piece, player.getName()).material(),
|
||||
ChatColor.GOLD + "Tyrant's " + player.getName() + " " + readable(piece.name()),
|
||||
List.of("Protection V", "Mending I", "Cost: 1 choice")
|
||||
));
|
||||
inventory.setItem(15, item(Material.LIME_WOOL, ChatColor.GREEN + "Confirm", List.of()));
|
||||
player.openInventory(inventory);
|
||||
}
|
||||
|
||||
private void clickArmorConfirmation(Player player, int slot, TyrantArmorPiece piece) {
|
||||
if (slot == 11) {
|
||||
openArmor(player);
|
||||
} else if (slot == 15 && piece != null) {
|
||||
player.performCommand("tyrant armor " + piece.commandName());
|
||||
openArmor(player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +459,9 @@ public final class BukkitTyrantControlPanelRenderer
|
||||
CLASSES,
|
||||
ABILITIES,
|
||||
PLAYERS,
|
||||
ASSIGN_CONFIRMATION
|
||||
ASSIGN_CONFIRMATION,
|
||||
ARMOR,
|
||||
ARMOR_CONFIRMATION
|
||||
}
|
||||
|
||||
private static final class MenuHolder implements InventoryHolder {
|
||||
@@ -394,6 +469,7 @@ public final class BukkitTyrantControlPanelRenderer
|
||||
private final TyrantUnlock unlock;
|
||||
private final TyrantClass tyrantClass;
|
||||
private final UUID targetId;
|
||||
private final TyrantArmorPiece armorPiece;
|
||||
private Inventory inventory;
|
||||
|
||||
private MenuHolder(MenuView view, TyrantUnlock unlock, TyrantClass tyrantClass) {
|
||||
@@ -405,11 +481,22 @@ public final class BukkitTyrantControlPanelRenderer
|
||||
TyrantUnlock unlock,
|
||||
TyrantClass tyrantClass,
|
||||
UUID targetId
|
||||
) {
|
||||
this(view, unlock, tyrantClass, targetId, null);
|
||||
}
|
||||
|
||||
private MenuHolder(
|
||||
MenuView view,
|
||||
TyrantUnlock unlock,
|
||||
TyrantClass tyrantClass,
|
||||
UUID targetId,
|
||||
TyrantArmorPiece armorPiece
|
||||
) {
|
||||
this.view = view;
|
||||
this.unlock = unlock;
|
||||
this.tyrantClass = tyrantClass;
|
||||
this.targetId = targetId;
|
||||
this.armorPiece = armorPiece;
|
||||
}
|
||||
|
||||
private MenuView view() {
|
||||
@@ -428,6 +515,10 @@ public final class BukkitTyrantControlPanelRenderer
|
||||
return targetId;
|
||||
}
|
||||
|
||||
private TyrantArmorPiece armorPiece() {
|
||||
return armorPiece;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory getInventory() {
|
||||
return inventory;
|
||||
|
||||
@@ -52,7 +52,8 @@ public final class GameLifecycleService {
|
||||
current.accumulatedPausedTime(),
|
||||
current.tyrantLevel(),
|
||||
current.unspentChoices(),
|
||||
current.purchases()
|
||||
current.purchases(),
|
||||
current.claimedArmor()
|
||||
);
|
||||
return new LifecycleState(paused, state.players());
|
||||
}
|
||||
@@ -77,7 +78,8 @@ public final class GameLifecycleService {
|
||||
current.accumulatedPausedTime().plus(pausedFor),
|
||||
current.tyrantLevel(),
|
||||
current.unspentChoices(),
|
||||
current.purchases()
|
||||
current.purchases(),
|
||||
current.claimedArmor()
|
||||
);
|
||||
Map<UUID, PlayerState> players = new HashMap<>();
|
||||
for (PlayerState player : state.players().values()) {
|
||||
|
||||
@@ -16,7 +16,8 @@ public record GameState(
|
||||
Duration accumulatedPausedTime,
|
||||
int tyrantLevel,
|
||||
int unspentChoices,
|
||||
Set<TyrantUnlock> purchases
|
||||
Set<TyrantUnlock> purchases,
|
||||
Set<TyrantArmorPiece> claimedArmor
|
||||
) {
|
||||
public GameState {
|
||||
lifecycle = lifecycle == null ? GameLifecycle.UNSTARTED : lifecycle;
|
||||
@@ -28,6 +29,7 @@ public record GameState(
|
||||
accumulatedPausedTime = accumulatedPausedTime == null
|
||||
? Duration.ZERO : accumulatedPausedTime;
|
||||
purchases = purchases == null ? Set.of() : Set.copyOf(purchases);
|
||||
claimedArmor = claimedArmor == null ? Set.of() : Set.copyOf(claimedArmor);
|
||||
if (tyrantId.isPresent() && tyrantId.equals(vigilanteId)) {
|
||||
throw new IllegalArgumentException("Tyrant and Vigilante must be different players");
|
||||
}
|
||||
@@ -42,6 +44,24 @@ public record GameState(
|
||||
}
|
||||
}
|
||||
|
||||
public GameState(
|
||||
GameLifecycle lifecycle,
|
||||
Optional<UUID> tyrantId,
|
||||
Optional<UUID> vigilanteId,
|
||||
Optional<PendingSelection> pendingTyrant,
|
||||
Optional<PendingSelection> pendingVigilante,
|
||||
Optional<Instant> pausedAt,
|
||||
Duration accumulatedPausedTime,
|
||||
int tyrantLevel,
|
||||
int unspentChoices,
|
||||
Set<TyrantUnlock> purchases
|
||||
) {
|
||||
this(
|
||||
lifecycle, tyrantId, vigilanteId, pendingTyrant, pendingVigilante, pausedAt,
|
||||
accumulatedPausedTime, tyrantLevel, unspentChoices, purchases, Set.of()
|
||||
);
|
||||
}
|
||||
|
||||
public static GameState empty() {
|
||||
return new GameState(
|
||||
GameLifecycle.UNSTARTED,
|
||||
@@ -53,6 +73,7 @@ public record GameState(
|
||||
Duration.ZERO,
|
||||
0,
|
||||
0,
|
||||
Set.of(),
|
||||
Set.of()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ public final class RoleMaintenanceService {
|
||||
return new GameState(
|
||||
game.lifecycle(), tyrant, vigilante, pendingTyrant, pendingVigilante,
|
||||
game.pausedAt(), game.accumulatedPausedTime(), game.tyrantLevel(),
|
||||
game.unspentChoices(), game.purchases()
|
||||
game.unspentChoices(), game.purchases(), game.claimedArmor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,8 @@ public final class TyrantAdminCommand implements CommandExecutor {
|
||||
+ game.vigilanteId().map(Object::toString).orElse("none"));
|
||||
sender.sendMessage("Level: " + game.tyrantLevel()
|
||||
+ ", choices: " + game.unspentChoices()
|
||||
+ ", purchases: " + game.purchases());
|
||||
+ ", purchases: " + game.purchases()
|
||||
+ ", claimed armor: " + game.claimedArmor());
|
||||
sender.sendMessage("Pending Tyrant: " + game.pendingTyrant());
|
||||
sender.sendMessage("Pending Vigilante: " + game.pendingVigilante());
|
||||
sender.sendMessage("Shared role arena: " + arenaLocations.location()
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TyrantArmorItemFactory {
|
||||
ItemStack create(TyrantArmorPiece piece, String tyrantName);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
public enum TyrantArmorPiece {
|
||||
HELMET,
|
||||
CHESTPLATE,
|
||||
LEGGINGS,
|
||||
BOOTS;
|
||||
|
||||
public static TyrantArmorPiece fromCommand(String value) {
|
||||
return switch (value.toLowerCase(java.util.Locale.ROOT)) {
|
||||
case "helmet" -> HELMET;
|
||||
case "chestplate", "chest" -> CHESTPLATE;
|
||||
case "leggings", "legs" -> LEGGINGS;
|
||||
case "boots", "feet" -> BOOTS;
|
||||
default -> throw new IllegalArgumentException("Unknown armor piece");
|
||||
};
|
||||
}
|
||||
|
||||
public String commandName() {
|
||||
return name().toLowerCase(java.util.Locale.ROOT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class TyrantArmorService {
|
||||
public ArmorClaimResult claim(GameState state, UUID claimantId, TyrantArmorPiece piece) {
|
||||
if (state.lifecycle() != GameLifecycle.RUNNING) {
|
||||
return result(state, ArmorClaimStatus.GAME_NOT_RUNNING);
|
||||
}
|
||||
if (state.tyrantId().filter(claimantId::equals).isEmpty()) {
|
||||
return result(state, ArmorClaimStatus.NOT_TYRANT);
|
||||
}
|
||||
if (!state.purchases().containsAll(EnumSet.allOf(TyrantUnlock.class))) {
|
||||
return result(state, ArmorClaimStatus.UNLOCKS_INCOMPLETE);
|
||||
}
|
||||
if (state.unspentChoices() == 0) {
|
||||
return result(state, ArmorClaimStatus.NO_CHOICES);
|
||||
}
|
||||
if (state.claimedArmor().contains(piece)) {
|
||||
return result(state, ArmorClaimStatus.ALREADY_CLAIMED);
|
||||
}
|
||||
Set<TyrantArmorPiece> claimed = EnumSet.noneOf(TyrantArmorPiece.class);
|
||||
claimed.addAll(state.claimedArmor());
|
||||
claimed.add(piece);
|
||||
GameState updated = new GameState(
|
||||
state.lifecycle(), state.tyrantId(), state.vigilanteId(), state.pendingTyrant(),
|
||||
state.pendingVigilante(), state.pausedAt(), state.accumulatedPausedTime(),
|
||||
state.tyrantLevel(), state.unspentChoices() - 1, state.purchases(), claimed
|
||||
);
|
||||
return result(updated, ArmorClaimStatus.CLAIMED);
|
||||
}
|
||||
|
||||
private static ArmorClaimResult result(GameState state, ArmorClaimStatus status) {
|
||||
return new ArmorClaimResult(state, status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package games.dmg.spigottyrant;
|
||||
|
||||
import java.util.List;
|
||||
import org.bukkit.Material;
|
||||
|
||||
public record TyrantArmorSpec(
|
||||
Material material,
|
||||
String displayName,
|
||||
List<String> lore,
|
||||
int protectionLevel,
|
||||
int mendingLevel
|
||||
) {
|
||||
public TyrantArmorSpec {
|
||||
lore = List.copyOf(lore);
|
||||
}
|
||||
|
||||
public static TyrantArmorSpec forPiece(TyrantArmorPiece piece, String tyrantName) {
|
||||
Material material = switch (piece) {
|
||||
case HELMET -> Material.NETHERITE_HELMET;
|
||||
case CHESTPLATE -> Material.NETHERITE_CHESTPLATE;
|
||||
case LEGGINGS -> Material.NETHERITE_LEGGINGS;
|
||||
case BOOTS -> Material.NETHERITE_BOOTS;
|
||||
};
|
||||
String pieceName = readable(piece);
|
||||
return new TyrantArmorSpec(
|
||||
material,
|
||||
"Tyrant's " + tyrantName + " " + pieceName,
|
||||
List.of(
|
||||
"Forged for Tyrant " + tyrantName + ".",
|
||||
"A lasting reward from " + tyrantName + "'s reign."
|
||||
),
|
||||
5,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
private static String readable(TyrantArmorPiece piece) {
|
||||
String lower = piece.name().toLowerCase(java.util.Locale.ROOT);
|
||||
return Character.toUpperCase(lower.charAt(0)) + lower.substring(1);
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ public final class TyrantCommand implements CommandExecutor {
|
||||
private final TyrantAbilityService tyrantAbilities;
|
||||
private final TyrantControlPanel controlPanel;
|
||||
private final RoleControlItemService roleControlItems;
|
||||
private final TyrantArmorService armor = new TyrantArmorService();
|
||||
private final TyrantArmorItemFactory armorItems;
|
||||
|
||||
public TyrantCommand(
|
||||
TyrantStateManager stateManager,
|
||||
@@ -63,6 +65,20 @@ public final class TyrantCommand implements CommandExecutor {
|
||||
);
|
||||
}
|
||||
|
||||
TyrantCommand(
|
||||
TyrantStateManager stateManager,
|
||||
TyrantProgressionService progression,
|
||||
TyrantArmorItemFactory armorItems
|
||||
) {
|
||||
this(
|
||||
stateManager, progression, new ClassAssignmentService(),
|
||||
new BukkitOnlinePlayerDirectory(), Clock.systemUTC(),
|
||||
new ParticipationService(Duration.ofDays(7)), null, null, null,
|
||||
new TyrantAbilityService(Duration.ofHours(24)), TyrantCommand::unavailablePanel,
|
||||
null, armorItems
|
||||
);
|
||||
}
|
||||
|
||||
TyrantCommand(
|
||||
TyrantStateManager stateManager,
|
||||
TyrantProgressionService progression,
|
||||
@@ -129,6 +145,28 @@ public final class TyrantCommand implements CommandExecutor {
|
||||
TyrantAbilityService tyrantAbilities,
|
||||
TyrantControlPanel controlPanel,
|
||||
RoleControlItemService roleControlItems
|
||||
) {
|
||||
this(
|
||||
stateManager, progression, assignments, onlinePlayers, clock, participation,
|
||||
relinquishment, presentation, abilityItems, tyrantAbilities, controlPanel,
|
||||
roleControlItems, new BukkitTyrantArmorItemFactory()
|
||||
);
|
||||
}
|
||||
|
||||
public TyrantCommand(
|
||||
TyrantStateManager stateManager,
|
||||
TyrantProgressionService progression,
|
||||
ClassAssignmentService assignments,
|
||||
OnlinePlayerDirectory onlinePlayers,
|
||||
Clock clock,
|
||||
ParticipationService participation,
|
||||
RoleRelinquishmentService relinquishment,
|
||||
TyrantPresentation presentation,
|
||||
AbilityItemService abilityItems,
|
||||
TyrantAbilityService tyrantAbilities,
|
||||
TyrantControlPanel controlPanel,
|
||||
RoleControlItemService roleControlItems,
|
||||
TyrantArmorItemFactory armorItems
|
||||
) {
|
||||
this.stateManager = stateManager;
|
||||
this.progression = progression;
|
||||
@@ -142,6 +180,7 @@ public final class TyrantCommand implements CommandExecutor {
|
||||
this.tyrantAbilities = tyrantAbilities;
|
||||
this.controlPanel = controlPanel;
|
||||
this.roleControlItems = roleControlItems;
|
||||
this.armorItems = armorItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -168,6 +207,10 @@ public final class TyrantCommand implements CommandExecutor {
|
||||
buy(player, arguments[1]);
|
||||
return true;
|
||||
}
|
||||
if (arguments.length == 2 && arguments[0].equalsIgnoreCase("armor")) {
|
||||
claimArmor(player, arguments[1]);
|
||||
return true;
|
||||
}
|
||||
if (arguments.length == 3 && arguments[0].equalsIgnoreCase("assign")) {
|
||||
assign(player, arguments[1], arguments[2]);
|
||||
return true;
|
||||
@@ -198,7 +241,7 @@ public final class TyrantCommand implements CommandExecutor {
|
||||
}
|
||||
player.sendMessage(
|
||||
ChatColor.YELLOW
|
||||
+ "Usage: /tyrant <menu|status|choices|buy|assign|item|intelligence|optout|optin|"
|
||||
+ "Usage: /tyrant <menu|status|choices|buy|armor|assign|item|intelligence|optout|optin|"
|
||||
+ "relinquish confirm>"
|
||||
);
|
||||
return true;
|
||||
@@ -220,6 +263,12 @@ public final class TyrantCommand implements CommandExecutor {
|
||||
}
|
||||
player.sendMessage(ChatColor.YELLOW + "[" + status + "] " + unlock.name());
|
||||
}
|
||||
if (state.purchases().size() == TyrantUnlock.values().length) {
|
||||
player.sendMessage(ChatColor.GOLD + "Legacy armor claimed: "
|
||||
+ (state.claimedArmor().isEmpty() ? "none" : state.claimedArmor()));
|
||||
player.sendMessage(ChatColor.YELLOW
|
||||
+ "Use /tyrant armor <helmet|chestplate|leggings|boots>.");
|
||||
}
|
||||
}
|
||||
|
||||
private void buy(Player player, String requestedUnlock) {
|
||||
@@ -246,6 +295,36 @@ public final class TyrantCommand implements CommandExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
private void claimArmor(Player player, String requestedPiece) {
|
||||
TyrantArmorPiece piece;
|
||||
try {
|
||||
piece = TyrantArmorPiece.fromCommand(requestedPiece);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
player.sendMessage(ChatColor.RED
|
||||
+ "Unknown armor piece. Use helmet, chestplate, leggings, or boots.");
|
||||
return;
|
||||
}
|
||||
PersistentState snapshot = stateManager.snapshot();
|
||||
ArmorClaimResult result = armor.claim(snapshot.game(), player.getUniqueId(), piece);
|
||||
if (result.status() != ArmorClaimStatus.CLAIMED) {
|
||||
player.sendMessage(ChatColor.RED + "Could not claim " + piece.commandName()
|
||||
+ ": " + readable(result.status()));
|
||||
return;
|
||||
}
|
||||
int inventorySlot = player.getInventory().firstEmpty();
|
||||
if (inventorySlot < 0) {
|
||||
player.sendMessage(ChatColor.RED
|
||||
+ "Your inventory is full; no choice was consumed.");
|
||||
return;
|
||||
}
|
||||
org.bukkit.inventory.ItemStack item = armorItems.create(piece, player.getName());
|
||||
stateManager.replaceState(new LifecycleState(result.state(), snapshot.players()));
|
||||
player.getInventory().setItem(inventorySlot, item);
|
||||
stateManager.saveIfDirty();
|
||||
player.sendMessage(ChatColor.GREEN + "Claimed " + piece.commandName()
|
||||
+ " for one choice.");
|
||||
}
|
||||
|
||||
private void assign(Player tyrant, String requestedClass, String targetName) {
|
||||
TyrantClass tyrantClass;
|
||||
try {
|
||||
|
||||
@@ -10,11 +10,13 @@ public record TyrantControlPanelModel(
|
||||
int level,
|
||||
int unspentChoices,
|
||||
Map<TyrantUnlock, UnlockAvailability> unlocks,
|
||||
Set<TyrantArmorPiece> claimedArmor,
|
||||
Map<TyrantClass, String> classHolders,
|
||||
Duration intelligenceCooldown
|
||||
) {
|
||||
public TyrantControlPanelModel {
|
||||
unlocks = Map.copyOf(unlocks);
|
||||
claimedArmor = Set.copyOf(claimedArmor);
|
||||
classHolders = Map.copyOf(classHolders);
|
||||
}
|
||||
|
||||
@@ -25,6 +27,15 @@ public record TyrantControlPanelModel(
|
||||
.collect(java.util.stream.Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
public boolean allStandardUnlocksPurchased() {
|
||||
return purchasedUnlocks().size() == TyrantUnlock.values().length;
|
||||
}
|
||||
|
||||
public boolean armorAvailable(TyrantArmorPiece piece) {
|
||||
return allStandardUnlocksPurchased() && unspentChoices > 0
|
||||
&& !claimedArmor.contains(piece);
|
||||
}
|
||||
|
||||
public static TyrantControlPanelModel create(
|
||||
GameState game,
|
||||
PlayerState tyrant,
|
||||
@@ -61,7 +72,8 @@ public record TyrantControlPanelModel(
|
||||
? Duration.between(now, tyrant.cooldownEnds().get(Ability.ROSTER_INTELLIGENCE))
|
||||
: Duration.ZERO;
|
||||
return new TyrantControlPanelModel(
|
||||
game.tyrantLevel(), game.unspentChoices(), unlocks, holders, cooldown
|
||||
game.tyrantLevel(), game.unspentChoices(), unlocks, game.claimedArmor(),
|
||||
holders, cooldown
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public final class TyrantProgressionService {
|
||||
}
|
||||
|
||||
public GameState resetReignProgression(GameState state) {
|
||||
return copyProgress(state, 0, 0, java.util.Set.of());
|
||||
return copyProgress(state, 0, 0, java.util.Set.of(), java.util.Set.of());
|
||||
}
|
||||
|
||||
private static GameState copyProgress(
|
||||
@@ -77,6 +77,16 @@ public final class TyrantProgressionService {
|
||||
int level,
|
||||
int choices,
|
||||
java.util.Set<TyrantUnlock> purchases
|
||||
) {
|
||||
return copyProgress(state, level, choices, purchases, state.claimedArmor());
|
||||
}
|
||||
|
||||
private static GameState copyProgress(
|
||||
GameState state,
|
||||
int level,
|
||||
int choices,
|
||||
java.util.Set<TyrantUnlock> purchases,
|
||||
java.util.Set<TyrantArmorPiece> claimedArmor
|
||||
) {
|
||||
return new GameState(
|
||||
state.lifecycle(),
|
||||
@@ -88,7 +98,8 @@ public final class TyrantProgressionService {
|
||||
state.accumulatedPausedTime(),
|
||||
level,
|
||||
choices,
|
||||
purchases
|
||||
purchases,
|
||||
claimedArmor
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,16 @@ import org.bukkit.entity.Player;
|
||||
|
||||
public final class TyrantTabCompleter implements TabCompleter {
|
||||
private static final List<String> SUBCOMMANDS = List.of(
|
||||
"menu", "status", "choices", "buy", "assign", "item", "intelligence",
|
||||
"menu", "status", "choices", "buy", "armor", "assign", "item", "intelligence",
|
||||
"optout", "optin", "relinquish"
|
||||
);
|
||||
private static final List<String> UNLOCKS = java.util.Arrays.stream(TyrantUnlock.values())
|
||||
.map(value -> value.name().toLowerCase(java.util.Locale.ROOT))
|
||||
.toList();
|
||||
private static final List<String> CLASSES = List.of("assassin", "fixer", "tamer");
|
||||
private static final List<String> ARMOR = java.util.Arrays.stream(TyrantArmorPiece.values())
|
||||
.map(TyrantArmorPiece::commandName)
|
||||
.toList();
|
||||
private final TyrantStateManager stateManager;
|
||||
private final OnlinePlayerDirectory onlinePlayers;
|
||||
|
||||
@@ -47,6 +50,7 @@ public final class TyrantTabCompleter implements TabCompleter {
|
||||
if (arguments.length == 2) {
|
||||
return switch (subcommand) {
|
||||
case "buy" -> TabSuggestions.matching(arguments[1], UNLOCKS);
|
||||
case "armor" -> TabSuggestions.matching(arguments[1], ARMOR);
|
||||
case "assign" -> TabSuggestions.matching(arguments[1], CLASSES);
|
||||
case "relinquish" -> TabSuggestions.matching(
|
||||
arguments[1], List.of("confirm")
|
||||
|
||||
@@ -15,7 +15,7 @@ public final class VigilanteArenaSuccessionService {
|
||||
GameState assigned = new GameState(
|
||||
game.lifecycle(), game.tyrantId(), Optional.of(winnerId), game.pendingTyrant(),
|
||||
Optional.empty(), game.pausedAt(), game.accumulatedPausedTime(),
|
||||
game.tyrantLevel(), game.unspentChoices(), game.purchases()
|
||||
game.tyrantLevel(), game.unspentChoices(), game.purchases(), game.claimedArmor()
|
||||
);
|
||||
return new LifecycleState(assigned, state.players());
|
||||
}
|
||||
|
||||
@@ -95,7 +95,8 @@ public final class YamlTyrantStateRepository {
|
||||
Duration.ofSeconds(nonNegativeLong(yaml, "game.accumulated-paused-seconds")),
|
||||
nonNegativeInt(yaml, "game.tyrant-level"),
|
||||
nonNegativeInt(yaml, "game.unspent-choices"),
|
||||
enumSet(TyrantUnlock.class, yaml.getStringList("game.purchases"))
|
||||
enumSet(TyrantUnlock.class, yaml.getStringList("game.purchases")),
|
||||
enumSet(TyrantArmorPiece.class, yaml.getStringList("game.claimed-armor"))
|
||||
);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
return GameState.empty();
|
||||
@@ -192,6 +193,10 @@ public final class YamlTyrantStateRepository {
|
||||
yaml.set("game.tyrant-level", game.tyrantLevel());
|
||||
yaml.set("game.unspent-choices", game.unspentChoices());
|
||||
yaml.set("game.purchases", game.purchases().stream().map(Enum::name).sorted().toList());
|
||||
yaml.set(
|
||||
"game.claimed-armor",
|
||||
game.claimedArmor().stream().map(Enum::name).sorted().toList()
|
||||
);
|
||||
}
|
||||
|
||||
private static void savePlayer(YamlConfiguration yaml, PlayerState player) {
|
||||
|
||||
@@ -7,7 +7,7 @@ author: dmg.games
|
||||
commands:
|
||||
tyrant:
|
||||
description: View and use Spigot Tyrant game features.
|
||||
usage: /tyrant <menu|status|choices|buy|assign|item|intelligence|optout|optin|relinquish confirm>
|
||||
usage: /tyrant <menu|status|choices|buy|armor <helmet|chestplate|leggings|boots>|assign|item|intelligence|optout|optin|relinquish confirm>
|
||||
vigilante:
|
||||
description: Manage Vigilante Followers.
|
||||
usage: /vigilante <menu|item|invite <player>|accept|dismiss <player>|leave>
|
||||
|
||||
Reference in New Issue
Block a user