init commit
This commit is contained in:
@@ -0,0 +1,346 @@
|
||||
package de.winniepat.pierredufaulersack.kits;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class KitService {
|
||||
|
||||
private static final int MIN_SLOT = 1;
|
||||
private static final int MAX_SLOT = 3;
|
||||
private static final int KIT_CONTENT_SIZE = 41;
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public KitService(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public boolean addKit(KitType type, int slot, Player player) {
|
||||
validateSlot(slot);
|
||||
String path = path(type, slot);
|
||||
if (plugin.getConfig().contains(path)) {
|
||||
return false;
|
||||
}
|
||||
setKit(type, slot, player);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setKit(KitType type, int slot, Player player) {
|
||||
setKit(type, slot, player.getInventory().getContents());
|
||||
}
|
||||
|
||||
public void setKit(KitType type, int slot, ItemStack[] contents) {
|
||||
validateSlot(slot);
|
||||
|
||||
ItemStack[] normalizedContents = normalizeContents(contents);
|
||||
List<Map<String, Object>> serialized = new ArrayList<>(normalizedContents.length);
|
||||
for (ItemStack item : normalizedContents) {
|
||||
serialized.add(item == null ? null : item.clone().serialize());
|
||||
}
|
||||
|
||||
plugin.getConfig().set(path(type, slot), serialized);
|
||||
plugin.saveConfig();
|
||||
}
|
||||
|
||||
public boolean deleteKit(KitType type, int slot) {
|
||||
validateSlot(slot);
|
||||
String path = path(type, slot);
|
||||
if (!plugin.getConfig().contains(path)) {
|
||||
return false;
|
||||
}
|
||||
plugin.getConfig().set(path, null);
|
||||
plugin.saveConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<Integer> listKitSlots(KitType type) {
|
||||
List<Integer> slots = new ArrayList<>();
|
||||
for (int slot = MIN_SLOT; slot <= MAX_SLOT; slot++) {
|
||||
if (hasKit(type, slot)) {
|
||||
slots.add(slot);
|
||||
}
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
public boolean hasKit(KitType type, int slot) {
|
||||
validateSlot(slot);
|
||||
return plugin.getConfig().contains(path(type, slot));
|
||||
}
|
||||
|
||||
public Optional<List<ItemStack>> getKit(KitType type, int slot) {
|
||||
validateSlot(slot);
|
||||
|
||||
List<?> raw = plugin.getConfig().getList(path(type, slot));
|
||||
if (raw == null || raw.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<ItemStack> saved = new ArrayList<>(KIT_CONTENT_SIZE);
|
||||
boolean hasItem = false;
|
||||
|
||||
for (int index = 0; index < KIT_CONTENT_SIZE; index++) {
|
||||
Object element = index < raw.size() ? raw.get(index) : null;
|
||||
ItemStack item = deserializeItem(element);
|
||||
if (item != null) {
|
||||
hasItem = true;
|
||||
}
|
||||
saved.add(item);
|
||||
}
|
||||
|
||||
if (!hasItem) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(cloneInventory(saved));
|
||||
}
|
||||
|
||||
public Optional<List<ItemStack>> getKitForPlayer(UUID playerId, KitType type, int slot) {
|
||||
Optional<List<ItemStack>> maybeBase = getKit(type, slot);
|
||||
if (maybeBase.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<ItemStack> base = maybeBase.get();
|
||||
Optional<int[]> maybeLayout = getPlayerLayout(playerId, type, slot);
|
||||
if (maybeLayout.isEmpty()) {
|
||||
return Optional.of(cloneInventory(base));
|
||||
}
|
||||
|
||||
return Optional.of(applyLayout(base, maybeLayout.get()));
|
||||
}
|
||||
|
||||
public boolean setPlayerLayout(UUID playerId, KitType type, int slot, ItemStack[] arrangedContents) {
|
||||
validateSlot(slot);
|
||||
|
||||
Optional<List<ItemStack>> maybeBase = getKit(type, slot);
|
||||
if (maybeBase.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<ItemStack> base = maybeBase.get();
|
||||
ItemStack[] arranged = normalizeContents(arrangedContents);
|
||||
int[] layout = deriveLayout(base, arranged);
|
||||
|
||||
List<Integer> serialized = new ArrayList<>(layout.length);
|
||||
for (int value : layout) {
|
||||
serialized.add(value);
|
||||
}
|
||||
|
||||
plugin.getConfig().set(playerLayoutPath(playerId, type, slot), serialized);
|
||||
plugin.saveConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
private Optional<int[]> getPlayerLayout(UUID playerId, KitType type, int slot) {
|
||||
String path = playerLayoutPath(playerId, type, slot);
|
||||
List<?> raw = plugin.getConfig().getList(path);
|
||||
if (raw == null || raw.size() != KIT_CONTENT_SIZE) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
int[] layout = new int[KIT_CONTENT_SIZE];
|
||||
for (int i = 0; i < KIT_CONTENT_SIZE; i++) {
|
||||
Object element = raw.get(i);
|
||||
if (!(element instanceof Number number)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
int value = number.intValue();
|
||||
if (value < 0 || value >= KIT_CONTENT_SIZE) {
|
||||
return Optional.empty();
|
||||
}
|
||||
layout[i] = value;
|
||||
}
|
||||
|
||||
return Optional.of(layout);
|
||||
}
|
||||
|
||||
private static List<ItemStack> applyLayout(List<ItemStack> base, int[] layout) {
|
||||
List<ItemStack> arranged = new ArrayList<>(KIT_CONTENT_SIZE);
|
||||
boolean[] consumed = new boolean[KIT_CONTENT_SIZE];
|
||||
|
||||
for (int target = 0; target < KIT_CONTENT_SIZE; target++) {
|
||||
int source = target < layout.length ? layout[target] : target;
|
||||
if (source < 0 || source >= KIT_CONTENT_SIZE || consumed[source]) {
|
||||
source = firstUnused(consumed, target);
|
||||
}
|
||||
consumed[source] = true;
|
||||
|
||||
ItemStack item = source < base.size() ? base.get(source) : null;
|
||||
arranged.add(item == null ? null : item.clone());
|
||||
}
|
||||
|
||||
return arranged;
|
||||
}
|
||||
|
||||
private static int[] deriveLayout(List<ItemStack> base, ItemStack[] arranged) {
|
||||
int[] layout = new int[KIT_CONTENT_SIZE];
|
||||
boolean[] used = new boolean[KIT_CONTENT_SIZE];
|
||||
|
||||
for (int target = 0; target < KIT_CONTENT_SIZE; target++) {
|
||||
ItemStack desired = arranged[target];
|
||||
int matched = -1;
|
||||
|
||||
for (int source = 0; source < KIT_CONTENT_SIZE; source++) {
|
||||
if (used[source]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemStack sourceItem = source < base.size() ? base.get(source) : null;
|
||||
if (isSameStack(sourceItem, desired)) {
|
||||
matched = source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched == -1) {
|
||||
matched = firstUnused(used, target);
|
||||
}
|
||||
|
||||
used[matched] = true;
|
||||
layout[target] = matched;
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private static int firstUnused(boolean[] used, int fallback) {
|
||||
if (fallback >= 0 && fallback < used.length && !used[fallback]) {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
for (int i = 0; i < used.length; i++) {
|
||||
if (!used[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static boolean isSameStack(ItemStack a, ItemStack b) {
|
||||
if (a == null || a.getType().isAir()) {
|
||||
return b == null || b.getType().isAir();
|
||||
}
|
||||
if (b == null || b.getType().isAir()) {
|
||||
return false;
|
||||
}
|
||||
return a.getAmount() == b.getAmount() && a.isSimilar(b);
|
||||
}
|
||||
|
||||
private static ItemStack deserializeItem(Object element) {
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (element instanceof ItemStack itemStack) {
|
||||
return itemStack.clone();
|
||||
}
|
||||
|
||||
if (element instanceof Map<?, ?> map) {
|
||||
Map<String, Object> serialized = new LinkedHashMap<>();
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
if (entry.getKey() instanceof String key) {
|
||||
serialized.put(key, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (!serialized.isEmpty()) {
|
||||
try {
|
||||
return ItemStack.deserialize(serialized);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public OptionalInt rollKitSlot(KitType type, IntPredicate allowedSlot) {
|
||||
List<Integer> slots = new ArrayList<>();
|
||||
List<Integer> weights = new ArrayList<>();
|
||||
int total = 0;
|
||||
|
||||
for (int slot = MIN_SLOT; slot <= MAX_SLOT; slot++) {
|
||||
if (!allowedSlot.test(slot) || !hasKit(type, slot)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int weight = getChance(type, slot);
|
||||
if (weight <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
slots.add(slot);
|
||||
weights.add(weight);
|
||||
total += weight;
|
||||
}
|
||||
|
||||
if (total <= 0) {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
|
||||
int roll = ThreadLocalRandom.current().nextInt(total);
|
||||
int cursor = 0;
|
||||
for (int i = 0; i < slots.size(); i++) {
|
||||
cursor += weights.get(i);
|
||||
if (roll < cursor) {
|
||||
return OptionalInt.of(slots.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
|
||||
public OptionalInt rollKitSlot(KitType type) {
|
||||
return rollKitSlot(type, slot -> true);
|
||||
}
|
||||
|
||||
private int getChance(KitType type, int slot) {
|
||||
String chancePath = "kit-chances." + type.key() + "." + slot;
|
||||
return plugin.getConfig().getInt(chancePath, 0);
|
||||
}
|
||||
|
||||
private static ItemStack[] normalizeContents(ItemStack[] contents) {
|
||||
ItemStack[] normalized = new ItemStack[KIT_CONTENT_SIZE];
|
||||
if (contents == null) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
int copyLength = Math.min(KIT_CONTENT_SIZE, contents.length);
|
||||
for (int index = 0; index < copyLength; index++) {
|
||||
ItemStack item = contents[index];
|
||||
normalized[index] = item == null ? null : item.clone();
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private static List<ItemStack> cloneInventory(List<ItemStack> contents) {
|
||||
List<ItemStack> cloned = new ArrayList<>(contents.size());
|
||||
for (ItemStack item : contents) {
|
||||
cloned.add(item == null ? null : item.clone());
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
private static String path(KitType type, int slot) {
|
||||
return "kits." + type.key() + "." + slot;
|
||||
}
|
||||
|
||||
private static String playerLayoutPath(UUID playerId, KitType type, int slot) {
|
||||
return "player-kit-layouts." + playerId + "." + type.key() + "." + slot;
|
||||
}
|
||||
|
||||
private static void validateSlot(int slot) {
|
||||
if (slot < MIN_SLOT || slot > MAX_SLOT) {
|
||||
throw new IllegalArgumentException("Slot must be between 1 and 3");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user