Skip to content
This repository was archived by the owner on Mar 8, 2022. It is now read-only.

Commit 91d7ad8

Browse files
committed
improve gui
1 parent 80b30b6 commit 91d7ad8

File tree

5 files changed

+193
-29
lines changed

5 files changed

+193
-29
lines changed

src/main/java/cat/nyaa/HamsterEcoHelper/quest/QuestCommands.java

+29-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import cat.nyaa.HamsterEcoHelper.HamsterEcoHelper;
44
import cat.nyaa.HamsterEcoHelper.I18n;
5+
import cat.nyaa.HamsterEcoHelper.quest.gui.PaginatedListGui;
6+
import cat.nyaa.HamsterEcoHelper.quest.gui.PairList;
57
import cat.nyaa.HamsterEcoHelper.utils.database.tables.quest.QuestStation;
68
import cat.nyaa.nyaacore.CommandReceiver;
79
import cat.nyaa.nyaacore.LanguageRepository;
@@ -10,6 +12,8 @@
1012
import org.bukkit.block.Sign;
1113
import org.bukkit.command.CommandSender;
1214
import org.bukkit.entity.Player;
15+
import org.bukkit.inventory.ItemStack;
16+
import org.bukkit.inventory.meta.ItemMeta;
1317

1418
import java.util.Set;
1519

@@ -37,6 +41,31 @@ public void postQuest(CommandSender sender, Arguments args) {
3741
new QuestWizard(station.id, asPlayer(sender), 30);
3842
}
3943

44+
public PaginatedListGui gui;
45+
@SubCommand(value = "test", permission = "heh.quest.admin")
46+
public void testCmd(CommandSender sender, Arguments args) {
47+
gui = new PaginatedListGui("Dummy") {
48+
@Override
49+
protected PairList<String, ItemStack> getFullGuiContent() {
50+
PairList<String, ItemStack> ret = new PairList<>();
51+
for (int i = 0; i < 150;i++) {
52+
ItemStack n = new ItemStack(Material.BOOK);
53+
ItemMeta m = n.getItemMeta();
54+
m.setDisplayName("Book #" + Integer.toString(i));
55+
n.setItemMeta(m);
56+
ret.put(Integer.toString(i), n);
57+
}
58+
return ret;
59+
}
60+
61+
@Override
62+
protected void itemClicked(Player player, String itemKey) {
63+
player.sendMessage("Clicked item #" + itemKey);
64+
}
65+
};
66+
gui.openFor(asPlayer(sender));
67+
}
68+
4069
public Sign getSignLookat(CommandSender sender) {
4170
Player p = asPlayer(sender);
4271
Block b = p.getTargetBlock((Set<Material>) null, 5);// TODO use nms rayTrace
@@ -46,6 +75,4 @@ public Sign getSignLookat(CommandSender sender) {
4675
}
4776
return (Sign)b.getState();
4877
}
49-
50-
5178
}

src/main/java/cat/nyaa/HamsterEcoHelper/quest/QuestListener.java

+3
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,8 @@ public void onInventoryClicked(InventoryClickEvent ev) {
8989
if (ev.getClickedInventory().getHolder() instanceof QuestStationGui) {
9090
((QuestStationGui)ev.getClickedInventory().getHolder()).onInventoryClicked(ev);
9191
}
92+
if (ev.getClickedInventory().getHolder() == plugin.commandHandler.questCommands.gui) {
93+
plugin.commandHandler.questCommands.gui.onInventoryClicked(ev);
94+
}
9295
}
9396
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package cat.nyaa.HamsterEcoHelper.quest.gui;
22

3+
import cat.nyaa.HamsterEcoHelper.I18n;
34
import org.bukkit.Bukkit;
5+
import org.bukkit.Material;
46
import org.bukkit.entity.Player;
57
import org.bukkit.event.inventory.InventoryClickEvent;
68
import org.bukkit.inventory.Inventory;
79
import org.bukkit.inventory.InventoryHolder;
810
import org.bukkit.inventory.ItemStack;
11+
import org.bukkit.inventory.meta.ItemMeta;
912

10-
import java.util.HashMap;
1113
import java.util.Map;
12-
import java.util.SortedMap;
14+
import java.util.TreeMap;
1315
import java.util.UUID;
1416

