Last active
December 25, 2024 07:03
-
-
Save rexlManu/fddc083c621480abf943c8a7dad10989 to your computer and use it in GitHub Desktop.
Example Trade GUI in InvUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.rexlmanu.pluginbase.trade; | |
import de.rexlmanu.pluginbase.utils.PlayerUtils; | |
import org.bukkit.Material; | |
import org.bukkit.entity.Player; | |
import xyz.xenondevs.invui.gui.Gui; | |
import xyz.xenondevs.invui.inventory.VirtualInventory; | |
import xyz.xenondevs.invui.inventory.event.PlayerUpdateReason; | |
import xyz.xenondevs.invui.item.Item; | |
import xyz.xenondevs.invui.item.builder.ItemBuilder; | |
import xyz.xenondevs.invui.item.impl.SimpleItem; | |
import xyz.xenondevs.invui.window.Window; | |
import java.util.*; | |
/** | |
* This class manages creating trades between multiple players using | |
* a GUI-based system. Each TradeSide is responsible for tracking | |
* a player's inventory interactions and confirmation status. | |
*/ | |
public class TradeGui { | |
/** | |
* This inner class represents one side of the trade. It holds a | |
* VirtualInventory and the associated Player, along with a flag | |
* to track whether that player has confirmed the trade. | |
*/ | |
public static class TradeSide { | |
private final VirtualInventory virtualInventory; | |
private final Player player; | |
private boolean confirmed; | |
public TradeSide(VirtualInventory virtualInventory, Player player) { | |
this.virtualInventory = virtualInventory; | |
this.player = player; | |
this.confirmed = false; | |
} | |
public VirtualInventory getVirtualInventory() { | |
return virtualInventory; | |
} | |
public Player getPlayer() { | |
return player; | |
} | |
public boolean isConfirmed() { | |
return confirmed; | |
} | |
public void setConfirmed(boolean confirmed) { | |
this.confirmed = confirmed; | |
} | |
} | |
/** | |
* The Trade class holds references to all sides of the trade, | |
* the confirm buttons in the GUI, and any open windows for the trade. | |
*/ | |
public static class Trade { | |
private final List<TradeSide> sides; | |
private final Set<Item> confirmButtons; | |
private final Set<Window> windows; | |
public Trade() { | |
this.sides = new ArrayList<>(); | |
this.confirmButtons = new HashSet<>(); | |
this.windows = new HashSet<>(); | |
} | |
public List<TradeSide> getSides() { | |
return sides; | |
} | |
public Set<Item> getConfirmButtons() { | |
return confirmButtons; | |
} | |
public Set<Window> getWindows() { | |
return windows; | |
} | |
public Optional<TradeSide> getSide(int index) { | |
if (index < 0 || index >= sides.size()) { | |
return Optional.empty(); | |
} | |
return Optional.of(sides.get(index)); | |
} | |
public boolean isConfirmed() { | |
// True only if all TradeSide objects are confirmed | |
return sides.stream().allMatch(TradeSide::isConfirmed); | |
} | |
public Optional<TradeSide> getOppositeSide(TradeSide side) { | |
return sides.stream().filter(s -> s != side).findFirst(); | |
} | |
public Optional<TradeSide> getSide(Player player) { | |
return sides.stream().filter(s -> s.getPlayer().equals(player)).findFirst(); | |
} | |
} | |
/** | |
* Creates a new Trade object for the supplied players. | |
* Each player gets a VirtualInventory and their updates | |
* are restricted to their own side of the inventory. | |
* | |
* @param players the players who will participate in the trade | |
* @return a Trade containing all participants | |
*/ | |
public static Trade createTrade(Player... players) { | |
Trade trade = new Trade(); | |
for (Player player : players) { | |
VirtualInventory virtualInventory = new VirtualInventory(27); | |
TradeSide side = new TradeSide(virtualInventory, player); | |
// Restrict updates so only the owner's inventory changes. | |
virtualInventory.setPreUpdateHandler(event -> { | |
if (event.getUpdateReason() instanceof PlayerUpdateReason reason) { | |
if (!player.equals(reason.getPlayer())) { | |
event.setCancelled(true); | |
} | |
} | |
}); | |
trade.getSides().add(side); | |
} | |
return trade; | |
} | |
/** | |
* Opens the GUI for a specific viewer and configures | |
* the layout to show both sides of the trade. | |
* | |
* @param viewer the player who will see the GUI | |
* @param trade the trade to be shown | |
*/ | |
public void openGui(Player viewer, Trade trade) { | |
// Basic layout structure for 6 rows | |
Gui.Builder.Normal layout = Gui.normal() | |
.setStructure( | |
"y y y y 1 p p p p", | |
"y y y y x p p p p", | |
"y y y y x p p p p", | |
"y y y y x p p p p", | |
"y y y y x p p p p", | |
"y y y y 2 p p p p" | |
) | |
.addIngredient('x', new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE)); | |
// Find the correct participant side and opposite side | |
trade.getSide(viewer).ifPresent(tradeSide -> { | |
drawSide(layout, tradeSide, trade, 'y', '1'); | |
trade.getOppositeSide(tradeSide).ifPresent(opposite -> { | |
drawSide(layout, opposite, trade, 'p', '2'); | |
}); | |
}); | |
// Build the Window and open it for the viewer | |
Window window = Window.single() | |
.setTitle("Trade") | |
.setGui(layout) | |
.setViewer(viewer) | |
.build(); | |
// Keep track of this window for closure after the trade completes | |
trade.getWindows().add(window); | |
window.open(); | |
} | |
/** | |
* Configures the GUI's item symbols for a specific side of the trade. | |
* Sets up the confirm button that the user can click to finalize. | |
*/ | |
private void drawSide(Gui.Builder.Normal builder, TradeSide tradeSide, Trade trade, char invSymbol, char confirmSymbol) { | |
// The inventory area is mapped to the provided invSymbol | |
builder.addIngredient(invSymbol, tradeSide.getVirtualInventory()); | |
// Prepare the SimpleItem for the confirm button | |
SimpleItem confirmButton = new SimpleItem( | |
state -> new ItemBuilder(tradeSide.isConfirmed() ? Material.LIME_DYE : Material.GRAY_DYE).get(), | |
click -> { | |
// Only the owner of the side can toggle confirmation | |
if (click.getClickType().isLeftClick() && tradeSide.getPlayer().equals(click.getPlayer())) { | |
tradeSide.setConfirmed(!tradeSide.isConfirmed()); | |
// Update all confirm buttons to reflect new state | |
for (Item item : trade.getConfirmButtons()) { | |
item.notifyWindows(); | |
} | |
// If every side is confirmed, finalize the trade | |
if (trade.isConfirmed()) { | |
executeTrade(trade); | |
} | |
} | |
} | |
); | |
// Add the confirm button to the GUI, then store the button reference | |
builder.addIngredient(confirmSymbol, confirmButton); | |
trade.getConfirmButtons().add(confirmButton); | |
} | |
/** | |
* Finalizes the trade by closing all open windows, then | |
* transferring items from each participant's inventory | |
* to their opposite side. | |
*/ | |
private void executeTrade(Trade trade) { | |
// Close all windows first | |
for (Window window : trade.getWindows()) { | |
window.close(); | |
} | |
// Distribute items between opposite sides | |
for (TradeSide side : trade.getSides()) { | |
trade.getOppositeSide(side).ifPresent(opposite -> { | |
Arrays.stream(side.getVirtualInventory().getItems()) | |
.filter(Objects::nonNull) | |
.forEach(itemStack -> PlayerUtils.giveOrDropItem(opposite.getPlayer(), itemStack)); | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment