765 lines
32 KiB
Java
765 lines
32 KiB
Java
package com.yourname.premiumah.gui;
|
|
|
|
import com.yourname.premiumah.config.ButtonConfig;
|
|
import com.yourname.premiumah.config.ConfigManager;
|
|
import com.yourname.premiumah.config.MessageManager;
|
|
import com.yourname.premiumah.manager.AuctionHouseManager;
|
|
import com.yourname.premiumah.manager.ClaimManager;
|
|
import com.yourname.premiumah.manager.ListingManager;
|
|
import com.yourname.premiumah.model.ActionResult;
|
|
import com.yourname.premiumah.model.ClaimRecord;
|
|
import com.yourname.premiumah.model.ClaimType;
|
|
import com.yourname.premiumah.model.Listing;
|
|
import com.yourname.premiumah.model.ListingCreationResult;
|
|
import com.yourname.premiumah.model.SortMode;
|
|
import com.yourname.premiumah.util.InventoryUtil;
|
|
import com.yourname.premiumah.util.MaterialUtil;
|
|
import com.yourname.premiumah.util.TextUtil;
|
|
import io.papermc.paper.event.player.AsyncChatEvent;
|
|
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.NamespacedKey;
|
|
import org.bukkit.Registry;
|
|
import org.bukkit.Sound;
|
|
import org.bukkit.entity.HumanEntity;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.inventory.ClickType;
|
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
|
import org.bukkit.event.inventory.InventoryCloseEvent;
|
|
import org.bukkit.event.inventory.InventoryDragEvent;
|
|
import org.bukkit.inventory.Inventory;
|
|
import org.bukkit.inventory.ItemFlag;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.inventory.PlayerInventory;
|
|
import org.bukkit.inventory.meta.ItemMeta;
|
|
import org.bukkit.plugin.java.JavaPlugin;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
public final class GuiManager {
|
|
private static final int SELL_ITEM_SLOT = 22;
|
|
|
|
private final JavaPlugin plugin;
|
|
private final ConfigManager config;
|
|
private final MessageManager messages;
|
|
private final ListingManager listings;
|
|
private final ClaimManager claims;
|
|
private final AuctionHouseManager auctionHouse;
|
|
private final Map<UUID, BrowseState> browseStates = new ConcurrentHashMap<>();
|
|
private final Map<UUID, SellSession> sellSessions = new ConcurrentHashMap<>();
|
|
private final Map<UUID, Long> clickDebounce = new ConcurrentHashMap<>();
|
|
|
|
public GuiManager(JavaPlugin plugin,
|
|
ConfigManager config,
|
|
MessageManager messages,
|
|
ListingManager listings,
|
|
ClaimManager claims,
|
|
AuctionHouseManager auctionHouse) {
|
|
this.plugin = plugin;
|
|
this.config = config;
|
|
this.messages = messages;
|
|
this.listings = listings;
|
|
this.claims = claims;
|
|
this.auctionHouse = auctionHouse;
|
|
}
|
|
|
|
public void openMain(Player player) {
|
|
GuiHolder holder = new GuiHolder(GuiType.MAIN, player.getUniqueId(), 0, null, null, null);
|
|
Inventory inventory = create(holder, config.guiSize("main"), config.guiTitle("main"), Map.of());
|
|
fill(inventory);
|
|
inventory.setItem(11, button("browse", Map.of()));
|
|
inventory.setItem(13, button("sell", Map.of()));
|
|
inventory.setItem(15, button("my-listings", Map.of()));
|
|
inventory.setItem(29, button("claims", Map.of()));
|
|
if (player.hasPermission("premiumah.admin")) {
|
|
inventory.setItem(31, button("admin", Map.of()));
|
|
}
|
|
inventory.setItem(33, button("close", Map.of()));
|
|
player.openInventory(inventory);
|
|
play(player, "open");
|
|
}
|
|
|
|
public void openBrowse(Player player, int requestedPage) {
|
|
BrowseState state = browseState(player);
|
|
List<Listing> active = listings.activeListings(state.sortMode(), state.filter());
|
|
openListingView(player, GuiType.BROWSE, active, requestedPage, "browse", null, state.sortMode(), state.filter());
|
|
}
|
|
|
|
public void openMyListings(Player player, int requestedPage) {
|
|
List<Listing> own = listings.activeListingsBySeller(player.getUniqueId(), SortMode.NEWEST);
|
|
openListingView(player, GuiType.MY_LISTINGS, own, requestedPage, "my-listings", null, SortMode.NEWEST, null);
|
|
}
|
|
|
|
public void openAdmin(Player player, int requestedPage) {
|
|
BrowseState state = browseState(player);
|
|
List<Listing> active = listings.activeListings(state.sortMode(), state.filter());
|
|
openListingView(player, GuiType.ADMIN, active, requestedPage, "admin", null, state.sortMode(), state.filter());
|
|
}
|
|
|
|
public void openAdminForSeller(Player player, String sellerName, int requestedPage) {
|
|
List<Listing> active = listings.activeListingsBySellerName(sellerName, SortMode.NEWEST);
|
|
openListingView(player, GuiType.ADMIN, active, requestedPage, "admin", sellerName, SortMode.NEWEST, null);
|
|
}
|
|
|
|
public void openClaims(Player player, int requestedPage) {
|
|
List<ClaimRecord> playerClaims = claims.claimsFor(player.getUniqueId());
|
|
int page = normalizePage(requestedPage, playerClaims.size());
|
|
GuiHolder holder = new GuiHolder(GuiType.CLAIMS, player.getUniqueId(), page, null, SortMode.NEWEST, null);
|
|
Inventory inventory = create(holder, config.guiSize("claims"), config.guiTitle("claims"), Map.of("page", String.valueOf(page + 1)));
|
|
fill(inventory);
|
|
List<Integer> slots = config.listingSlots();
|
|
int start = page * slots.size();
|
|
for (int i = 0; i < slots.size(); i++) {
|
|
int index = start + i;
|
|
if (index >= playerClaims.size()) {
|
|
break;
|
|
}
|
|
ClaimRecord claim = playerClaims.get(index);
|
|
int slot = slots.get(i);
|
|
holder.claimSlots().put(slot, claim.id());
|
|
inventory.setItem(slot, claimItem(claim));
|
|
}
|
|
addPagedControls(inventory, page, playerClaims.size(), slots.size(), "back");
|
|
player.openInventory(inventory);
|
|
play(player, "open");
|
|
}
|
|
|
|
public void openSell(Player player, boolean takeHeldItem) {
|
|
if (!player.hasPermission("premiumah.sell")) {
|
|
messages.send(player, "no-permission");
|
|
play(player, "fail");
|
|
return;
|
|
}
|
|
SellSession session = sellSessions.computeIfAbsent(player.getUniqueId(), ignored -> new SellSession());
|
|
if (takeHeldItem && InventoryUtil.isAir(session.rawItem())) {
|
|
ItemStack held = player.getInventory().getItemInMainHand();
|
|
if (!InventoryUtil.isAir(held)) {
|
|
session.item(held);
|
|
player.getInventory().setItemInMainHand(null);
|
|
}
|
|
}
|
|
session.awaitingPrice(false);
|
|
session.completed(false);
|
|
|
|
GuiHolder holder = new GuiHolder(GuiType.SELL, player.getUniqueId(), 0, null, null, null);
|
|
Inventory inventory = create(holder, config.guiSize("sell"), config.guiTitle("sell"), Map.of());
|
|
fill(inventory);
|
|
inventory.setItem(SELL_ITEM_SLOT, session.item());
|
|
inventory.setItem(20, customItem(Material.GOLD_INGOT, "<#f5d58a>Set Price", List.of(
|
|
"<#9ca3af>Current: <#ffffff>" + (session.price() <= 0.0D ? "Not set" : auctionHouse.formatMoney(session.price())),
|
|
"<#6ee7b7>Click to type a price."
|
|
), Map.of()));
|
|
inventory.setItem(24, button("confirm", Map.of()));
|
|
inventory.setItem(36, button("back", Map.of()));
|
|
inventory.setItem(40, button("cancel", Map.of()));
|
|
player.openInventory(inventory);
|
|
play(player, "open");
|
|
}
|
|
|
|
public void openConfirmBuy(Player player, String listingId) {
|
|
Listing listing = listings.get(listingId).orElse(null);
|
|
if (listing == null || !listing.isActive(System.currentTimeMillis())) {
|
|
messages.send(player, "listing-no-longer-available");
|
|
play(player, "fail");
|
|
openBrowse(player, 0);
|
|
return;
|
|
}
|
|
GuiHolder holder = new GuiHolder(GuiType.CONFIRM_BUY, player.getUniqueId(), 0, listing.id(), null, null);
|
|
Inventory inventory = create(holder, config.guiSize("confirm-buy"), config.guiTitle("confirm-buy"), Map.of());
|
|
fill(inventory);
|
|
inventory.setItem(11, button("confirm", Map.of()));
|
|
inventory.setItem(13, listingItem(listing, "confirm-buy"));
|
|
inventory.setItem(15, button("cancel", Map.of()));
|
|
player.openInventory(inventory);
|
|
play(player, "open");
|
|
}
|
|
|
|
public void handleClick(InventoryClickEvent event) {
|
|
if (!(event.getWhoClicked() instanceof Player player) || !(event.getView().getTopInventory().getHolder() instanceof GuiHolder holder)) {
|
|
return;
|
|
}
|
|
int topSize = event.getView().getTopInventory().getSize();
|
|
int rawSlot = event.getRawSlot();
|
|
|
|
if (holder.type() != GuiType.SELL && rawSlot >= topSize) {
|
|
if (event.isShiftClick()) {
|
|
event.setCancelled(true);
|
|
}
|
|
return;
|
|
}
|
|
if (holder.type() == GuiType.SELL && rawSlot >= topSize) {
|
|
if (event.isShiftClick()) {
|
|
event.setCancelled(true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (holder.type() == GuiType.SELL && rawSlot == SELL_ITEM_SLOT) {
|
|
event.setCancelled(false);
|
|
Bukkit.getScheduler().runTask(plugin, () -> syncSellSlot(player, event.getView().getTopInventory()));
|
|
return;
|
|
}
|
|
|
|
event.setCancelled(true);
|
|
if (rawSlot < 0 || !canClick(player)) {
|
|
return;
|
|
}
|
|
|
|
switch (holder.type()) {
|
|
case MAIN -> handleMainClick(player, rawSlot);
|
|
case BROWSE -> handleBrowseClick(player, holder, rawSlot, event.getClick());
|
|
case MY_LISTINGS -> handleMyListingsClick(player, holder, rawSlot);
|
|
case CLAIMS -> handleClaimsClick(player, holder, rawSlot);
|
|
case SELL -> handleSellClick(player, event.getView().getTopInventory(), rawSlot);
|
|
case CONFIRM_BUY -> handleConfirmClick(player, holder, rawSlot);
|
|
case ADMIN -> handleAdminClick(player, holder, rawSlot, event.getClick());
|
|
}
|
|
}
|
|
|
|
public void handleDrag(InventoryDragEvent event) {
|
|
if (!(event.getWhoClicked() instanceof Player player) || !(event.getView().getTopInventory().getHolder() instanceof GuiHolder holder)) {
|
|
return;
|
|
}
|
|
int topSize = event.getView().getTopInventory().getSize();
|
|
boolean touchesTop = event.getRawSlots().stream().anyMatch(slot -> slot < topSize);
|
|
if (!touchesTop) {
|
|
return;
|
|
}
|
|
if (holder.type() == GuiType.SELL && event.getRawSlots().stream().allMatch(slot -> slot == SELL_ITEM_SLOT || slot >= topSize)) {
|
|
Bukkit.getScheduler().runTask(plugin, () -> syncSellSlot(player, event.getView().getTopInventory()));
|
|
return;
|
|
}
|
|
event.setCancelled(true);
|
|
}
|
|
|
|
public void handleClose(InventoryCloseEvent event) {
|
|
if (!(event.getPlayer() instanceof Player player) || !(event.getInventory().getHolder() instanceof GuiHolder holder)) {
|
|
return;
|
|
}
|
|
if (holder.type() != GuiType.SELL) {
|
|
return;
|
|
}
|
|
SellSession session = sellSessions.get(player.getUniqueId());
|
|
if (session == null || session.awaitingPrice() || session.completed()) {
|
|
return;
|
|
}
|
|
ItemStack item = event.getInventory().getItem(SELL_ITEM_SLOT);
|
|
if (InventoryUtil.isAir(item)) {
|
|
item = session.item();
|
|
}
|
|
returnItem(player, item);
|
|
sellSessions.remove(player.getUniqueId());
|
|
}
|
|
|
|
public void handleChat(AsyncChatEvent event) {
|
|
Player player = event.getPlayer();
|
|
SellSession session = sellSessions.get(player.getUniqueId());
|
|
if (session == null || !session.awaitingPrice()) {
|
|
return;
|
|
}
|
|
event.setCancelled(true);
|
|
String input = PlainTextComponentSerializer.plainText().serialize(event.message()).trim();
|
|
Bukkit.getScheduler().runTask(plugin, () -> completePricePrompt(player, input));
|
|
}
|
|
|
|
public void setSort(Player player, SortMode sortMode) {
|
|
browseState(player).sortMode(sortMode);
|
|
}
|
|
|
|
public void shutdown() {
|
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
|
if (player.getOpenInventory().getTopInventory().getHolder() instanceof GuiHolder) {
|
|
player.closeInventory();
|
|
}
|
|
}
|
|
for (UUID uuid : new ArrayList<>(sellSessions.keySet())) {
|
|
Player player = Bukkit.getPlayer(uuid);
|
|
SellSession session = sellSessions.remove(uuid);
|
|
if (player != null && session != null && !InventoryUtil.isAir(session.rawItem())) {
|
|
returnItem(player, session.rawItem());
|
|
}
|
|
if (session != null) {
|
|
session.cancelTimeout();
|
|
}
|
|
}
|
|
sellSessions.clear();
|
|
browseStates.clear();
|
|
clickDebounce.clear();
|
|
}
|
|
|
|
public void handleQuit(Player player) {
|
|
SellSession session = sellSessions.remove(player.getUniqueId());
|
|
if (session != null) {
|
|
session.cancelTimeout();
|
|
if (!InventoryUtil.isAir(session.rawItem())) {
|
|
returnItem(player, session.rawItem());
|
|
}
|
|
}
|
|
browseStates.remove(player.getUniqueId());
|
|
clickDebounce.remove(player.getUniqueId());
|
|
}
|
|
|
|
private void handleMainClick(Player player, int slot) {
|
|
play(player, "click");
|
|
if (slot == 11) {
|
|
openBrowse(player, 0);
|
|
} else if (slot == 13) {
|
|
openSell(player, true);
|
|
} else if (slot == 15) {
|
|
openMyListings(player, 0);
|
|
} else if (slot == 29) {
|
|
openClaims(player, 0);
|
|
} else if (slot == 31 && player.hasPermission("premiumah.admin")) {
|
|
openAdmin(player, 0);
|
|
} else if (slot == 33) {
|
|
player.closeInventory();
|
|
}
|
|
}
|
|
|
|
private void handleBrowseClick(Player player, GuiHolder holder, int slot, ClickType click) {
|
|
if (holder.listingSlots().containsKey(slot)) {
|
|
Listing listing = listings.get(holder.listingSlots().get(slot)).orElse(null);
|
|
if (listing == null) {
|
|
messages.send(player, "listing-no-longer-available");
|
|
play(player, "fail");
|
|
openBrowse(player, holder.page());
|
|
return;
|
|
}
|
|
if (click.isRightClick() && player.hasPermission("premiumah.admin.remove")) {
|
|
ActionResult result = auctionHouse.adminRemoveListing(listing.id());
|
|
messages.send(player, result.messageKey(), result.placeholders());
|
|
play(player, result.success() ? "success" : "fail");
|
|
openBrowse(player, holder.page());
|
|
return;
|
|
}
|
|
if (click.isShiftClick()) {
|
|
browseState(player).filter(listing.rawItem().getType());
|
|
openBrowse(player, 0);
|
|
return;
|
|
}
|
|
openConfirmBuy(player, listing.id());
|
|
return;
|
|
}
|
|
handlePagedControl(player, holder, slot, GuiType.BROWSE);
|
|
}
|
|
|
|
private void handleMyListingsClick(Player player, GuiHolder holder, int slot) {
|
|
if (holder.listingSlots().containsKey(slot)) {
|
|
ActionResult result = auctionHouse.cancelListing(player, holder.listingSlots().get(slot));
|
|
messages.send(player, result.messageKey(), result.placeholders());
|
|
play(player, result.success() ? "success" : "fail");
|
|
openMyListings(player, holder.page());
|
|
return;
|
|
}
|
|
handlePagedControl(player, holder, slot, GuiType.MY_LISTINGS);
|
|
}
|
|
|
|
private void handleClaimsClick(Player player, GuiHolder holder, int slot) {
|
|
if (holder.claimSlots().containsKey(slot)) {
|
|
ActionResult result = auctionHouse.claim(player, holder.claimSlots().get(slot));
|
|
messages.send(player, result.messageKey(), result.placeholders());
|
|
play(player, result.success() ? "success" : "fail");
|
|
openClaims(player, holder.page());
|
|
return;
|
|
}
|
|
handlePagedControl(player, holder, slot, GuiType.CLAIMS);
|
|
}
|
|
|
|
private void handleSellClick(Player player, Inventory inventory, int slot) {
|
|
play(player, "click");
|
|
if (slot == 20) {
|
|
beginPricePrompt(player, inventory);
|
|
} else if (slot == 24) {
|
|
confirmSell(player, inventory);
|
|
} else if (slot == 36) {
|
|
closeSellReturningItem(player, inventory, true);
|
|
openMain(player);
|
|
} else if (slot == 40) {
|
|
closeSellReturningItem(player, inventory, false);
|
|
}
|
|
}
|
|
|
|
private void handleConfirmClick(Player player, GuiHolder holder, int slot) {
|
|
if (slot == 11) {
|
|
ActionResult result = auctionHouse.buyListing(player, holder.context());
|
|
messages.send(player, result.messageKey(), result.placeholders());
|
|
play(player, result.success() ? "success" : "fail");
|
|
if (result.success()) {
|
|
openBrowse(player, 0);
|
|
} else {
|
|
player.closeInventory();
|
|
}
|
|
} else if (slot == 15) {
|
|
play(player, "click");
|
|
openBrowse(player, 0);
|
|
}
|
|
}
|
|
|
|
private void handleAdminClick(Player player, GuiHolder holder, int slot, ClickType click) {
|
|
if (holder.listingSlots().containsKey(slot)) {
|
|
Listing listing = listings.get(holder.listingSlots().get(slot)).orElse(null);
|
|
if (listing == null) {
|
|
messages.send(player, "listing-no-longer-available");
|
|
openAdmin(player, holder.page());
|
|
return;
|
|
}
|
|
if (click.isRightClick() || click.isShiftClick()) {
|
|
ActionResult result = auctionHouse.adminRemoveListing(listing.id());
|
|
messages.send(player, result.messageKey(), result.placeholders());
|
|
play(player, result.success() ? "success" : "fail");
|
|
openAdmin(player, holder.page());
|
|
} else {
|
|
openConfirmBuy(player, listing.id());
|
|
}
|
|
return;
|
|
}
|
|
handlePagedControl(player, holder, slot, GuiType.ADMIN);
|
|
}
|
|
|
|
private void handlePagedControl(Player player, GuiHolder holder, int slot, GuiType type) {
|
|
play(player, "click");
|
|
if (slot == 45) {
|
|
openByType(player, type, Math.max(0, holder.page() - 1), holder.context());
|
|
} else if (slot == 49) {
|
|
openMain(player);
|
|
} else if (slot == 53) {
|
|
openByType(player, type, holder.page() + 1, holder.context());
|
|
} else if (slot == 50 && (type == GuiType.BROWSE || type == GuiType.ADMIN)) {
|
|
BrowseState state = browseState(player);
|
|
state.sortMode(state.sortMode().next());
|
|
openByType(player, type, 0, holder.context());
|
|
} else if (slot == 48 && (type == GuiType.BROWSE || type == GuiType.ADMIN)) {
|
|
browseState(player).filter(null);
|
|
openByType(player, type, 0, holder.context());
|
|
}
|
|
}
|
|
|
|
private void openByType(Player player, GuiType type, int page, String context) {
|
|
switch (type) {
|
|
case BROWSE -> openBrowse(player, page);
|
|
case MY_LISTINGS -> openMyListings(player, page);
|
|
case CLAIMS -> openClaims(player, page);
|
|
case ADMIN -> {
|
|
if (context == null || context.isBlank()) {
|
|
openAdmin(player, page);
|
|
} else {
|
|
openAdminForSeller(player, context, page);
|
|
}
|
|
}
|
|
default -> openMain(player);
|
|
}
|
|
}
|
|
|
|
private void openListingView(Player player,
|
|
GuiType type,
|
|
List<Listing> source,
|
|
int requestedPage,
|
|
String titleKey,
|
|
String context,
|
|
SortMode sortMode,
|
|
Material filter) {
|
|
int page = normalizePage(requestedPage, source.size());
|
|
GuiHolder holder = new GuiHolder(type, player.getUniqueId(), page, context, sortMode, filter);
|
|
Inventory inventory = create(holder, config.guiSize(titleKey), config.guiTitle(titleKey), Map.of("page", String.valueOf(page + 1)));
|
|
fill(inventory);
|
|
List<Integer> slots = config.listingSlots();
|
|
int start = page * slots.size();
|
|
String loreKey = type == GuiType.MY_LISTINGS ? "my-listing" : "listing";
|
|
for (int i = 0; i < slots.size(); i++) {
|
|
int index = start + i;
|
|
if (index >= source.size()) {
|
|
break;
|
|
}
|
|
Listing listing = source.get(index);
|
|
int slot = slots.get(i);
|
|
holder.listingSlots().put(slot, listing.id());
|
|
inventory.setItem(slot, listingItem(listing, loreKey));
|
|
}
|
|
addPagedControls(inventory, page, source.size(), slots.size(), "back");
|
|
if (type == GuiType.BROWSE || type == GuiType.ADMIN) {
|
|
BrowseState state = browseState(player);
|
|
inventory.setItem(48, button("filter", Map.of("filter", state.filter() == null ? "All" : MaterialUtil.pretty(state.filter()))));
|
|
inventory.setItem(50, button("sort", Map.of("sort", state.sortMode().displayName())));
|
|
}
|
|
player.openInventory(inventory);
|
|
play(player, "open");
|
|
}
|
|
|
|
private void addPagedControls(Inventory inventory, int page, int totalItems, int pageSize, String backKey) {
|
|
if (page > 0) {
|
|
inventory.setItem(45, button("previous", Map.of()));
|
|
}
|
|
inventory.setItem(49, button(backKey, Map.of()));
|
|
if ((page + 1) * pageSize < totalItems) {
|
|
inventory.setItem(53, button("next", Map.of()));
|
|
}
|
|
}
|
|
|
|
private Inventory create(GuiHolder holder, int size, String title, Map<String, String> placeholders) {
|
|
Inventory inventory = Bukkit.createInventory(holder, size, messages.component(messages.apply(title, placeholders)));
|
|
holder.inventory(inventory);
|
|
return inventory;
|
|
}
|
|
|
|
private void fill(Inventory inventory) {
|
|
if (!config.fillerEnabled()) {
|
|
return;
|
|
}
|
|
ItemStack filler = customItem(config.fillerMaterial(), config.fillerName(), List.of(), Map.of());
|
|
for (int i = 0; i < inventory.getSize(); i++) {
|
|
if (inventory.getItem(i) == null) {
|
|
inventory.setItem(i, filler);
|
|
}
|
|
}
|
|
}
|
|
|
|
private ItemStack button(String key, Map<String, String> placeholders) {
|
|
ButtonConfig button = config.button(key);
|
|
return customItem(button.material(), button.name(), button.lore(), placeholders);
|
|
}
|
|
|
|
private ItemStack customItem(Material material, String name, List<String> lore, Map<String, String> placeholders) {
|
|
ItemStack item = new ItemStack(material);
|
|
ItemMeta meta = item.getItemMeta();
|
|
if (meta != null) {
|
|
meta.displayName(messages.component(messages.apply(name, placeholders)));
|
|
if (lore != null && !lore.isEmpty()) {
|
|
meta.lore(lore.stream().map(line -> messages.component(messages.apply(line, placeholders))).toList());
|
|
}
|
|
meta.addItemFlags(ItemFlag.values());
|
|
item.setItemMeta(meta);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
private ItemStack listingItem(Listing listing, String loreKey) {
|
|
ItemStack item = listing.item();
|
|
ItemMeta meta = item.getItemMeta();
|
|
if (meta != null) {
|
|
List<net.kyori.adventure.text.Component> lore = new ArrayList<>();
|
|
if (meta.lore() != null && !meta.lore().isEmpty()) {
|
|
lore.addAll(meta.lore());
|
|
lore.add(TextUtil.component(""));
|
|
}
|
|
lore.addAll(messages.guiLore(loreKey, listingPlaceholders(listing)));
|
|
meta.lore(lore);
|
|
item.setItemMeta(meta);
|
|
}
|
|
return item;
|
|
}
|
|
|
|
private ItemStack claimItem(ClaimRecord claim) {
|
|
ItemStack item;
|
|
if (claim.type() == ClaimType.MONEY) {
|
|
item = customItem(Material.EMERALD, "<#6ee7b7>Sale Payment <#ffffff>{amount}", messages.rawGuiLore("claim"), claimPlaceholders(claim));
|
|
} else {
|
|
item = claim.item();
|
|
ItemMeta meta = item.getItemMeta();
|
|
if (meta != null) {
|
|
List<net.kyori.adventure.text.Component> lore = new ArrayList<>();
|
|
if (meta.lore() != null && !meta.lore().isEmpty()) {
|
|
lore.addAll(meta.lore());
|
|
lore.add(TextUtil.component(""));
|
|
}
|
|
lore.addAll(messages.guiLore("claim", claimPlaceholders(claim)));
|
|
meta.lore(lore);
|
|
item.setItemMeta(meta);
|
|
}
|
|
}
|
|
return item;
|
|
}
|
|
|
|
private Map<String, String> listingPlaceholders(Listing listing) {
|
|
return Map.of(
|
|
"seller", listing.sellerName(),
|
|
"price", auctionHouse.formatMoney(listing.price()),
|
|
"remaining", com.yourname.premiumah.util.TimeUtil.remaining(listing.expiresAt()),
|
|
"id", listing.id()
|
|
);
|
|
}
|
|
|
|
private Map<String, String> claimPlaceholders(ClaimRecord claim) {
|
|
String itemName = claim.type() == ClaimType.MONEY ? auctionHouse.formatMoney(claim.moneyAmount()) : auctionHouse.itemName(claim.item());
|
|
Map<String, String> placeholders = new HashMap<>();
|
|
placeholders.put("reason", claim.reason().displayName());
|
|
placeholders.put("id", claim.listingId() == null ? "N/A" : claim.listingId());
|
|
placeholders.put("age", com.yourname.premiumah.util.TimeUtil.age(claim.createdAt()));
|
|
placeholders.put("item", itemName);
|
|
placeholders.put("amount", itemName);
|
|
return placeholders;
|
|
}
|
|
|
|
private BrowseState browseState(Player player) {
|
|
return browseStates.computeIfAbsent(player.getUniqueId(), ignored -> new BrowseState(config.defaultSort(), null));
|
|
}
|
|
|
|
private int normalizePage(int requestedPage, int totalItems) {
|
|
int pageSize = Math.max(1, config.listingSlots().size());
|
|
int maxPage = Math.max(0, (int) Math.ceil(totalItems / (double) pageSize) - 1);
|
|
return Math.max(0, Math.min(requestedPage, maxPage));
|
|
}
|
|
|
|
private boolean canClick(Player player) {
|
|
long now = System.currentTimeMillis();
|
|
long last = clickDebounce.getOrDefault(player.getUniqueId(), 0L);
|
|
if (now - last < config.clickDebounceMillis()) {
|
|
return false;
|
|
}
|
|
clickDebounce.put(player.getUniqueId(), now);
|
|
return true;
|
|
}
|
|
|
|
private void syncSellSlot(Player player, Inventory inventory) {
|
|
SellSession session = sellSessions.get(player.getUniqueId());
|
|
if (session != null) {
|
|
session.item(inventory.getItem(SELL_ITEM_SLOT));
|
|
}
|
|
}
|
|
|
|
private void beginPricePrompt(Player player, Inventory inventory) {
|
|
SellSession session = sellSessions.computeIfAbsent(player.getUniqueId(), ignored -> new SellSession());
|
|
session.item(inventory.getItem(SELL_ITEM_SLOT));
|
|
inventory.setItem(SELL_ITEM_SLOT, null);
|
|
session.awaitingPrice(true);
|
|
session.completed(false);
|
|
session.cancelTimeout();
|
|
session.timeoutTask(Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
|
SellSession current = sellSessions.get(player.getUniqueId());
|
|
if (current != null && current.awaitingPrice()) {
|
|
current.awaitingPrice(false);
|
|
returnItem(player, current.rawItem());
|
|
current.cancelTimeout();
|
|
sellSessions.remove(player.getUniqueId());
|
|
messages.send(player, "price-prompt-timeout");
|
|
play(player, "fail");
|
|
}
|
|
}, config.chatPriceTimeoutSeconds() * 20L));
|
|
player.closeInventory();
|
|
messages.send(player, "price-prompt");
|
|
}
|
|
|
|
private void completePricePrompt(Player player, String input) {
|
|
SellSession session = sellSessions.get(player.getUniqueId());
|
|
if (session == null || !session.awaitingPrice()) {
|
|
return;
|
|
}
|
|
session.cancelTimeout();
|
|
if (input.equalsIgnoreCase("cancel")) {
|
|
session.awaitingPrice(false);
|
|
returnItem(player, session.rawItem());
|
|
sellSessions.remove(player.getUniqueId());
|
|
messages.send(player, "price-prompt-cancelled");
|
|
play(player, "click");
|
|
return;
|
|
}
|
|
double price;
|
|
try {
|
|
price = Double.parseDouble(input.replace(",", ""));
|
|
} catch (NumberFormatException exception) {
|
|
messages.send(player, "invalid-price");
|
|
play(player, "fail");
|
|
beginPricePrompt(player, createTempSellInventory(player, session));
|
|
return;
|
|
}
|
|
if (!Double.isFinite(price) || price <= 0.0D) {
|
|
messages.send(player, "invalid-price");
|
|
play(player, "fail");
|
|
beginPricePrompt(player, createTempSellInventory(player, session));
|
|
return;
|
|
}
|
|
session.price(price);
|
|
session.awaitingPrice(false);
|
|
messages.send(player, "price-set", Map.of("price", auctionHouse.formatMoney(price)));
|
|
openSell(player, false);
|
|
}
|
|
|
|
private Inventory createTempSellInventory(Player player, SellSession session) {
|
|
GuiHolder holder = new GuiHolder(GuiType.SELL, player.getUniqueId(), 0, null, null, null);
|
|
Inventory inventory = create(holder, config.guiSize("sell"), config.guiTitle("sell"), Map.of());
|
|
inventory.setItem(SELL_ITEM_SLOT, session.item());
|
|
return inventory;
|
|
}
|
|
|
|
private void confirmSell(Player player, Inventory inventory) {
|
|
SellSession session = sellSessions.computeIfAbsent(player.getUniqueId(), ignored -> new SellSession());
|
|
ItemStack item = inventory.getItem(SELL_ITEM_SLOT);
|
|
if (InventoryUtil.isAir(item)) {
|
|
messages.send(player, "no-item-in-hand");
|
|
play(player, "fail");
|
|
return;
|
|
}
|
|
if (session.price() <= 0.0D) {
|
|
messages.send(player, "invalid-price");
|
|
play(player, "fail");
|
|
return;
|
|
}
|
|
inventory.setItem(SELL_ITEM_SLOT, null);
|
|
ListingCreationResult result = auctionHouse.createListing(player, item, session.price());
|
|
if (!result.success()) {
|
|
inventory.setItem(SELL_ITEM_SLOT, item);
|
|
messages.send(player, result.messageKey(), result.placeholders());
|
|
play(player, "fail");
|
|
return;
|
|
}
|
|
session.completed(true);
|
|
session.cancelTimeout();
|
|
sellSessions.remove(player.getUniqueId());
|
|
messages.send(player, result.messageKey(), result.placeholders());
|
|
play(player, "success");
|
|
openBrowse(player, 0);
|
|
}
|
|
|
|
private void closeSellReturningItem(Player player, Inventory inventory, boolean keepOpen) {
|
|
SellSession session = sellSessions.remove(player.getUniqueId());
|
|
ItemStack item = inventory.getItem(SELL_ITEM_SLOT);
|
|
inventory.setItem(SELL_ITEM_SLOT, null);
|
|
if (session != null) {
|
|
session.completed(true);
|
|
session.cancelTimeout();
|
|
if (InventoryUtil.isAir(item)) {
|
|
item = session.rawItem();
|
|
}
|
|
}
|
|
returnItem(player, item);
|
|
if (!keepOpen) {
|
|
player.closeInventory();
|
|
}
|
|
}
|
|
|
|
private void returnItem(Player player, ItemStack item) {
|
|
if (InventoryUtil.isAir(item)) {
|
|
return;
|
|
}
|
|
Map<Integer, ItemStack> leftovers = InventoryUtil.addItem(player.getInventory(), item);
|
|
for (ItemStack leftover : leftovers.values()) {
|
|
player.getWorld().dropItemNaturally(player.getLocation(), leftover);
|
|
}
|
|
}
|
|
|
|
private void play(Player player, String key) {
|
|
if (!config.soundsEnabled()) {
|
|
return;
|
|
}
|
|
String soundName = config.soundName(key);
|
|
if (soundName == null || soundName.isBlank()) {
|
|
return;
|
|
}
|
|
try {
|
|
String soundKey = soundName.toLowerCase(Locale.ROOT).replace('_', '.');
|
|
Sound sound = Registry.SOUNDS.get(NamespacedKey.minecraft(soundKey));
|
|
if (sound == null) {
|
|
return;
|
|
}
|
|
player.playSound(player.getLocation(), sound, 0.7F, 1.0F);
|
|
} catch (IllegalArgumentException ignored) {
|
|
}
|
|
}
|
|
}
|