@@ -5,6 +5,7 @@ import li.angu.challengeplugin.models.Challenge
55import li.angu.challengeplugin.models.ChallengeStatus
66import org.bukkit.Bukkit
77import org.bukkit.Material
8+ import org.bukkit.NamespacedKey
89import org.bukkit.entity.Player
910import org.bukkit.event.EventHandler
1011import org.bukkit.event.HandlerList
@@ -14,13 +15,15 @@ import org.bukkit.event.inventory.InventoryCloseEvent
1415import org.bukkit.inventory.Inventory
1516import org.bukkit.inventory.ItemStack
1617import org.bukkit.inventory.meta.ItemMeta
18+ import org.bukkit.persistence.PersistentDataType
1719import java.util.UUID
1820import java.util.concurrent.ConcurrentHashMap
1921
2022class ChallengeMenuManager (private val plugin : ChallengePluginPlugin ) : Listener {
2123
2224 private val playerMenus = ConcurrentHashMap <UUID , Inventory >()
23- private val challengesPerInventory = ConcurrentHashMap <Inventory , List <Challenge >>()
25+ private val challengesPerInventory = ConcurrentHashMap <Inventory , List <ChallengeMenuData >>()
26+ private val filterToggleKey = NamespacedKey (plugin, " filter_show_all" )
2427
2528 init {
2629 plugin.server.pluginManager.registerEvents(this , plugin)
@@ -32,7 +35,7 @@ class ChallengeMenuManager(private val plugin: ChallengePluginPlugin) : Listener
3235 challengesPerInventory.clear()
3336 }
3437
35- fun openMainMenu (player : Player ) {
38+ fun openMainMenu (player : Player , showAll : Boolean = false ) {
3639 val inventory = Bukkit .createInventory(
3740 player,
3841 54 , // 6 rows
@@ -56,7 +59,11 @@ class ChallengeMenuManager(private val plugin: ChallengePluginPlugin) : Listener
5659 createMeta.setDisplayName(plugin.languageManager.getMessage(" challenge.menu.create" , player))
5760 createMeta.lore = listOf (plugin.languageManager.getMessage(" challenge.menu.create_lore" , player))
5861 createItem.itemMeta = createMeta
59- inventory.setItem(4 , createItem)
62+ inventory.setItem(0 , createItem)
63+
64+ // Filter toggle button (slot 4)
65+ val filterItem = createFilterToggleItem(player, showAll)
66+ inventory.setItem(4 , filterItem)
6067
6168 // Create Leave Challenge button
6269 val leaveItem = ItemStack (Material .BARRIER )
@@ -66,24 +73,24 @@ class ChallengeMenuManager(private val plugin: ChallengePluginPlugin) : Listener
6673 leaveItem.itemMeta = leaveMeta
6774 inventory.setItem(8 , leaveItem)
6875
69- // Get all active challenges
70- val challenges = plugin.challengeManager.getActiveChallenges( )
76+ // Get challenges using efficient database query
77+ val challenges = plugin.challengeManager.getChallengesForMenu(player.uniqueId, showAll )
7178 val playerCurrentChallenge = plugin.challengeManager.getPlayerChallenge(player)
7279
7380 // Store the challenges for this inventory so we can retrieve them in the click handler
7481 challengesPerInventory[inventory] = challenges
7582
7683 // Add challenges to inventory
7784 if (challenges.isNotEmpty()) {
78- challenges.forEachIndexed { index, challenge ->
85+ challenges.forEachIndexed { index, challengeData ->
7986 // Start at slot 18 (third row) and fill rows from there
8087 val slot = 18 + index
8188
8289 // Skip if we've reached the end of the inventory
8390 if (slot >= inventory.size) return @forEachIndexed
8491
85- val isCurrentChallenge = playerCurrentChallenge?.id == challenge .id
86- val challengeItem = createChallengeItem(challenge , player, isCurrentChallenge)
92+ val isCurrentChallenge = playerCurrentChallenge?.id == challengeData .id
93+ val challengeItem = createChallengeItemFromData(challengeData , player, isCurrentChallenge)
8794 inventory.setItem(slot, challengeItem)
8895 }
8996 } else {
@@ -102,43 +109,63 @@ class ChallengeMenuManager(private val plugin: ChallengePluginPlugin) : Listener
102109 player.openInventory(inventory)
103110 }
104111
105- private fun createChallengeItem (challenge : Challenge , player : Player , isCurrentChallenge : Boolean ): ItemStack {
106- // Choose material based on whether this is the player's current challenge
107- val material = when {
108- isCurrentChallenge -> Material .ENCHANTED_BOOK
109- else -> Material .BOOK
112+ private fun createFilterToggleItem (player : Player , showAll : Boolean ): ItemStack {
113+ val item = ItemStack (Material .PLAYER_HEAD )
114+ val meta = item.itemMeta ? : Bukkit .getItemFactory().getItemMeta(Material .PLAYER_HEAD )
115+
116+ // Store filter state in persistent data
117+ meta.persistentDataContainer.set(filterToggleKey, PersistentDataType .BYTE , if (showAll) 1 else 0 )
118+
119+ if (showAll) {
120+ meta.setDisplayName(plugin.languageManager.getMessage(" challenge.menu.filter_all" , player))
121+ meta.lore = listOf (plugin.languageManager.getMessage(" challenge.menu.filter_all_lore" , player))
122+ } else {
123+ meta.setDisplayName(plugin.languageManager.getMessage(" challenge.menu.filter_your" , player))
124+ meta.lore = listOf (plugin.languageManager.getMessage(" challenge.menu.filter_your_lore" , player))
125+
126+ // Set to player's head
127+ if (meta is org.bukkit.inventory.meta.SkullMeta ) {
128+ meta.owningPlayer = player
129+ }
110130 }
111131
132+ item.itemMeta = meta
133+ return item
134+ }
135+
136+ private fun createChallengeItemFromData (challengeData : ChallengeMenuData , player : Player , isCurrentChallenge : Boolean ): ItemStack {
137+ val material = if (isCurrentChallenge) Material .ENCHANTED_BOOK else Material .BOOK
138+
112139 val item = ItemStack (material)
113140 val meta = item.itemMeta ? : Bukkit .getItemFactory().getItemMeta(material)
114141
115142 meta.setDisplayName(
116143 if (isCurrentChallenge)
117- plugin.languageManager.getMessage(" challenge.menu.current_challenge" , player, " name" to challenge .name)
144+ plugin.languageManager.getMessage(" challenge.menu.current_challenge" , player, " name" to challengeData .name)
118145 else
119- plugin.languageManager.getMessage(" challenge.menu.challenge" , player, " name" to challenge .name)
146+ plugin.languageManager.getMessage(" challenge.menu.challenge" , player, " name" to challengeData .name)
120147 )
121148
122149 val loreList = mutableListOf<String >()
123150
124151 // Challenge ID
125- loreList.add(plugin.languageManager.getMessage(" challenge.menu.id" , player, " id" to challenge.id.toString()))
126-
127- // Challenge status
128- val statusKey = when (challenge.status) {
129- ChallengeStatus .ACTIVE -> " status.active"
130- ChallengeStatus .COMPLETED -> " status.completed"
131- ChallengeStatus .FAILED -> " status.failed"
132- }
133- loreList.add(plugin.languageManager.getMessage(" challenge.menu.status" , player,
134- " status" to plugin.languageManager.getMessage(statusKey, player)))
152+ loreList.add(plugin.languageManager.getMessage(" challenge.menu.id" , player, " id" to challengeData.id.toString()))
135153
136154 // Players
137- loreList.add(plugin.languageManager.getMessage(" challenge.menu.players" , player, " count" to challenge.players.size.toString()))
155+ loreList.add(plugin.languageManager.getMessage(" challenge.menu.players" , player, " count" to challengeData.playerCount.toString()))
156+
157+ // Player status for this challenge
158+ val playerStatusMessage = when (challengeData.playerStatus) {
159+ " active" -> plugin.languageManager.getMessage(" player_status.active" , player)
160+ " failed" -> plugin.languageManager.getMessage(" player_status.failed" , player)
161+ " completed" -> plugin.languageManager.getMessage(" player_status.completed" , player)
162+ else -> plugin.languageManager.getMessage(" player_status.not_joined" , player)
163+ }
164+ loreList.add(plugin.languageManager.getMessage(" challenge.menu.your_status" , player, " status" to playerStatusMessage))
138165
139166 // Duration (if started)
140- if (challenge .startedAt != null ) {
141- loreList.add(plugin.languageManager.getMessage(" challenge.menu.duration" , player, " time" to challenge .getFormattedDuration()))
167+ if (challengeData .startedAt != null ) {
168+ loreList.add(plugin.languageManager.getMessage(" challenge.menu.duration" , player, " time" to challengeData .getFormattedDuration()))
142169 }
143170
144171 // Action text
@@ -155,6 +182,7 @@ class ChallengeMenuManager(private val plugin: ChallengePluginPlugin) : Listener
155182 return item
156183 }
157184
185+
158186 @EventHandler
159187 fun onInventoryClick (event : InventoryClickEvent ) {
160188 val player = event.whoClicked as ? Player ? : return
@@ -171,11 +199,21 @@ class ChallengeMenuManager(private val plugin: ChallengePluginPlugin) : Listener
171199
172200 when (event.slot) {
173201 // Create Challenge button
174- 4 -> {
202+ 0 -> {
175203 player.closeInventory()
176204 player.performCommand(" create " ) // Open create name prompt
177205 }
178206
207+ // Filter Toggle button
208+ 4 -> {
209+ val filterItem = event.currentItem ? : return
210+ val currentShowAll = filterItem.itemMeta?.persistentDataContainer?.get(filterToggleKey, PersistentDataType .BYTE ) == 1 .toByte()
211+
212+ // Toggle filter and refresh menu
213+ player.closeInventory()
214+ openMainMenu(player, ! currentShowAll)
215+ }
216+
179217 // Leave Challenge button
180218 8 -> {
181219 val currentChallenge = plugin.challengeManager.getPlayerChallenge(player)
@@ -202,15 +240,15 @@ class ChallengeMenuManager(private val plugin: ChallengePluginPlugin) : Listener
202240 // Calculate index in the challenges list
203241 val index = event.slot - 18
204242 if (index >= 0 && index < challenges.size) {
205- val challenge = challenges[index]
243+ val challengeData = challenges[index]
206244 val currentChallenge = plugin.challengeManager.getPlayerChallenge(player)
207245
208- if (currentChallenge?.id == challenge .id) {
246+ if (currentChallenge?.id == challengeData .id) {
209247 // Player clicked their current challenge, leave it
210- plugin.playerDataManager.savePlayerData(player, challenge .id)
248+ plugin.playerDataManager.savePlayerData(player, challengeData .id)
211249
212250 if (plugin.challengeManager.leaveChallenge(player)) {
213- player.sendMessage(plugin.languageManager.getMessage(" challenge.left" , player, " name" to challenge .name))
251+ player.sendMessage(plugin.languageManager.getMessage(" challenge.left" , player, " name" to challengeData .name))
214252
215253 // Refresh the menu
216254 player.closeInventory()
@@ -220,13 +258,15 @@ class ChallengeMenuManager(private val plugin: ChallengePluginPlugin) : Listener
220258 }
221259 } else {
222260 // Player clicked a different challenge, join it
223- if (challenge .status != ChallengeStatus .ACTIVE ) {
261+ if (challengeData .status != ChallengeStatus .ACTIVE ) {
224262 player.sendMessage(plugin.languageManager.getMessage(" challenge.already_completed" , player))
225263 return
226264 }
227265
228- if (plugin.challengeManager.joinChallenge(player, challenge)) {
229- player.sendMessage(plugin.languageManager.getMessage(" challenge.joined" , player, " name" to challenge.name))
266+ // Get full challenge object from memory to join
267+ val fullChallenge = plugin.challengeManager.getChallenge(challengeData.id)
268+ if (fullChallenge != null && plugin.challengeManager.joinChallenge(player, fullChallenge)) {
269+ player.sendMessage(plugin.languageManager.getMessage(" challenge.joined" , player, " name" to challengeData.name))
230270
231271 // Close the menu
232272 player.closeInventory()
0 commit comments