Add prize management improvements and /easter getprizes subcommand

This commit is contained in:
akastijn 2026-04-05 17:06:17 +02:00
parent e46e8b2e32
commit f25e696448
6 changed files with 145 additions and 7 deletions

View File

@ -4,6 +4,7 @@ import com.alttd.easter.Easter;
import com.alttd.easter.commands.subcommands.Reload; import com.alttd.easter.commands.subcommands.Reload;
import com.alttd.easter.commands.subcommands.SetPrize; import com.alttd.easter.commands.subcommands.SetPrize;
import com.alttd.easter.commands.subcommands.SpawnEggs; import com.alttd.easter.commands.subcommands.SpawnEggs;
import com.alttd.easter.commands.subcommands.GetPrizes;
import com.alttd.easter.config.Messages; import com.alttd.easter.config.Messages;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -38,6 +39,7 @@ public class Command implements CommandExecutor, TabExecutor {
subCommands = List.of( subCommands = List.of(
new Reload(easter), new Reload(easter),
new SetPrize(), new SetPrize(),
new GetPrizes(),
new SpawnEggs() new SpawnEggs()
); );
} }

View File

@ -0,0 +1,61 @@
package com.alttd.easter.commands.subcommands;
import com.alttd.easter.commands.SubCommand;
import com.alttd.easter.config.Config;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.command.CommandSender;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.ArrayList;
import java.util.List;
public class GetPrizes extends SubCommand {
@Override
public boolean onCommand(CommandSender commandSender, String[] args) {
List<ItemStack> list = Config.PRIZES.LIST;
if (list.isEmpty()) {
commandSender.sendRichMessage("<red>No prizes are configured.</red>");
return true;
}
commandSender.sendRichMessage("<gold>Configured prizes (index : material - name)</gold>");
for (int i = 0; i < list.size(); i++) {
ItemStack item = list.get(i);
String material = item.getType().name();
String name = material;
ItemMeta meta = item.getItemMeta();
if (meta != null && meta.hasDisplayName()) {
Component component = meta.displayName();
if (component != null) {
name = PlainTextComponentSerializer.plainText().serialize(component);
}
}
commandSender.sendRichMessage("<gray><index></gray> : <yellow><material></yellow> - <green><name></green>",
TagResolver.resolver(
Placeholder.parsed("index", String.valueOf(i)),
Placeholder.parsed("material", material),
Placeholder.parsed("name", name)
));
}
return true;
}
@Override
public String getName() {
return "getprizes";
}
@Override
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
return new ArrayList<>();
}
@Override
public String getHelpMessage() {
return "<green>List configured prizes with index: <gold>/easter getprizes</gold></green>";
}
}

View File