1517
/**
@@ -19,29 +21,81 @@
1921
* otherwise synchronization issues will occur
2022
*/
2123
public abstract class PaginatedListGui implements InventoryHolder {
22-
23-
protected final Map<UUID, Inventory> openedUI = new HashMap<>();
24-
protected final Map<UUID, Integer> openedUiPages = new HashMap<>();
24+
protected final Map<UUID, Inventory> openedUI = new TreeMap<>();
25+
protected final Map<UUID, Integer> openedUiPages = new TreeMap<>(); // pages count from 0
2526
protected final String basicTitle;
2627

27-
private PairList<String, ItemStack> currentItems;
28+
/* the following three variables are updated (and should only be update)
29+
* in {@link #contentChanged()}
30+
*/
31+
private boolean guiFrozen = false;
32+
private PairList<String, ItemStack> fullContentCache = null;
33+
private int maxPage; // how may pages this gui has. pageIdx in range [0, maxPage)
2834

2935
protected PaginatedListGui(String title) {
3036
basicTitle = title;
37+
contentChanged();
3138
}
3239

3340
/**
3441
* Show the player this GUI
3542
*/
3643
public void openFor(Player player) {
37-
openedUiPages.put(player.getUniqueId(), 1);
38-
Inventory inv = Bukkit.createInventory(this, 54, basicTitle + " - Page 1");
39-
currentItems = getFullGuiContent();
40-
ItemStack[] t = currentItems.getValues(0,45).toArray(new ItemStack[0]);
41-
inv.setContents(t);
42-
openedUI.put(player.getUniqueId(), inv);
43-
player.openInventory(inv);
44-
// TODO
44+
showPlayerPage(player, 0);
45+
}
46+
47+
/**
48+
* Show page to player.
49+
* If page not in range, close gui & clear player record.
50+
* @param p
51+
* @param page
52+
*/
53+
private void showPlayerPage(Player p, int page) {
54+
UUID id = p.getUniqueId();
55+
if (page >=0 && page < maxPage) {
56+
Inventory inv = Bukkit.createInventory(this, 54, basicTitle + I18n.format("user.quest.title_page", page + 1));
57+
ItemStack[] tmp = fullContentCache.getValues(page*45, (page+1)*45).toArray(new ItemStack[0]);
58+
inv.setContents(tmp);
59+
60+
// set prev/next page buttons
61+
if (page == 0) {
62+
inv.setItem(45, getNamedItem(Material.BARRIER, I18n.format("user.quest.first_page")));
63+
} else {
64+
inv.setItem(45, getNamedItem(Material.ARROW, I18n.format("user.quest.prev_page")));
65+
}
66+
if (page == maxPage - 1) {
67+
inv.setItem(53, getNamedItem(Material.BARRIER, I18n.format("user.quest.last_page")));
68+
} else {
69+
inv.setItem(53, getNamedItem(Material.ARROW, I18n.format("user.quest.next_page")));
70+
}
71+
72+
openedUiPages.put(id, page);
73+
openedUI.put(id, inv);
74+
p.openInventory(inv);
75+
} else {
76+
openedUiPages.remove(id);
77+
openedUI.remove(id);
78+
if (isCurrentGui(p)) p.closeInventory();
79+
}
80+
}
81+
82+
public ItemStack getNamedItem(Material material, String title) {
83+
ItemStack stack = new ItemStack(material);
84+
ItemMeta m = stack.getItemMeta();
85+
m.setDisplayName(title);
86+
stack.setItemMeta(m);
87+
return stack;
88+
}
89+
90+
/**
91+
* Check if the inventory opened by the player
92+
* is managed by this GUI object
93+
* i.e. inventoryHolder == this
94+
*/
95+
public boolean isCurrentGui(Player p) {
96+
return p.getOpenInventory() != null &&
97+
p.getOpenInventory().getTopInventory() != null &&
98+
p.getOpenInventory().getTopInventory().getHolder() == this;
4599
}
46100

47101
/**
@@ -63,34 +117,105 @@ public final Inventory getInventory() {
63117
* @param ev
64118
*/
65119
public void onInventoryClicked(InventoryClickEvent ev) {
66-
// TODO
120+
// TODO change all System.err
67121
ev.setCancelled(true);
122+
if (guiFrozen) return;
68123
if (ev.getClickedInventory() == null) return;
69124
if (!(ev.getWhoClicked() instanceof Player)) {
70125
System.err.print("inventory not clicked by player?");
71126
return;
72127
}
73-
if (ev.getClickedInventory() != openedUI.get(ev.getWhoClicked().getUniqueId())) {
74-
System.err.print("user clicked an unknown inventory.");
75-
System.err.print("clicked:"+ev.getInventory());
76-
System.err.print("expected:"+openedUI.get(ev.getWhoClicked().getUniqueId()));
77-
//return;
78-
// TODO it seems bukkit does some magic when opening an inventory
79-
// so we need another way to track inventories (not by "==")
128+
Player p = (Player)ev.getWhoClicked();
129+
if (!openedUI.containsKey(p.getUniqueId()) || !openedUiPages.containsKey(p.getUniqueId())) {
130+
System.err.print("user not registered in gui");
131+
return;
80132
}
81-
Integer page = openedUiPages.get(ev.getWhoClicked().getUniqueId());
82-
if (page == null) {
83-
System.err.print("user clicked an unknown inventory page no.");
133+
Inventory inv = ev.getClickedInventory();
134+
if (!inv.equals(openedUI.get(p.getUniqueId()))) {
135+
System.err.print("user clicked on unknown inventory");
84136
return;
85137
}
86-
87-
itemClicked((Player)ev.getWhoClicked(), currentItems.getKey((page-1)*45+ev.getSlot()));
138+
Integer page = openedUiPages.get(ev.getWhoClicked().getUniqueId());
139+
Integer slot = ev.getSlot();
140+
if (slot >= 0 && slot < 45) { //clicked on item
141+
String key = fullContentCache.getKey(page * 45 + slot);
142+
if (key != null) {
143+
itemClicked(p, key);
144+
} else {
145+
// TODO user clicked on an empty slot. Need better handling
146+
//System.err.print("user clicked slot out of range");
147+
}
148+
} else if (slot == 45) { // previous page
149+
if (page - 1 >= 0) showPlayerPage(p, page - 1);
150+
} else if (slot == 53) { // next page
151+
if (page + 1 < maxPage) showPlayerPage(p, page + 1);
152+
} else if (slot > 45 && slot < 53) { // custom buttons
153+
// TODO not implemented
154+
} else { // unknown buttons
155+
System.err.print("user clicked on unknown slot");
156+
}
88157
}
89158

90159
/**
91160
* Should be implemented by subclasses.
161+
* PaginatedListGui will cache the returned List.
162+
* When the content is changed, subclass should call {@link #contentChanged}
163+
*
92164
* @return An ordered map of items to be shown in this GUI
93165
*/
94166
protected abstract PairList<String, ItemStack> getFullGuiContent();
167+
168+
/**
169+
* This method will be called when a player clicked on an item.
170+
* TODO: subclasses may need another cache to store the itemKeys, maybe we can avoid this?
171+
* @param itemKey the key returned by {@link #getFullGuiContent()}
172+
*/
95173
protected abstract void itemClicked(Player player, String itemKey);
174+
175+
/**
176+
* Invoked when all inventories of this gui are closed by players.
177+
* Subclasses may or may not implement this.
178+
* TODO not implemented, inventory close event
179+
*/
180+
protected void guiCompletelyClosed() {
181+
182+
}
183+
184+
/**
185+
* Subclasses should call this method when there's any change
186+
* on the contents.
187+
* When invoked, PaginatedListGui will invalid the cache immediately
188+
* call {@link #getFullGuiContent()} again, and refresh all opened inventories.
189+
* TODO: players may unintentionally click on the wrong item if there are many players competing.
190+
* TODO: adding a short cooldown time may be a good idea?
191+
*/
192+
protected void contentChanged() {
193+
guiFrozen = true; // TODO gui may not need to be frozen, further investigation needed.
194+
195+
// update cache
196+
fullContentCache = getFullGuiContent();
197+
maxPage = fullContentCache.size() <= 0? 1 :(fullContentCache.size()-1)/45+1;
198+
199+
// update opened inventories
200+
if (!openedUI.isEmpty() || !openedUiPages.isEmpty()) {
201+
Map<Player, Integer> newPages = new TreeMap<>();
202+
203+
for (UUID id : openedUI.keySet()) {
204+
Player p = Bukkit.getPlayer(id);
205+
if (p == null) continue;
206+
Integer page = openedUiPages.get(id);
207+
if (page == null) continue;
208+
if (page < 0) continue;
209+
if (page >= maxPage) page = maxPage - 1;
210+
newPages.put(p, page);
211+
}
212+
openedUI.clear();
213+
openedUiPages.clear();
214+
for (Map.Entry<Player, Integer> e : newPages.entrySet()) {
215+
showPlayerPage(e.getKey(), e.getValue());
216+
}
217+
}
218+
219+
guiFrozen = false;
220+
}
96221
}

src/main/java/cat/nyaa/HamsterEcoHelper/quest/gui/PairList.java

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ public K getKey(int index) {
3838
return index>=l.size()? null:l.get(index).key;
3939
}
4040

41+
public int size() {
42+
return l.size();
43+
}
44+
4145
@Override
4246
public Iterator<Pair> iterator() {
4347
return new Iterator<Pair>() {

src/main/resources/lang/en_US.yml

+5
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ user:
220220
cannot_break: "You cannot break this station"
221221
has_ongoing_quest: "There are quests in progress."
222222
not_station: "Please look at a quest station sign."
223+
title_page: " - Page %d"
224+
first_page: "This is the first page"
225+
last_page: "This is the last page"
226+
prev_page: "Previous page"
227+
next_page: "Next page"
223228

224229

225230
wizard:

0 commit comments

Comments
 (0)