2021-07-14 15:23:55 +02:00
|
|
|
package net.citizensnpcs.trait;
|
|
|
|
|
2022-02-19 13:27:41 +01:00
|
|
|
import java.util.List;
|
2021-07-14 15:23:55 +02:00
|
|
|
import java.util.Map;
|
2023-08-13 19:20:41 +02:00
|
|
|
import java.util.UUID;
|
2022-07-17 16:29:11 +02:00
|
|
|
import java.util.function.Consumer;
|
2022-07-26 17:50:28 +02:00
|
|
|
import java.util.function.Function;
|
2023-01-01 16:28:21 +01:00
|
|
|
import java.util.regex.Matcher;
|
|
|
|
import java.util.regex.Pattern;
|
2021-07-14 15:23:55 +02:00
|
|
|
|
2022-07-21 18:43:40 +02:00
|
|
|
import org.bukkit.Bukkit;
|
2022-07-17 17:01:59 +02:00
|
|
|
import org.bukkit.ChatColor;
|
2022-04-20 05:55:15 +02:00
|
|
|
import org.bukkit.Material;
|
2022-07-17 16:29:11 +02:00
|
|
|
import org.bukkit.entity.HumanEntity;
|
2021-07-14 15:23:55 +02:00
|
|
|
import org.bukkit.entity.Player;
|
2023-05-07 16:03:07 +02:00
|
|
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
2022-04-20 05:55:15 +02:00
|
|
|
import org.bukkit.event.inventory.InventoryType;
|
2022-07-21 18:43:40 +02:00
|
|
|
import org.bukkit.inventory.Inventory;
|
2022-02-19 13:27:41 +01:00
|
|
|
import org.bukkit.inventory.ItemStack;
|
2022-07-17 16:29:11 +02:00
|
|
|
import org.bukkit.inventory.meta.ItemMeta;
|
2021-07-14 15:23:55 +02:00
|
|
|
|
2022-07-17 16:29:11 +02:00
|
|
|
import com.google.common.base.Joiner;
|
|
|
|
import com.google.common.base.Splitter;
|
2023-01-01 16:28:21 +01:00
|
|
|
import com.google.common.collect.Iterables;
|
2022-02-19 13:27:41 +01:00
|
|
|
import com.google.common.collect.Lists;
|
2021-07-14 15:23:55 +02:00
|
|
|
import com.google.common.collect.Maps;
|
|
|
|
|
2023-05-20 11:10:53 +02:00
|
|
|
import net.citizensnpcs.Settings.Setting;
|
2022-12-03 14:32:58 +01:00
|
|
|
import net.citizensnpcs.StoredShops;
|
2022-07-21 18:43:40 +02:00
|
|
|
import net.citizensnpcs.api.CitizensAPI;
|
2022-04-20 05:55:15 +02:00
|
|
|
import net.citizensnpcs.api.gui.CitizensInventoryClickEvent;
|
|
|
|
import net.citizensnpcs.api.gui.ClickHandler;
|
2022-06-14 15:26:18 +02:00
|
|
|
import net.citizensnpcs.api.gui.InputMenus;
|
2022-04-20 05:55:15 +02:00
|
|
|
import net.citizensnpcs.api.gui.InventoryMenu;
|
|
|
|
import net.citizensnpcs.api.gui.InventoryMenuPage;
|
2022-07-26 17:50:28 +02:00
|
|
|
import net.citizensnpcs.api.gui.InventoryMenuPattern;
|
2022-04-20 05:55:15 +02:00
|
|
|
import net.citizensnpcs.api.gui.InventoryMenuSlot;
|
|
|
|
import net.citizensnpcs.api.gui.Menu;
|
|
|
|
import net.citizensnpcs.api.gui.MenuContext;
|
2022-07-26 17:50:28 +02:00
|
|
|
import net.citizensnpcs.api.gui.MenuPattern;
|
2022-04-20 05:55:15 +02:00
|
|
|
import net.citizensnpcs.api.gui.MenuSlot;
|
2023-01-01 08:26:35 +01:00
|
|
|
import net.citizensnpcs.api.npc.NPC;
|
2021-07-14 15:23:55 +02:00
|
|
|
import net.citizensnpcs.api.persistence.Persist;
|
2023-01-01 16:28:21 +01:00
|
|
|
import net.citizensnpcs.api.persistence.Persistable;
|
2021-07-14 15:23:55 +02:00
|
|
|
import net.citizensnpcs.api.trait.Trait;
|
|
|
|
import net.citizensnpcs.api.trait.TraitName;
|
2023-01-01 08:26:35 +01:00
|
|
|
import net.citizensnpcs.api.trait.trait.Owner;
|
2023-01-01 16:28:21 +01:00
|
|
|
import net.citizensnpcs.api.util.DataKey;
|
2022-11-23 12:03:06 +01:00
|
|
|
import net.citizensnpcs.api.util.Messaging;
|
2022-07-29 03:15:15 +02:00
|
|
|
import net.citizensnpcs.api.util.Placeholders;
|
2022-12-12 00:28:12 +01:00
|
|
|
import net.citizensnpcs.trait.shop.CommandAction;
|
2022-12-10 06:42:07 +01:00
|
|
|
import net.citizensnpcs.trait.shop.CommandAction.CommandActionGUI;
|
2023-03-16 15:01:04 +01:00
|
|
|
import net.citizensnpcs.trait.shop.ExperienceAction;
|
|
|
|
import net.citizensnpcs.trait.shop.ExperienceAction.ExperienceActionGUI;
|
2022-07-27 11:05:52 +02:00
|
|
|
import net.citizensnpcs.trait.shop.ItemAction;
|
|
|
|
import net.citizensnpcs.trait.shop.ItemAction.ItemActionGUI;
|
|
|
|
import net.citizensnpcs.trait.shop.MoneyAction;
|
|
|
|
import net.citizensnpcs.trait.shop.MoneyAction.MoneyActionGUI;
|
2022-07-21 17:13:51 +02:00
|
|
|
import net.citizensnpcs.trait.shop.NPCShopAction;
|
2022-07-26 17:50:28 +02:00
|
|
|
import net.citizensnpcs.trait.shop.NPCShopAction.GUI;
|
2022-07-26 19:29:10 +02:00
|
|
|
import net.citizensnpcs.trait.shop.NPCShopAction.Transaction;
|
2022-07-27 11:05:52 +02:00
|
|
|
import net.citizensnpcs.trait.shop.PermissionAction;
|
|
|
|
import net.citizensnpcs.trait.shop.PermissionAction.PermissionActionGUI;
|
|
|
|
import net.citizensnpcs.util.Util;
|
2021-07-14 15:23:55 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Shop trait for NPC GUI shops.
|
|
|
|
*/
|
|
|
|
@TraitName("shop")
|
|
|
|
public class ShopTrait extends Trait {
|
2022-11-29 22:35:31 +01:00
|
|
|
@Persist
|
|
|
|
private String rightClickShop;
|
2022-12-03 14:32:58 +01:00
|
|
|
private StoredShops shops;
|
2022-11-29 22:35:31 +01:00
|
|
|
|
2021-07-14 15:23:55 +02:00
|
|
|
public ShopTrait() {
|
|
|
|
super("shop");
|
|
|
|
}
|
|
|
|
|
2022-12-03 14:32:58 +01:00
|
|
|
public ShopTrait(StoredShops shops) {
|
|
|
|
this();
|
|
|
|
this.shops = shops;
|
|
|
|
}
|
|
|
|
|
2021-07-14 15:23:55 +02:00
|
|
|
public NPCShop getDefaultShop() {
|
2023-01-31 15:50:25 +01:00
|
|
|
return shops.npcShops.computeIfAbsent(npc.getUniqueId().toString(), NPCShop::new);
|
2021-07-14 15:23:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public NPCShop getShop(String name) {
|
2023-01-31 15:50:25 +01:00
|
|
|
return shops.globalShops.computeIfAbsent(name, NPCShop::new);
|
2021-07-14 15:23:55 +02:00
|
|
|
}
|
|
|
|
|
2022-12-06 15:54:27 +01:00
|
|
|
@Override
|
|
|
|
public void onRemove() {
|
2023-01-31 15:50:25 +01:00
|
|
|
Messaging.debug("Removing", npc, "default shop due to onRemove");
|
2022-12-29 16:26:02 +01:00
|
|
|
shops.deleteShop(getDefaultShop());
|
2022-12-06 15:54:27 +01:00
|
|
|
}
|
|
|
|
|
2022-10-22 04:42:44 +02:00
|
|
|
public void onRightClick(Player player) {
|
2023-11-05 10:41:18 +01:00
|
|
|
if (rightClickShop == null || rightClickShop.isEmpty()
|
|
|
|
|| !Setting.SHOP_GLOBAL_VIEW_PERMISSION.asString().isEmpty()
|
|
|
|
&& !player.hasPermission(Setting.SHOP_GLOBAL_VIEW_PERMISSION.asString()))
|
2023-05-20 11:10:53 +02:00
|
|
|
return;
|
|
|
|
|
2022-12-03 14:32:58 +01:00
|
|
|
NPCShop shop = shops.globalShops.getOrDefault(rightClickShop, getDefaultShop());
|
2022-11-29 22:35:31 +01:00
|
|
|
shop.display(player);
|
2022-10-22 04:42:44 +02:00
|
|
|
}
|
|
|
|
|
2021-07-14 15:23:55 +02:00
|
|
|
public static class NPCShop {
|
2022-07-21 17:13:51 +02:00
|
|
|
@Persist(value = "")
|
2022-07-27 11:05:52 +02:00
|
|
|
private String name;
|
2022-02-19 13:27:41 +01:00
|
|
|
@Persist(reify = true)
|
|
|
|
private final List<NPCShopPage> pages = Lists.newArrayList();
|
|
|
|
@Persist
|
2022-07-17 16:29:11 +02:00
|
|
|
private String title;
|
|
|
|
@Persist
|
2022-07-16 20:10:38 +02:00
|
|
|
private String viewPermission;
|
2021-07-14 15:23:55 +02:00
|
|
|
|
2022-07-27 11:05:52 +02:00
|
|
|
private NPCShop() {
|
|
|
|
}
|
|
|
|
|
2022-12-18 18:44:16 +01:00
|
|
|
public NPCShop(String name) {
|
2021-07-14 15:23:55 +02:00
|
|
|
this.name = name;
|
|
|
|
}
|
|
|
|
|
2023-01-01 08:26:35 +01:00
|
|
|
public boolean canEdit(NPC npc, Player sender) {
|
|
|
|
return sender.hasPermission("citizens.admin") || sender.hasPermission("citizens.npc.shop.edit")
|
|
|
|
|| sender.hasPermission("citizens.npc.shop.edit." + getName())
|
|
|
|
|| npc.getOrAddTrait(Owner.class).isOwnedBy(sender);
|
|
|
|
}
|
|
|
|
|
2021-07-14 15:23:55 +02:00
|
|
|
public void display(Player sender) {
|
2023-11-05 10:41:18 +01:00
|
|
|
if (viewPermission != null && !sender.hasPermission(viewPermission)
|
|
|
|
|| !Setting.SHOP_GLOBAL_VIEW_PERMISSION.asString().isEmpty()
|
|
|
|
&& !sender.hasPermission(Setting.SHOP_GLOBAL_VIEW_PERMISSION.asString()))
|
2023-08-06 12:57:42 +02:00
|
|
|
return;
|
2023-10-08 18:54:31 +02:00
|
|
|
|
2022-07-21 18:43:40 +02:00
|
|
|
if (pages.size() == 0) {
|
2023-01-01 08:26:35 +01:00
|
|
|
Messaging.sendError(sender, "Empty shop");
|
2022-07-21 18:43:40 +02:00
|
|
|
return;
|
|
|
|
}
|
2022-07-29 03:15:15 +02:00
|
|
|
InventoryMenu.createSelfRegistered(new NPCShopViewer(this, sender)).present(sender);
|
2021-07-14 15:23:55 +02:00
|
|
|
}
|
2022-01-06 12:25:01 +01:00
|
|
|
|
2022-11-29 22:35:31 +01:00
|
|
|
public void displayEditor(ShopTrait trait, Player sender) {
|
|
|
|
InventoryMenu.createSelfRegistered(new NPCShopSettings(trait, this)).present(sender);
|
2022-01-06 12:25:01 +01:00
|
|
|
}
|
2022-02-19 13:27:41 +01:00
|
|
|
|
|
|
|
public String getName() {
|
2022-12-03 11:21:46 +01:00
|
|
|
return name == null ? "" : name;
|
2022-02-19 13:27:41 +01:00
|
|
|
}
|
2022-06-11 19:37:38 +02:00
|
|
|
|
2022-07-17 16:29:11 +02:00
|
|
|
public NPCShopPage getOrCreatePage(int page) {
|
|
|
|
while (pages.size() <= page) {
|
|
|
|
pages.add(new NPCShopPage(page));
|
|
|
|
}
|
|
|
|
return pages.get(page);
|
|
|
|
}
|
|
|
|
|
2022-06-11 19:37:38 +02:00
|
|
|
public String getRequiredPermission() {
|
2022-07-16 20:10:38 +02:00
|
|
|
return viewPermission;
|
2022-06-11 19:37:38 +02:00
|
|
|
}
|
|
|
|
|
2022-07-17 16:29:11 +02:00
|
|
|
public void removePage(int index) {
|
|
|
|
for (int i = 0; i < pages.size(); i++) {
|
|
|
|
if (pages.get(i).index == index) {
|
|
|
|
pages.remove(i--);
|
|
|
|
index = -1;
|
|
|
|
} else if (index == -1) {
|
|
|
|
pages.get(i).index--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-11 19:37:38 +02:00
|
|
|
public void setPermission(String permission) {
|
2023-11-05 10:41:18 +01:00
|
|
|
viewPermission = permission;
|
2022-07-16 20:10:38 +02:00
|
|
|
if (viewPermission != null && viewPermission.isEmpty()) {
|
|
|
|
viewPermission = null;
|
2022-06-11 19:37:38 +02:00
|
|
|
}
|
|
|
|
}
|
2022-02-19 13:27:41 +01:00
|
|
|
}
|
|
|
|
|
2022-07-17 16:29:11 +02:00
|
|
|
@Menu(title = "NPC Shop Contents Editor", type = InventoryType.CHEST, dimensions = { 5, 9 })
|
|
|
|
public static class NPCShopContentsEditor extends InventoryMenuPage {
|
2023-05-07 16:43:05 +02:00
|
|
|
private NPCShopItem copying;
|
2022-07-17 16:29:11 +02:00
|
|
|
private MenuContext ctx;
|
|
|
|
private int page = 0;
|
|
|
|
private final NPCShop shop;
|
|
|
|
|
|
|
|
public NPCShopContentsEditor(NPCShop shop) {
|
|
|
|
this.shop = shop;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void changePage(int newPage) {
|
2023-11-05 10:41:18 +01:00
|
|
|
page = newPage;
|
|
|
|
ctx.setTitle("NPC Shop Contents Editor (" + (newPage + 1) + "/" + (shop.pages.size() + 1) + ")");
|
2022-10-26 09:44:03 +02:00
|
|
|
NPCShopPage shopPage = shop.getOrCreatePage(page);
|
2022-07-17 16:29:11 +02:00
|
|
|
for (int i = 0; i < ctx.getInventory().getSize(); i++) {
|
2022-10-26 09:44:03 +02:00
|
|
|
InventoryMenuSlot slot = ctx.getSlot(i);
|
|
|
|
slot.clear();
|
|
|
|
|
2023-05-07 16:43:05 +02:00
|
|
|
if (shopPage.getItem(i) != null) {
|
|
|
|
slot.setItemStack(shopPage.getItem(i).getDisplayItem(null));
|
2022-10-26 09:44:03 +02:00
|
|
|
}
|
2023-11-05 10:41:18 +01:00
|
|
|
int idx = i;
|
2022-10-26 09:44:03 +02:00
|
|
|
slot.setClickHandler(evt -> {
|
2023-05-07 16:43:05 +02:00
|
|
|
NPCShopItem display = shopPage.getItem(idx);
|
|
|
|
if (display != null && evt.isShiftClick() && evt.getCursorNonNull().getType() == Material.AIR
|
|
|
|
&& display.display != null) {
|
|
|
|
copying = display.clone();
|
|
|
|
evt.setCursor(display.getDisplayItem(null));
|
|
|
|
evt.setCancelled(true);
|
|
|
|
return;
|
|
|
|
}
|
2022-07-17 16:29:11 +02:00
|
|
|
if (display == null) {
|
2023-05-07 16:43:05 +02:00
|
|
|
if (copying != null && evt.getCursorNonNull().getType() != Material.AIR
|
|
|
|
&& evt.getCursorNonNull().equals(copying.getDisplayItem(null))) {
|
|
|
|
shopPage.setItem(idx, copying);
|
|
|
|
copying = null;
|
|
|
|
return;
|
|
|
|
}
|
2022-07-17 16:29:11 +02:00
|
|
|
display = new NPCShopItem();
|
|
|
|
if (evt.getCursor() != null) {
|
|
|
|
display.display = evt.getCursor().clone();
|
|
|
|
}
|
|
|
|
}
|
2023-05-07 16:43:05 +02:00
|
|
|
ctx.clearSlots();
|
2022-07-17 16:29:11 +02:00
|
|
|
ctx.getMenu().transition(new NPCShopItemEditor(display, modified -> {
|
|
|
|
if (modified == null) {
|
2022-10-26 09:44:03 +02:00
|
|
|
shopPage.removeItem(idx);
|
2022-07-17 16:29:11 +02:00
|
|
|
} else {
|
2022-10-26 09:44:03 +02:00
|
|
|
shopPage.setItem(idx, modified);
|
2022-07-17 16:29:11 +02:00
|
|
|
}
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
InventoryMenuSlot prev = ctx.getSlot(4 * 9 + 3);
|
|
|
|
InventoryMenuSlot edit = ctx.getSlot(4 * 9 + 4);
|
|
|
|
InventoryMenuSlot next = ctx.getSlot(4 * 9 + 5);
|
|
|
|
if (page > 0) {
|
2023-11-05 10:41:18 +01:00
|
|
|
prev.setItemStack(shopPage.getNextPageItem(null, 4 * 9 + 3), "Previous page (" + newPage + ")");
|
2023-06-14 17:19:05 +02:00
|
|
|
Consumer<CitizensInventoryClickEvent> prevItemEditor = prev.getClickHandlers().get(0);
|
2022-07-26 21:40:46 +02:00
|
|
|
prev.setClickHandler(evt -> {
|
2023-06-14 17:19:05 +02:00
|
|
|
if (evt.isShiftClick()) {
|
|
|
|
prevItemEditor.accept(evt);
|
|
|
|
return;
|
|
|
|
}
|
2022-07-17 16:29:11 +02:00
|
|
|
evt.setCancelled(true);
|
|
|
|
changePage(page - 1);
|
|
|
|
});
|
|
|
|
}
|
2023-11-05 10:41:18 +01:00
|
|
|
next.setItemStack(shopPage.getNextPageItem(null, 4 * 9 + 5),
|
|
|
|
page + 1 >= shop.pages.size() ? "New page" : "Next page (" + (newPage + 1) + ")");
|
2023-06-14 17:19:05 +02:00
|
|
|
Consumer<CitizensInventoryClickEvent> nextItemEditor = next.getClickHandlers().get(0);
|
2022-07-26 21:40:46 +02:00
|
|
|
next.setClickHandler(evt -> {
|
2023-06-14 17:19:05 +02:00
|
|
|
if (evt.isShiftClick()) {
|
|
|
|
nextItemEditor.accept(evt);
|
|
|
|
return;
|
|
|
|
}
|
2022-07-17 16:29:11 +02:00
|
|
|
evt.setCancelled(true);
|
|
|
|
changePage(page + 1);
|
|
|
|
});
|
|
|
|
|
2023-06-14 17:19:05 +02:00
|
|
|
Consumer<CitizensInventoryClickEvent> editPageItem = edit.getClickHandlers().get(0);
|
2022-07-17 16:29:11 +02:00
|
|
|
edit.setItemStack(new ItemStack(Material.BOOK), "Edit page");
|
2023-06-14 17:19:05 +02:00
|
|
|
edit.setClickHandler(evt -> {
|
|
|
|
if (evt.isShiftClick()) {
|
|
|
|
editPageItem.accept(evt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ctx.getMenu().transition(new NPCShopPageSettings(shop.getOrCreatePage(page)));
|
|
|
|
});
|
2022-07-17 16:29:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void initialise(MenuContext ctx) {
|
|
|
|
this.ctx = ctx;
|
|
|
|
if (ctx.data().containsKey("removePage")) {
|
|
|
|
int index = (int) ctx.data().remove("removePage");
|
|
|
|
shop.removePage(index);
|
|
|
|
page = Math.max(page - 1, 0);
|
|
|
|
}
|
|
|
|
changePage(page);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-01 16:28:21 +01:00
|
|
|
public static class NPCShopItem implements Cloneable, Persistable {
|
2023-10-08 18:54:31 +02:00
|
|
|
@Persist
|
|
|
|
private String alreadyPurchasedMessage;
|
2023-01-01 16:28:21 +01:00
|
|
|
@Persist
|
|
|
|
private String clickToConfirmMessage;
|
2022-02-19 13:27:41 +01:00
|
|
|
@Persist
|
2022-07-26 17:50:28 +02:00
|
|
|
private final List<NPCShopAction> cost = Lists.newArrayList();
|
2022-02-19 13:27:41 +01:00
|
|
|
@Persist
|
2023-05-07 16:03:07 +02:00
|
|
|
private String costMessage;
|
|
|
|
@Persist
|
2022-02-19 13:27:41 +01:00
|
|
|
private ItemStack display;
|
2022-07-21 17:13:51 +02:00
|
|
|
@Persist
|
2023-04-05 20:35:08 +02:00
|
|
|
private boolean maxRepeatsOnShiftClick;
|
2023-10-08 18:54:31 +02:00
|
|
|
@Persist(keyType = UUID.class)
|
|
|
|
private final Map<UUID, Integer> purchases = Maps.newHashMap();
|
2023-04-05 20:35:08 +02:00
|
|
|
@Persist
|
2022-07-26 17:50:28 +02:00
|
|
|
private final List<NPCShopAction> result = Lists.newArrayList();
|
2023-05-07 16:03:07 +02:00
|
|
|
@Persist
|
|
|
|
private String resultMessage;
|
2023-08-13 19:20:41 +02:00
|
|
|
@Persist
|
|
|
|
private int timesPurchasable = 0;
|
2022-07-26 17:50:28 +02:00
|
|
|
|
2023-04-05 20:35:08 +02:00
|
|
|
public List<Transaction> apply(List<NPCShopAction> actions, Function<NPCShopAction, Transaction> func) {
|
|
|
|
List<Transaction> pending = Lists.newArrayList();
|
|
|
|
for (NPCShopAction action : actions) {
|
|
|
|
Transaction take = func.apply(action);
|
|
|
|
if (!take.isPossible()) {
|
2023-11-05 10:41:18 +01:00
|
|
|
pending.forEach(Transaction::rollback);
|
2023-04-05 20:35:08 +02:00
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
take.run();
|
|
|
|
pending.add(take);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return pending;
|
|
|
|
}
|
|
|
|
|
2022-07-26 17:50:28 +02:00
|
|
|
private void changeAction(List<NPCShopAction> source, Function<NPCShopAction, Boolean> filter,
|
|
|
|
NPCShopAction delta) {
|
|
|
|
for (int i = 0; i < source.size(); i++) {
|
|
|
|
if (filter.apply(source.get(i))) {
|
|
|
|
if (delta == null) {
|
|
|
|
source.remove(i);
|
|
|
|
} else {
|
|
|
|
source.set(i, delta);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (delta != null) {
|
|
|
|
source.add(delta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void changeCost(Function<NPCShopAction, Boolean> filter, NPCShopAction cost) {
|
|
|
|
changeAction(this.cost, filter, cost);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void changeResult(Function<NPCShopAction, Boolean> filter, NPCShopAction result) {
|
|
|
|
changeAction(this.result, filter, result);
|
|
|
|
}
|
2022-07-17 16:29:11 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public NPCShopItem clone() {
|
|
|
|
try {
|
|
|
|
return (NPCShopItem) super.clone();
|
|
|
|
} catch (CloneNotSupportedException e) {
|
|
|
|
throw new Error(e);
|
|
|
|
}
|
|
|
|
}
|
2022-07-21 18:43:40 +02:00
|
|
|
|
2022-07-29 03:15:15 +02:00
|
|
|
public ItemStack getDisplayItem(Player player) {
|
|
|
|
if (display == null)
|
|
|
|
return null;
|
|
|
|
ItemStack stack = display.clone();
|
|
|
|
ItemMeta meta = stack.getItemMeta();
|
|
|
|
if (meta.hasDisplayName()) {
|
2023-01-01 16:28:21 +01:00
|
|
|
meta.setDisplayName(placeholders(meta.getDisplayName(), player));
|
2022-07-29 03:15:15 +02:00
|
|
|
}
|
2022-11-28 16:10:50 +01:00
|
|
|
if (!meta.hasLore()) {
|
|
|
|
List<String> lore = Lists.newArrayList();
|
2023-10-14 16:41:10 +02:00
|
|
|
cost.forEach(c -> lore.add(c.describe()));
|
|
|
|
result.forEach(r -> {
|
|
|
|
if (!(r instanceof CommandAction)) {
|
|
|
|
lore.add(r.describe());
|
|
|
|
}
|
|
|
|
});
|
2023-11-05 10:41:18 +01:00
|
|
|
|
2023-08-13 19:20:41 +02:00
|
|
|
if (timesPurchasable > 0) {
|
|
|
|
lore.add("Times purchasable: " + timesPurchasable);
|
|
|
|
}
|
|
|
|
meta.setLore(lore);
|
2022-11-28 16:10:50 +01:00
|
|
|
}
|
2022-07-29 03:15:15 +02:00
|
|
|
if (meta.hasLore()) {
|
2023-01-01 16:28:21 +01:00
|
|
|
meta.setLore(Lists.transform(meta.getLore(), line -> placeholders(line, player)));
|
2022-07-29 03:15:15 +02:00
|
|
|
}
|
|
|
|
stack.setItemMeta(meta);
|
|
|
|
return stack;
|
|
|
|
}
|
|
|
|
|
2023-01-01 16:28:21 +01:00
|
|
|
@Override
|
|
|
|
public void load(DataKey key) {
|
|
|
|
if (key.keyExists("message")) {
|
2023-05-07 16:03:07 +02:00
|
|
|
resultMessage = key.getString("message");
|
2023-01-01 16:28:21 +01:00
|
|
|
key.removeKey("message");
|
|
|
|
}
|
2023-05-07 16:03:07 +02:00
|
|
|
if (key.keyExists("clickMessage")) {
|
|
|
|
resultMessage = key.getString("clickMessage");
|
|
|
|
key.removeKey("clickMessage");
|
|
|
|
}
|
2023-01-01 16:28:21 +01:00
|
|
|
}
|
|
|
|
|
2023-05-07 16:03:07 +02:00
|
|
|
public void onClick(NPCShop shop, InventoryClickEvent event, boolean secondClick) {
|
2023-08-13 19:20:41 +02:00
|
|
|
Player player = (Player) event.getWhoClicked();
|
2023-10-08 18:54:31 +02:00
|
|
|
if (purchases.containsKey(player.getUniqueId()) && timesPurchasable > 0
|
|
|
|
&& purchases.get(player.getUniqueId()) == timesPurchasable) {
|
2023-08-13 19:20:41 +02:00
|
|
|
if (alreadyPurchasedMessage != null) {
|
2023-10-08 18:54:31 +02:00
|
|
|
Messaging.sendColorless(event.getWhoClicked(), placeholders(alreadyPurchasedMessage, player));
|
2023-08-13 19:20:41 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2023-01-01 16:28:21 +01:00
|
|
|
if (clickToConfirmMessage != null && !secondClick) {
|
2023-10-08 18:54:31 +02:00
|
|
|
Messaging.sendColorless(event.getWhoClicked(), placeholders(clickToConfirmMessage, player));
|
2023-01-01 16:28:21 +01:00
|
|
|
return;
|
|
|
|
}
|
2023-04-05 20:35:08 +02:00
|
|
|
int max = Integer.MAX_VALUE;
|
|
|
|
if (maxRepeatsOnShiftClick && event.isShiftClick()) {
|
|
|
|
for (NPCShopAction action : cost) {
|
|
|
|
int r = action.getMaxRepeats(event.getWhoClicked());
|
|
|
|
if (r != -1) {
|
|
|
|
max = Math.min(max, r);
|
|
|
|
}
|
|
|
|
}
|
2023-04-07 20:35:55 +02:00
|
|
|
if (max == 0)
|
|
|
|
return;
|
2023-04-05 20:35:08 +02:00
|
|
|
}
|
2023-11-05 10:41:18 +01:00
|
|
|
int repeats = max == Integer.MAX_VALUE ? 1 : max;
|
2023-04-05 20:35:08 +02:00
|
|
|
List<Transaction> take = apply(cost, action -> action.take(event.getWhoClicked(), repeats));
|
2023-05-07 16:03:07 +02:00
|
|
|
if (take == null) {
|
|
|
|
if (costMessage != null) {
|
2023-10-08 18:54:31 +02:00
|
|
|
Messaging.sendColorless(event.getWhoClicked(), placeholders(costMessage, player));
|
2023-05-07 16:03:07 +02:00
|
|
|
}
|
2022-10-26 06:33:23 +02:00
|
|
|
return;
|
2023-05-07 16:03:07 +02:00
|
|
|
}
|
2023-04-05 20:35:08 +02:00
|
|
|
if (apply(result, action -> action.grant(event.getWhoClicked(), repeats)) == null) {
|
2023-11-05 10:41:18 +01:00
|
|
|
take.forEach(Transaction::rollback);
|
2023-04-05 20:35:08 +02:00
|
|
|
return;
|
2022-07-26 18:19:47 +02:00
|
|
|
}
|
2023-05-07 16:03:07 +02:00
|
|
|
if (resultMessage != null) {
|
2023-10-08 18:54:31 +02:00
|
|
|
Messaging.sendColorless(event.getWhoClicked(), placeholders(resultMessage, player));
|
2023-08-13 19:20:41 +02:00
|
|
|
}
|
|
|
|
if (timesPurchasable > 0) {
|
2023-10-08 18:54:31 +02:00
|
|
|
int timesPurchasedAlready = purchases.get(player.getUniqueId()) == null ? 0
|
|
|
|
: purchases.get(player.getUniqueId());
|
2023-08-13 19:20:41 +02:00
|
|
|
purchases.put(player.getUniqueId(), ++timesPurchasedAlready);
|
2022-12-11 14:28:39 +01:00
|
|
|
}
|
2022-07-21 18:43:40 +02:00
|
|
|
}
|
2023-01-01 16:28:21 +01:00
|
|
|
|
|
|
|
private String placeholders(String string, Player player) {
|
|
|
|
string = Placeholders.replace(string, player);
|
|
|
|
StringBuffer sb = new StringBuffer();
|
|
|
|
Matcher matcher = PLACEHOLDER_REGEX.matcher(string);
|
|
|
|
while (matcher.find()) {
|
2024-02-18 14:06:02 +01:00
|
|
|
matcher.appendReplacement(sb,
|
|
|
|
Joiner.on(", ")
|
|
|
|
.join(Iterables.transform(matcher.group(1).equalsIgnoreCase("cost") ? cost : result,
|
|
|
|
NPCShopAction::describe))
|
|
|
|
.replace("$", "\\$").replace("{", "\\{"));
|
2023-01-01 16:28:21 +01:00
|
|
|
}
|
|
|
|
matcher.appendTail(sb);
|
|
|
|
return sb.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void save(DataKey key) {
|
|
|
|
}
|
|
|
|
|
2023-11-05 10:41:18 +01:00
|
|
|
private static Pattern PLACEHOLDER_REGEX = Pattern.compile("<(cost|result)>", Pattern.CASE_INSENSITIVE);
|
2022-02-19 13:27:41 +01:00
|
|
|
}
|
|
|
|
|
2022-07-25 17:14:00 +02:00
|
|
|
@Menu(title = "NPC Shop Item Editor", type = InventoryType.CHEST, dimensions = { 6, 9 })
|
2022-07-16 20:10:38 +02:00
|
|
|
public static class NPCShopItemEditor extends InventoryMenuPage {
|
2022-07-26 17:50:28 +02:00
|
|
|
@MenuPattern(
|
2022-07-27 11:05:52 +02:00
|
|
|
offset = { 0, 6 },
|
2022-07-26 17:50:28 +02:00
|
|
|
slots = { @MenuSlot(pat = 'x', material = Material.AIR) },
|
2023-04-05 20:35:08 +02:00
|
|
|
value = "xxx\nxxx\nxxx")
|
2022-07-26 17:50:28 +02:00
|
|
|
private InventoryMenuPattern actionItems;
|
2022-12-11 14:28:39 +01:00
|
|
|
private NPCShopItem base;
|
2022-07-24 18:23:44 +02:00
|
|
|
private final Consumer<NPCShopItem> callback;
|
2022-07-26 17:50:28 +02:00
|
|
|
@MenuPattern(
|
2022-07-27 11:05:52 +02:00
|
|
|
offset = { 0, 0 },
|
2022-07-26 17:50:28 +02:00
|
|
|
slots = { @MenuSlot(pat = 'x', material = Material.AIR) },
|
2023-04-05 20:35:08 +02:00
|
|
|
value = "xxx\nxxx\nxxx")
|
2022-07-26 17:50:28 +02:00
|
|
|
private InventoryMenuPattern costItems;
|
2022-07-16 20:10:38 +02:00
|
|
|
private MenuContext ctx;
|
2022-07-17 16:29:11 +02:00
|
|
|
private final NPCShopItem modified;
|
2022-07-16 20:10:38 +02:00
|
|
|
|
2022-07-17 17:01:00 +02:00
|
|
|
public NPCShopItemEditor(NPCShopItem item, Consumer<NPCShopItem> consumer) {
|
2023-11-05 10:41:18 +01:00
|
|
|
base = item;
|
|
|
|
modified = base.clone();
|
|
|
|
callback = consumer;
|
2022-07-16 20:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void initialise(MenuContext ctx) {
|
|
|
|
this.ctx = ctx;
|
2022-07-17 16:29:11 +02:00
|
|
|
if (modified.display != null) {
|
2023-01-02 05:54:19 +01:00
|
|
|
ctx.getSlot(9 * 4 + 4).setItemStack(modified.getDisplayItem(null));
|
2022-07-17 16:29:11 +02:00
|
|
|
}
|
2023-10-08 18:54:31 +02:00
|
|
|
ctx.getSlot(9 * 3 + 2).setItemStack(new ItemStack(Material.EGG), "Only purchasable once per player",
|
|
|
|
"Times purchasable: " + modified.timesPurchasable
|
|
|
|
+ (modified.timesPurchasable == 0 ? " (no limit)" : ""));
|
|
|
|
ctx.getSlot(9 * 3 + 2).setClickHandler(e -> ctx.getMenu()
|
|
|
|
.transition(InputMenus.stringSetter(() -> String.valueOf(modified.timesPurchasable), s -> {
|
2023-08-13 19:20:41 +02:00
|
|
|
modified.timesPurchasable = Integer.parseInt(s);
|
2023-10-08 18:54:31 +02:00
|
|
|
ctx.getSlot(9 * 4 + 2).setDescription("Times purchasable: " + modified.timesPurchasable
|
|
|
|
+ (modified.timesPurchasable == 0 ? " (no limit)" : ""));
|
2023-08-13 19:20:41 +02:00
|
|
|
})));
|
|
|
|
|
|
|
|
ctx.getSlot(9 * 4 + 2).setItemStack(new ItemStack(Util.getFallbackMaterial("OAK_SIGN", "SIGN")),
|
2023-10-08 18:54:31 +02:00
|
|
|
"Set already purchased message, currently:\n",
|
2023-08-13 19:20:41 +02:00
|
|
|
modified.alreadyPurchasedMessage == null ? "Unset" : modified.alreadyPurchasedMessage);
|
|
|
|
ctx.getSlot(9 * 4 + 2).setClickHandler(
|
|
|
|
e -> ctx.getMenu().transition(InputMenus.stringSetter(() -> modified.alreadyPurchasedMessage, s -> {
|
|
|
|
modified.alreadyPurchasedMessage = s;
|
|
|
|
ctx.getSlot(9 * 4 + 2).setDescription(modified.alreadyPurchasedMessage);
|
|
|
|
})));
|
|
|
|
|
2023-11-05 10:41:18 +01:00
|
|
|
ctx.getSlot(9 * 3 + 3).setItemStack(
|
|
|
|
new ItemStack(Util.getFallbackMaterial("GREEN_WOOL", "EMERALD", "OAK_SIGN", "SIGN")),
|
2023-10-08 18:54:31 +02:00
|
|
|
"Set successful click message, currently:\n",
|
2023-05-07 16:03:07 +02:00
|
|
|
modified.resultMessage == null ? "Unset" : modified.resultMessage);
|
2023-01-01 16:28:21 +01:00
|
|
|
ctx.getSlot(9 * 3 + 3).setClickHandler(
|
2023-05-07 16:03:07 +02:00
|
|
|
e -> ctx.getMenu().transition(InputMenus.stringSetter(() -> modified.resultMessage, s -> {
|
|
|
|
modified.resultMessage = s;
|
|
|
|
ctx.getSlot(9 * 3 + 3).setDescription(modified.resultMessage);
|
|
|
|
})));
|
|
|
|
|
2023-11-05 10:41:18 +01:00
|
|
|
ctx.getSlot(9 * 3 + 6).setItemStack(new ItemStack(Util.getFallbackMaterial("RED_WOOL", "OAK_SIGN", "SIGN")),
|
2023-10-08 18:54:31 +02:00
|
|
|
"Set unsuccessful click message, currently:\n",
|
2023-05-07 16:03:07 +02:00
|
|
|
modified.costMessage == null ? "Unset" : modified.costMessage);
|
|
|
|
ctx.getSlot(9 * 3 + 6).setClickHandler(
|
|
|
|
e -> ctx.getMenu().transition(InputMenus.stringSetter(() -> modified.costMessage, s -> {
|
|
|
|
modified.costMessage = s;
|
|
|
|
ctx.getSlot(9 * 3 + 6).setDescription(modified.costMessage);
|
2023-01-01 16:28:21 +01:00
|
|
|
})));
|
|
|
|
|
2023-11-05 10:41:18 +01:00
|
|
|
ctx.getSlot(9 * 3 + 5).setItemStack(new ItemStack(Util.getFallbackMaterial("FEATHER", "OAK_SIGN", "SIGN")),
|
2023-05-07 16:03:07 +02:00
|
|
|
"Set click to confirm message.",
|
|
|
|
"For example, 'click again to buy this item'\nYou can use <cost> or <result> placeholders.\nCurrently:\n"
|
2023-01-01 16:28:21 +01:00
|
|
|
+ (modified.clickToConfirmMessage == null ? "Unset" : modified.clickToConfirmMessage));
|
|
|
|
ctx.getSlot(9 * 3 + 5).setClickHandler(
|
|
|
|
e -> ctx.getMenu().transition(InputMenus.stringSetter(() -> modified.clickToConfirmMessage, s -> {
|
|
|
|
modified.clickToConfirmMessage = s;
|
|
|
|
ctx.getSlot(9 * 3 + 5).setDescription(modified.clickToConfirmMessage);
|
2022-12-11 14:28:39 +01:00
|
|
|
})));
|
2023-04-05 20:35:08 +02:00
|
|
|
|
|
|
|
ctx.getSlot(9 * 3 + 4).setItemStack(new ItemStack(Material.REDSTONE),
|
|
|
|
"Sell as many times as possible on shift click\n", "Currently: " + modified.maxRepeatsOnShiftClick);
|
|
|
|
ctx.getSlot(9 * 3 + 4).setClickHandler(
|
|
|
|
InputMenus.toggler(res -> modified.maxRepeatsOnShiftClick = res, modified.maxRepeatsOnShiftClick));
|
2022-07-26 17:50:28 +02:00
|
|
|
int pos = 0;
|
2023-11-05 10:41:18 +01:00
|
|
|
|
2022-07-26 17:50:28 +02:00
|
|
|
for (GUI template : NPCShopAction.getGUIs()) {
|
2024-01-07 10:06:55 +01:00
|
|
|
if (template.createMenuItem(null) == null)
|
2022-07-26 17:50:28 +02:00
|
|
|
continue;
|
2024-01-07 10:06:55 +01:00
|
|
|
|
2022-07-27 11:05:52 +02:00
|
|
|
NPCShopAction oldCost = modified.cost.stream().filter(template::manages).findFirst().orElse(null);
|
|
|
|
costItems.getSlots().get(pos)
|
|
|
|
.setItemStack(Util.editTitle(template.createMenuItem(oldCost), title -> title + " Cost"));
|
2022-12-03 11:21:46 +01:00
|
|
|
costItems.getSlots().get(pos).setClickHandler(event -> ctx.getMenu().transition(
|
|
|
|
template.createEditor(oldCost, cost -> modified.changeCost(template::manages, cost))));
|
2022-07-26 17:50:28 +02:00
|
|
|
|
2022-07-27 11:05:52 +02:00
|
|
|
NPCShopAction oldResult = modified.result.stream().filter(template::manages).findFirst().orElse(null);
|
|
|
|
actionItems.getSlots().get(pos)
|
|
|
|
.setItemStack(Util.editTitle(template.createMenuItem(oldResult), title -> title + " Result"));
|
2022-12-03 14:32:58 +01:00
|
|
|
actionItems.getSlots().get(pos).setClickHandler(event -> ctx.getMenu().transition(
|
|
|
|
template.createEditor(oldResult, result -> modified.changeResult(template::manages, result))));
|
2022-07-26 17:50:28 +02:00
|
|
|
|
|
|
|
pos++;
|
|
|
|
}
|
2022-07-17 16:29:11 +02:00
|
|
|
}
|
|
|
|
|
2022-07-26 17:50:28 +02:00
|
|
|
@MenuSlot(slot = { 5, 3 }, material = Material.REDSTONE_BLOCK, amount = 1, title = "<7>Cancel")
|
2022-07-17 16:29:11 +02:00
|
|
|
public void onCancel(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
|
|
|
ctx.getMenu().transitionBack();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onClose(HumanEntity who) {
|
2022-12-11 14:28:39 +01:00
|
|
|
if (base != null && base.display == null) {
|
|
|
|
base = null;
|
2022-07-17 16:29:11 +02:00
|
|
|
}
|
2022-12-11 14:28:39 +01:00
|
|
|
callback.accept(base);
|
2022-07-17 16:29:11 +02:00
|
|
|
}
|
|
|
|
|
2022-07-26 17:50:28 +02:00
|
|
|
@MenuSlot(slot = { 4, 5 }, material = Material.BOOK, amount = 1, title = "<f>Set description")
|
2022-07-17 16:29:11 +02:00
|
|
|
public void onEditDescription(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
|
|
|
event.setCancelled(true);
|
2022-07-17 17:01:00 +02:00
|
|
|
if (modified.display == null)
|
|
|
|
return;
|
2023-11-05 10:41:18 +01:00
|
|
|
|
2022-07-26 17:50:28 +02:00
|
|
|
ctx.getMenu()
|
|
|
|
.transition(InputMenus.stringSetter(() -> modified.display.getItemMeta().hasLore()
|
|
|
|
? Joiner.on("<br>").skipNulls().join(modified.display.getItemMeta().getLore())
|
|
|
|
: "", description -> {
|
|
|
|
ItemMeta meta = modified.display.getItemMeta();
|
2023-10-08 18:54:31 +02:00
|
|
|
meta.setLore(Lists
|
|
|
|
.newArrayList(Splitter.on('\n').split(Messaging.parseComponents(description))));
|
2022-07-26 17:50:28 +02:00
|
|
|
modified.display.setItemMeta(meta);
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2023-01-01 16:28:21 +01:00
|
|
|
@MenuSlot(slot = { 4, 3 }, material = Material.NAME_TAG, amount = 1, title = "<f>Set name")
|
2022-07-17 16:29:11 +02:00
|
|
|
public void onEditName(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
|
|
|
event.setCancelled(true);
|
2022-07-17 17:01:00 +02:00
|
|
|
if (modified.display == null)
|
|
|
|
return;
|
2023-11-05 10:41:18 +01:00
|
|
|
|
2022-07-17 17:01:00 +02:00
|
|
|
ctx.getMenu().transition(InputMenus.stringSetter(modified.display.getItemMeta()::getDisplayName, name -> {
|
|
|
|
ItemMeta meta = modified.display.getItemMeta();
|
2022-12-18 06:50:19 +01:00
|
|
|
meta.setDisplayName(ChatColor.RESET + Messaging.parseComponents(name));
|
2022-07-17 17:01:00 +02:00
|
|
|
modified.display.setItemMeta(meta);
|
|
|
|
}));
|
2022-07-17 16:29:11 +02:00
|
|
|
}
|
|
|
|
|
2022-07-26 17:50:28 +02:00
|
|
|
@ClickHandler(slot = { 4, 4 })
|
2022-07-17 16:29:11 +02:00
|
|
|
public void onModifyDisplayItem(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
|
|
|
event.setCancelled(true);
|
|
|
|
if (event.getCursor() != null) {
|
|
|
|
event.setCurrentItem(event.getCursor());
|
|
|
|
modified.display = event.getCursor().clone();
|
|
|
|
} else {
|
|
|
|
event.setCurrentItem(null);
|
|
|
|
modified.display = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-26 17:50:28 +02:00
|
|
|
@MenuSlot(slot = { 5, 4 }, material = Material.TNT, amount = 1, title = "<c>Remove")
|
2022-07-17 16:29:11 +02:00
|
|
|
public void onRemove(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
2022-12-11 14:28:39 +01:00
|
|
|
base = null;
|
2022-07-17 16:29:11 +02:00
|
|
|
ctx.getMenu().transitionBack();
|
|
|
|
}
|
|
|
|
|
2022-07-26 17:50:28 +02:00
|
|
|
@MenuSlot(slot = { 5, 5 }, material = Material.EMERALD_BLOCK, amount = 1, title = "<a>Save")
|
2022-07-17 16:29:11 +02:00
|
|
|
public void onSave(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
2022-12-11 14:28:39 +01:00
|
|
|
base = modified;
|
2022-07-17 16:29:11 +02:00
|
|
|
ctx.getMenu().transitionBack();
|
2022-07-16 20:10:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-19 13:27:41 +01:00
|
|
|
public static class NPCShopPage {
|
2022-07-27 11:05:52 +02:00
|
|
|
@Persist("$key")
|
2022-02-19 13:27:41 +01:00
|
|
|
private int index;
|
2022-07-21 17:13:51 +02:00
|
|
|
@Persist(keyType = Integer.class, reify = true)
|
2022-02-19 13:27:41 +01:00
|
|
|
private final Map<Integer, NPCShopItem> items = Maps.newHashMap();
|
|
|
|
@Persist
|
|
|
|
private String title;
|
2022-07-17 16:29:11 +02:00
|
|
|
|
2022-07-27 11:05:52 +02:00
|
|
|
private NPCShopPage() {
|
|
|
|
}
|
|
|
|
|
2022-07-17 16:29:11 +02:00
|
|
|
public NPCShopPage(int page) {
|
2023-11-05 10:41:18 +01:00
|
|
|
index = page;
|
2022-07-17 16:29:11 +02:00
|
|
|
}
|
2022-07-21 17:13:51 +02:00
|
|
|
|
|
|
|
public NPCShopItem getItem(int idx) {
|
|
|
|
return items.get(idx);
|
|
|
|
}
|
|
|
|
|
2023-11-05 10:41:18 +01:00
|
|
|
public ItemStack getNextPageItem(Player player, int idx) {
|
|
|
|
return items.containsKey(idx) ? items.get(idx).getDisplayItem(player) : new ItemStack(Material.FEATHER, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public ItemStack getPreviousPageItem(Player player, int idx) {
|
|
|
|
return items.containsKey(idx) ? items.get(idx).getDisplayItem(player) : new ItemStack(Material.FEATHER, 1);
|
|
|
|
}
|
|
|
|
|
2022-07-21 17:13:51 +02:00
|
|
|
public void removeItem(int idx) {
|
|
|
|
items.remove(idx);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setItem(int idx, NPCShopItem modified) {
|
|
|
|
items.put(idx, modified);
|
|
|
|
}
|
2022-07-17 16:29:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Menu(title = "NPC Shop Page Editor", type = InventoryType.CHEST, dimensions = { 5, 9 })
|
2022-07-21 18:43:40 +02:00
|
|
|
public static class NPCShopPageSettings extends InventoryMenuPage {
|
2022-07-17 16:29:11 +02:00
|
|
|
private MenuContext ctx;
|
|
|
|
private final NPCShopPage page;
|
|
|
|
|
2022-07-21 18:43:40 +02:00
|
|
|
public NPCShopPageSettings(NPCShopPage page) {
|
2022-07-17 16:29:11 +02:00
|
|
|
this.page = page;
|
|
|
|
}
|
|
|
|
|
|
|
|
@MenuSlot(slot = { 0, 4 }, material = Material.FEATHER, amount = 1)
|
|
|
|
public void editPageTitle(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
2023-04-05 20:35:08 +02:00
|
|
|
ctx.getMenu().transition(InputMenus.stringSetter(() -> page.title,
|
|
|
|
newTitle -> page.title = newTitle.isEmpty() ? null : newTitle));
|
2022-07-17 16:29:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void initialise(MenuContext ctx) {
|
|
|
|
this.ctx = ctx;
|
|
|
|
ctx.getSlot(4).setDescription("Set page title<br>Currently: " + page.title);
|
|
|
|
}
|
|
|
|
|
|
|
|
@MenuSlot(slot = { 4, 4 }, material = Material.TNT, amount = 1, title = "<c>Remove page")
|
|
|
|
public void removePage(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
|
|
|
ctx.data().put("removePage", page.index);
|
|
|
|
ctx.getMenu().transitionBack();
|
|
|
|
}
|
2021-07-14 15:23:55 +02:00
|
|
|
}
|
|
|
|
|
2022-07-21 18:43:40 +02:00
|
|
|
@Menu(title = "NPC Shop Editor", type = InventoryType.CHEST, dimensions = { 1, 9 })
|
|
|
|
public static class NPCShopSettings extends InventoryMenuPage {
|
|
|
|
private MenuContext ctx;
|
|
|
|
private final NPCShop shop;
|
2022-11-29 22:35:31 +01:00
|
|
|
private final ShopTrait trait;
|
2022-07-21 18:43:40 +02:00
|
|
|
|
2022-11-29 22:35:31 +01:00
|
|
|
public NPCShopSettings(ShopTrait trait, NPCShop shop) {
|
|
|
|
this.trait = trait;
|
2022-07-21 18:43:40 +02:00
|
|
|
this.shop = shop;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void initialise(MenuContext ctx) {
|
|
|
|
this.ctx = ctx;
|
2024-02-04 17:05:51 +01:00
|
|
|
ctx.getSlot(0)
|
2023-11-25 19:28:45 +01:00
|
|
|
.setDescription("<f>Edit permission required to view shop<br>" + shop.getRequiredPermission());
|
2024-02-04 17:05:51 +01:00
|
|
|
ctx.getSlot(4).setDescription("<f>Edit shop title<br>" + shop.title);
|
2022-12-18 18:44:16 +01:00
|
|
|
if (trait != null) {
|
2024-02-04 17:05:51 +01:00
|
|
|
ctx.getSlot(6).setDescription(
|
2022-12-18 18:44:16 +01:00
|
|
|
"<f>Show shop on right click<br>" + shop.getName().equals(trait.rightClickShop));
|
|
|
|
}
|
2022-07-21 18:43:40 +02:00
|
|
|
}
|
|
|
|
|
2024-02-04 17:05:51 +01:00
|
|
|
@MenuSlot(slot = { 0, 2 }, material = Material.FEATHER, amount = 1, title = "<f>Edit shop items")
|
2022-07-21 18:43:40 +02:00
|
|
|
public void onEditItems(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
|
|
|
ctx.getMenu().transition(new NPCShopContentsEditor(shop));
|
|
|
|
}
|
|
|
|
|
2024-02-04 17:05:51 +01:00
|
|
|
@MenuSlot(slot = { 0, 0 }, compatMaterial = { "OAK_SIGN", "SIGN" }, amount = 1)
|
2022-07-21 18:43:40 +02:00
|
|
|
public void onPermissionChange(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
|
|
|
ctx.getMenu().transition(InputMenus.stringSetter(shop::getRequiredPermission, shop::setPermission));
|
|
|
|
}
|
|
|
|
|
2024-02-04 17:05:51 +01:00
|
|
|
@MenuSlot(slot = { 0, 4 }, material = Material.NAME_TAG, amount = 1)
|
2022-07-21 18:43:40 +02:00
|
|
|
public void onSetTitle(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
2023-04-05 20:35:08 +02:00
|
|
|
ctx.getMenu().transition(InputMenus.stringSetter(() -> shop.title, newTitle -> shop.title = newTitle));
|
2022-07-21 18:43:40 +02:00
|
|
|
}
|
|
|
|
|
2024-02-04 17:05:51 +01:00
|
|
|
@MenuSlot(slot = { 0, 6 }, compatMaterial = { "COMMAND_BLOCK", "COMMAND" }, amount = 1)
|
2022-10-22 04:42:44 +02:00
|
|
|
public void onToggleRightClick(InventoryMenuSlot slot, CitizensInventoryClickEvent event) {
|
|
|
|
event.setCancelled(true);
|
2022-12-18 18:44:16 +01:00
|
|
|
if (trait == null)
|
|
|
|
return;
|
2023-11-05 10:41:18 +01:00
|
|
|
|
2022-12-03 11:21:46 +01:00
|
|
|
if (shop.getName().equals(trait.rightClickShop)) {
|
2022-11-29 22:35:31 +01:00
|
|
|
trait.rightClickShop = null;
|
|
|
|
} else {
|
|
|
|
trait.rightClickShop = shop.name;
|
|
|
|
}
|
2024-02-04 17:05:51 +01:00
|
|
|
ctx.getSlot(6)
|
2023-11-05 10:41:18 +01:00
|
|
|
.setDescription("<f>Show shop on right click<br>" + shop.getName().equals(trait.rightClickShop));
|
2022-10-22 04:42:44 +02:00
|
|
|
}
|
2022-07-21 18:43:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Menu(title = "Shop", type = InventoryType.CHEST, dimensions = { 5, 9 })
|
|
|
|
public static class NPCShopViewer extends InventoryMenuPage {
|
|
|
|
private MenuContext ctx;
|
|
|
|
private int currentPage = 0;
|
2023-01-01 16:28:21 +01:00
|
|
|
private NPCShopItem lastClickedItem;
|
2022-07-29 03:15:15 +02:00
|
|
|
private final Player player;
|
2022-07-21 18:43:40 +02:00
|
|
|
private final NPCShop shop;
|
|
|
|
|
2022-07-29 03:15:15 +02:00
|
|
|
public NPCShopViewer(NPCShop shop, Player player) {
|
2022-07-21 18:43:40 +02:00
|
|
|
this.shop = shop;
|
2022-07-29 03:15:15 +02:00
|
|
|
this.player = player;
|
2022-07-21 18:43:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void changePage(int newPage) {
|
2023-11-05 10:41:18 +01:00
|
|
|
currentPage = newPage;
|
2022-07-21 18:43:40 +02:00
|
|
|
NPCShopPage page = shop.pages.get(currentPage);
|
|
|
|
if (page.title != null && !page.title.isEmpty()) {
|
|
|
|
Bukkit.getScheduler().runTaskLater(CitizensAPI.getPlugin(), () -> {
|
2022-07-29 03:15:15 +02:00
|
|
|
ctx.setTitle(Placeholders.replace(page.title, player));
|
2022-07-21 18:43:40 +02:00
|
|
|
}, 1);
|
|
|
|
}
|
|
|
|
for (int i = 0; i < ctx.getInventory().getSize(); i++) {
|
|
|
|
ctx.getSlot(i).clear();
|
|
|
|
NPCShopItem item = page.getItem(i);
|
|
|
|
if (item == null)
|
|
|
|
continue;
|
|
|
|
|
2022-07-29 03:15:15 +02:00
|
|
|
ctx.getSlot(i).setItemStack(item.getDisplayItem(player));
|
2022-07-26 21:40:46 +02:00
|
|
|
ctx.getSlot(i).setClickHandler(evt -> {
|
2022-07-21 18:43:40 +02:00
|
|
|
evt.setCancelled(true);
|
2023-01-01 16:28:21 +01:00
|
|
|
item.onClick(shop, evt, lastClickedItem == item);
|
|
|
|
lastClickedItem = item;
|
2022-07-21 18:43:40 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
InventoryMenuSlot prev = ctx.getSlot(4 * 9 + 3);
|
|
|
|
InventoryMenuSlot next = ctx.getSlot(4 * 9 + 5);
|
|
|
|
if (currentPage > 0) {
|
2023-07-08 18:02:47 +02:00
|
|
|
prev.clear();
|
2023-11-05 10:41:18 +01:00
|
|
|
prev.setItemStack(page.getPreviousPageItem(player, 4 * 9 + 3), "Previous page (" + newPage + ")");
|
2022-07-26 21:40:46 +02:00
|
|
|
prev.setClickHandler(evt -> {
|
2022-07-21 18:43:40 +02:00
|
|
|
evt.setCancelled(true);
|
|
|
|
changePage(currentPage - 1);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if (currentPage + 1 < shop.pages.size()) {
|
2023-07-08 18:02:47 +02:00
|
|
|
next.clear();
|
2023-11-05 10:41:18 +01:00
|
|
|
next.setItemStack(page.getNextPageItem(player, 4 * 9 + 5), "Next page (" + (newPage + 1) + ")");
|
2022-07-26 21:40:46 +02:00
|
|
|
next.setClickHandler(evt -> {
|
2022-07-21 18:43:40 +02:00
|
|
|
evt.setCancelled(true);
|
|
|
|
changePage(currentPage + 1);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Inventory createInventory(String title) {
|
2023-11-25 19:28:45 +01:00
|
|
|
return Bukkit.createInventory(null, 45, shop.title == null || shop.title.isEmpty() ? "Shop"
|
|
|
|
: Messaging.parseComponents(Placeholders.replace(shop.title, player)));
|
2022-07-21 18:43:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void initialise(MenuContext ctx) {
|
|
|
|
this.ctx = ctx;
|
|
|
|
changePage(currentPage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-27 11:05:52 +02:00
|
|
|
static {
|
|
|
|
NPCShopAction.register(ItemAction.class, "items", new ItemActionGUI());
|
|
|
|
NPCShopAction.register(PermissionAction.class, "permissions", new PermissionActionGUI());
|
|
|
|
NPCShopAction.register(MoneyAction.class, "money", new MoneyActionGUI());
|
2022-12-12 00:28:12 +01:00
|
|
|
NPCShopAction.register(CommandAction.class, "command", new CommandActionGUI());
|
2023-03-16 15:01:04 +01:00
|
|
|
NPCShopAction.register(ExperienceAction.class, "experience", new ExperienceActionGUI());
|
2022-07-27 11:05:52 +02:00
|
|
|
}
|
2023-08-13 19:20:41 +02:00
|
|
|
}
|