@ -7,6 +7,7 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class SetPrize extends SubCommand { public class SetPrize extends SubCommand {
@ -22,8 +23,23 @@ public class SetPrize extends SubCommand {
player.sendRichMessage(Messages.SETPRIZE.EMPTY_HAND); player.sendRichMessage(Messages.SETPRIZE.EMPTY_HAND);
return true; return true;
} }
Config.PRIZES.addPrize(item.clone()); // args[0] is the subcommand name. Optional args[1] may be index.
player.sendRichMessage(Messages.SETPRIZE.SUCCESS); if (args.length >= 2) {
try {
int index = Integer.parseInt(args[1]);
if (index < 0 || index > Config.PRIZES.LIST.size()) {
player.sendRichMessage("<red>Index out of bounds. Valid range: 0 - " + Config.PRIZES.LIST.size() + "</red>");
return true;
}
Config.PRIZES.setPrizeAt(index, item.clone());
player.sendRichMessage("<green>Set prize at index </green><yellow>" + index + "</yellow>");
} catch (NumberFormatException e) {
player.sendRichMessage("<red>Invalid index. Usage: /easter setprize [index]</red>");
}
} else {
Config.PRIZES.addPrize(item.clone());
player.sendRichMessage(Messages.SETPRIZE.SUCCESS);
}
return true; return true;
} }
@ -34,6 +50,14 @@ public class SetPrize extends SubCommand {
@Override @Override
public List<String> getTabComplete(CommandSender commandSender, String[] args) { public List<String> getTabComplete(CommandSender commandSender, String[] args) {
if (args.length == 2) {
List<String> res = new ArrayList<>();
int maxIndex = Config.PRIZES.LIST.size();
for (int i = 0; i <= maxIndex; i++) {
res.add(String.valueOf(i));
}
return res;
}
return List.of(); return List.of();
} }

View File

@ -69,6 +69,7 @@ public class Config extends AbstractConfig {
public static class PRIZES { public static class PRIZES {
private static final String prefix = "prizes."; private static final String prefix = "prizes.";
public static List<ItemStack> LIST = new ArrayList<>(); public static List<ItemStack> LIST = new ArrayList<>();
private static int NEXT_INDEX = 0;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static void load() { private static void load() {
@ -83,18 +84,58 @@ public class Config extends AbstractConfig {
} }
// ensure path exists // ensure path exists
config.yaml.addDefault(prefix + "list", LIST); config.yaml.addDefault(prefix + "list", LIST);
// load next index and clamp to range
NEXT_INDEX = config.getInt(prefix, "next-index", 0);
if (LIST.isEmpty()) {
NEXT_INDEX = 0;
} else if (NEXT_INDEX < 0 || NEXT_INDEX >= LIST.size()) {
NEXT_INDEX = NEXT_INDEX % LIST.size();
if (NEXT_INDEX < 0) NEXT_INDEX += LIST.size();
}
config.yaml.addDefault(prefix + "next-index", NEXT_INDEX);
} }
public static void addPrize(ItemStack item) { public static void addPrize(ItemStack item) {
LIST.add(item); LIST.add(item);
config.set(prefix, "list", LIST); config.set(prefix, "list", LIST);
// keep NEXT_INDEX valid
if (NEXT_INDEX < 0 || NEXT_INDEX >= LIST.size()) {
NEXT_INDEX = 0;
}
config.set(prefix, "next-index", NEXT_INDEX);
} }
public static ItemStack getRandomPrize() { public static void setPrizeAt(int index, ItemStack item) {
if (index < 0 || index > LIST.size()) {
throw new IndexOutOfBoundsException("Index must be between 0 and " + LIST.size());
}
if (index == LIST.size()) {
LIST.add(item);
} else {
LIST.set(index, item);
}
config.set(prefix, "list", LIST);
// adjust NEXT_INDEX if necessary
if (LIST.isEmpty()) { if (LIST.isEmpty()) {
NEXT_INDEX = 0;
} else if (NEXT_INDEX >= LIST.size()) {
NEXT_INDEX = NEXT_INDEX % LIST.size();
}
config.set(prefix, "next-index", NEXT_INDEX);
}
public static ItemStack getNextPrize() {
if (LIST.isEmpty()) {
log.warn("[Easter] No prizes configured. Staff should configure prizes using /easter setprize.");
return null; return null;
} }
return LIST.get((int) (Math.random() * LIST.size())).clone(); if (NEXT_INDEX < 0 || NEXT_INDEX >= LIST.size()) {
NEXT_INDEX = 0;
}
ItemStack prize = LIST.get(NEXT_INDEX).clone();
NEXT_INDEX = (NEXT_INDEX + 1) % LIST.size();
config.set(prefix, "next-index", NEXT_INDEX);
return prize;
} }
} }
} }

View File

@ -10,7 +10,6 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -127,9 +126,17 @@ public class TurnInGuiManager implements Listener {
int prevMilestones = prevTotal / 4; int prevMilestones = prevTotal / 4;
int newMilestones = newTotal / 4; int newMilestones = newTotal / 4;
int prizesToGive = Math.max(0, newMilestones - prevMilestones); int prizesToGive = Math.max(0, newMilestones - prevMilestones);
List<ItemStack> prizeList = Config.PRIZES.LIST;
for (int i = 0; i < prizesToGive; i++) { for (int i = 0; i < prizesToGive; i++) {
var prize = Config.PRIZES.getRandomPrize(); int intendedIndex = prevMilestones + i;
if (prize != null) { if (prizeList.isEmpty()) {
log.warn("No prizes configured. Tried to give prize for intended index {}. Staff should configure prizes using /easter setprize.", intendedIndex);
} else {
int wrappedIndex = intendedIndex % prizeList.size();
if (intendedIndex >= prizeList.size() || intendedIndex < 0) {
log.warn("Prize index overflow: intended index {} -> wrapped to {} (prize list size {}).", intendedIndex, wrappedIndex, prizeList.size());
}
ItemStack prize = prizeList.get(wrappedIndex).clone();
player.getInventory().addItem(prize); player.getInventory().addItem(prize);
} }
player.sendRichMessage(Messages.RABBIT.REWARD); player.sendRichMessage(Messages.RABBIT.REWARD);

View File

@ -17,6 +17,9 @@ permissions:
easter.setprize: easter.setprize:
description: Set the held item as a prize description: Set the held item as a prize
default: op default: op
easter.getprizes:
description: List configured prizes with their index
default: op
easter.spawneggs: easter.spawneggs:
description: Give yourself debug eggs description: Give yourself debug eggs
default: op default: op