From 3e57b837aae0c3497dd6baca6dd5f111ebec86c1 Mon Sep 17 00:00:00 2001 From: Kam Date: Sat, 24 Jan 2026 01:57:46 -0500 Subject: [PATCH] Auction Fixes --- .../packets/player/AuctionActionPacket.java | 133 ++++++++++-- .../client/gui/player/GuiAuctionBidding.java | 94 ++++---- .../client/gui/player/GuiAuctionSell.java | 54 +++-- .../client/gui/player/GuiAuctionTrades.java | 76 ++++++- .../client/gui/player/SubGuiAuctionBid.java | 41 ++-- .../gui/player/SubGuiAuctionBuyNow.java | 35 ++- .../containers/ContainerAuctionBidding.java | 10 +- .../npcs/containers/ContainerAuctionSell.java | 18 +- .../containers/ContainerAuctionTrades.java | 202 +++++++++++++++--- .../containers/InventoryAuctionDisplay.java | 15 +- .../npcs/controllers/AuctionController.java | 38 ++-- .../npcs/controllers/data/AuctionClaim.java | 47 +++- .../assets/customnpcs/lang/en_US.lang | 2 + 13 files changed, 555 insertions(+), 210 deletions(-) diff --git a/src/main/java/kamkeel/npcs/network/packets/player/AuctionActionPacket.java b/src/main/java/kamkeel/npcs/network/packets/player/AuctionActionPacket.java index 45d38068b..a7549ab85 100644 --- a/src/main/java/kamkeel/npcs/network/packets/player/AuctionActionPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/player/AuctionActionPacket.java @@ -20,6 +20,7 @@ import noppes.npcs.config.ConfigMarket; import noppes.npcs.constants.EnumGuiType; import noppes.npcs.constants.EnumRoleType; import noppes.npcs.controllers.AuctionController; +import noppes.npcs.controllers.data.AuctionClaim; import noppes.npcs.controllers.data.AuctionFilter; import noppes.npcs.controllers.data.AuctionListing; @@ -176,8 +177,18 @@ public class AuctionActionPacket extends AbstractPacket { if (npc.advanced.role != EnumRoleType.Auctioneer) return; if (!ConfigMarket.AuctionEnabled) return; - Action requestedAction = Action.values()[in.readInt()]; + int actionOrdinal = in.readInt(); + if (actionOrdinal < 0 || actionOrdinal >= Action.values().length) { + return; // Invalid action + } + Action requestedAction = Action.values()[actionOrdinal]; + AuctionController controller = AuctionController.getInstance(); + if (controller == null) { + sendError(playerMP, "Auction system is not available."); + return; + } + String result = null; switch (requestedAction) { @@ -292,8 +303,10 @@ public class AuctionActionPacket extends AbstractPacket { String result = controller.createListing(player, item, startingPrice, buyoutPrice); if (result != null) { - // Failed - return item - player.inventory.addItemStackToInventory(item); + // Failed - return item (drop if inventory full to prevent item loss) + if (!player.inventory.addItemStackToInventory(item)) { + player.entityDropItem(item, 0.5f); + } } return result; } @@ -308,6 +321,46 @@ public class AuctionActionPacket extends AbstractPacket { default: guiType = EnumGuiType.PlayerAuction; break; } NoppesUtilServer.sendOpenGui(player, guiType, npc); + + // Send trades data when opening the trades page + if (page == 2) { + AuctionController controller = AuctionController.getInstance(); + if (controller != null) { + sendTradesData(player, controller); + } + } + } + + /** Send full trades data to player (listings, bids, claims) */ + private void sendTradesData(EntityPlayerMP player, AuctionController controller) { + NBTTagCompound response = new NBTTagCompound(); + response.setBoolean("TradesData", true); + + // Active listings (items player is selling) + List listings = controller.getPlayerActiveListings(player.getUniqueID()); + NBTTagList listingsNBT = new NBTTagList(); + for (AuctionListing listing : listings) { + listingsNBT.appendTag(listing.writeToNBT(new NBTTagCompound())); + } + response.setTag("ActiveListings", listingsNBT); + + // Active bids (items player is bidding on) + List bids = controller.getPlayerActiveBids(player.getUniqueID()); + NBTTagList bidsNBT = new NBTTagList(); + for (AuctionListing bid : bids) { + bidsNBT.appendTag(bid.writeToNBT(new NBTTagCompound())); + } + response.setTag("ActiveBids", bidsNBT); + + // Claims (items/currency to claim) + List claims = controller.getPlayerClaims(player.getUniqueID()); + NBTTagList claimsNBT = new NBTTagList(); + for (AuctionClaim claim : claims) { + claimsNBT.appendTag(claim.writeToNBT(new NBTTagCompound())); + } + response.setTag("Claims", claimsNBT); + + GuiDataPacket.sendGuiData(player, response); } private void handleOpenBidding(ByteBuf in, EntityPlayerMP player, AuctionController controller) throws IOException { @@ -368,15 +421,67 @@ public class AuctionActionPacket extends AbstractPacket { // Helpers // ========================================= - private boolean removeItemFromInventory(EntityPlayerMP player, ItemStack target) { - for (int i = 0; i < player.inventory.getSizeInventory(); i++) { - ItemStack stack = player.inventory.getStackInSlot(i); - if (stack != null && ItemStack.areItemStacksEqual(stack, target)) { - player.inventory.setInventorySlotContents(i, null); - return true; + /** + * Checks if two ItemStacks are the same type (item, damage, NBT) ignoring stack size. + */ + private boolean areItemsSameType(ItemStack item1, ItemStack item2) { + if (item1 == null || item2 == null) + return item1 == item2; + if (item1.getItem() != item2.getItem()) + return false; + if (item1.getItemDamage() != item2.getItemDamage()) + return false; + return ItemStack.areItemStackTagsEqual(item1, item2); + } + + /** + * Counts total matching items across all inventory stacks. + */ + private int countItemsInInventory(EntityPlayerMP player, ItemStack target) { + int count = 0; + for (int i = 0; i < player.inventory.mainInventory.length; i++) { + ItemStack stack = player.inventory.mainInventory[i]; + if (stack != null && areItemsSameType(stack, target)) { + count += stack.stackSize; } } - return false; + return count; + } + + /** + * Removes items from inventory, consuming from multiple stacks if needed. + * Returns true if successful, false if not enough items. + */ + private boolean removeItemFromInventory(EntityPlayerMP player, ItemStack target) { + if (target == null) return false; + + int needed = target.stackSize; + + // First verify player has enough total items + int available = countItemsInInventory(player, target); + if (available < needed) { + return false; + } + + // Consume items from matching stacks + for (int i = 0; i < player.inventory.mainInventory.length && needed > 0; i++) { + ItemStack stack = player.inventory.mainInventory[i]; + if (stack == null || !areItemsSameType(stack, target)) + continue; + + if (needed >= stack.stackSize) { + // Need entire stack + needed -= stack.stackSize; + player.inventory.mainInventory[i] = null; + } else { + // Need only part of stack + stack.splitStack(needed); + needed = 0; + } + } + + player.inventory.markDirty(); + return true; } private void sendError(EntityPlayerMP player, String message) { @@ -385,9 +490,11 @@ public class AuctionActionPacket extends AbstractPacket { } private void sendTradesUpdate(EntityPlayerMP player) { - NBTTagCompound compound = new NBTTagCompound(); - compound.setBoolean("TradesUpdate", true); - GuiDataPacket.sendGuiData(player, compound); + // Send full trades data to refresh the client + AuctionController controller = AuctionController.getInstance(); + if (controller != null) { + sendTradesData(player, controller); + } } private void sendListingsUpdate(EntityPlayerMP player) { diff --git a/src/main/java/noppes/npcs/client/gui/player/GuiAuctionBidding.java b/src/main/java/noppes/npcs/client/gui/player/GuiAuctionBidding.java index 5159b09de..cc5e5d804 100644 --- a/src/main/java/noppes/npcs/client/gui/player/GuiAuctionBidding.java +++ b/src/main/java/noppes/npcs/client/gui/player/GuiAuctionBidding.java @@ -29,20 +29,19 @@ public class GuiAuctionBidding extends GuiAuctionInterface implements ISubGuiLis private static final ResourceLocation ICON_BID = new ResourceLocation("customnpcs", "textures/items/npcCoinDiamond.png"); private static final ResourceLocation ICON_BUYOUT = new ResourceLocation("customnpcs", "textures/items/npcCoinEmerald.png"); - // Layout - non-final for testing/positioning - protected int itemSlotX = 56; - protected int itemSlotY = 52; - protected int btnBidX = 56; - protected int btnBidY = 80; - protected int btnBuyoutX = 80; - protected int btnBuyoutY = 80; - protected int infoX = 110; - protected int infoY = 50; - protected int infoWidth = 130; + // Layout + private int itemSlotX = 70; + private int itemSlotY = 75; + private int btnBidX = 58; + private int btnBidY = 110; + private int btnBuyoutX = 82; + private int btnBuyoutY = 110; + private int infoX = 110; + private int infoY = 50; // Button IDs - private static final int BTN_BID = 200; - private static final int BTN_BUYOUT = 201; + private int btnBidId = 200; + private int btnBuyoutId = 201; private final ContainerAuctionBidding biddingContainer; private AuctionListing listing; @@ -64,12 +63,12 @@ public class GuiAuctionBidding extends GuiAuctionInterface implements ISubGuiLis super.initGui(); // Bid button (diamond coin) - btnBid = new GuiAuctionNavButton(BTN_BID, guiLeft + btnBidX, guiTop + btnBidY, + btnBid = new GuiAuctionNavButton(btnBidId, guiLeft + btnBidX, guiTop + btnBidY, "auction.bid.place", ICON_BID); addButton(btnBid); // Buy Now button (emerald coin) - only visible if listing has buyout - btnBuyout = new GuiAuctionNavButton(BTN_BUYOUT, guiLeft + btnBuyoutX, guiTop + btnBuyoutY, + btnBuyout = new GuiAuctionNavButton(btnBuyoutId, guiLeft + btnBuyoutX, guiTop + btnBuyoutY, "auction.bid.buyout", ICON_BUYOUT); addButton(btnBuyout); @@ -112,15 +111,12 @@ public class GuiAuctionBidding extends GuiAuctionInterface implements ISubGuiLis if (listing == null) return; - switch (button.id) { - case BTN_BID: - openBidSubGui(); - break; - case BTN_BUYOUT: - if (listing.hasBuyout()) { - openBuyNowSubGui(); - } - break; + if (button.id == btnBidId) { + openBidSubGui(); + } else if (button.id == btnBuyoutId) { + if (listing.hasBuyout()) { + openBuyNowSubGui(); + } } } @@ -190,38 +186,46 @@ public class GuiAuctionBidding extends GuiAuctionInterface implements ISubGuiLis /** Draw auction listing information on the right side */ private void drawAuctionInfo() { - int x = guiLeft + infoX; + int x = guiLeft + 105; int y = guiTop + infoY; - int lineHeight = 12; - String currencyName = AuctionClientConfig.getCurrencyName(); + int xRight = guiLeft + 215; + + int lineHeight = 9; // Seller String sellerLabel = StatCollector.translateToLocal("auction.seller").replace("%s", ""); fontRendererObj.drawString(EnumChatFormatting.GRAY + sellerLabel, x, y, 0xFFFFFF); - y += lineHeight; - fontRendererObj.drawString(EnumChatFormatting.WHITE + listing.sellerName, x + 10, y, 0xFFFFFF); + + String label = listing.sellerName; + int length = fontRendererObj.getStringWidth(label); + fontRendererObj.drawString(EnumChatFormatting.WHITE + listing.sellerName, xRight - length, y, 0xFFFFFF); y += lineHeight + 4; // Current Bid or Starting Price if (listing.hasBids()) { - String bidLabel = StatCollector.translateToLocal("auction.info.currentBid"); + String bidLabel = StatCollector.translateToLocal("auction.info.currentBid") + ":"; fontRendererObj.drawString(EnumChatFormatting.YELLOW + bidLabel, x, y, 0xFFFFFF); - y += lineHeight; - fontRendererObj.drawString(EnumChatFormatting.GOLD + formatCurrency(listing.currentBid) + " " + currencyName, x + 10, y, 0xFFFFFF); + + label = formatCurrency(listing.currentBid); + length = fontRendererObj.getStringWidth(label); + fontRendererObj.drawString(EnumChatFormatting.GOLD + label, xRight - length, y, 0xFFFFFF); y += lineHeight + 4; // Current Bidder String bidderLabel = StatCollector.translateToLocal("auction.info.highBidder"); fontRendererObj.drawString(EnumChatFormatting.GRAY + bidderLabel, x, y, 0xFFFFFF); - y += lineHeight; - String bidderName = listing.highBidderName != null ? listing.highBidderName : "???"; - fontRendererObj.drawString(EnumChatFormatting.WHITE + bidderName, x + 10, y, 0xFFFFFF); + + label = listing.highBidderName != null ? listing.highBidderName : "???"; + length = fontRendererObj.getStringWidth(label); + fontRendererObj.drawString(EnumChatFormatting.WHITE + label, xRight - length, y, 0xFFFFFF); y += lineHeight + 4; } else { String startLabel = StatCollector.translateToLocal("auction.info.startingPrice"); fontRendererObj.drawString(EnumChatFormatting.YELLOW + startLabel, x, y, 0xFFFFFF); - y += lineHeight; - fontRendererObj.drawString(EnumChatFormatting.GOLD + formatCurrency(listing.startingPrice) + " " + currencyName, x + 10, y, 0xFFFFFF); + + label = formatCurrency(listing.startingPrice); + length = fontRendererObj.getStringWidth(label); + fontRendererObj.drawString(EnumChatFormatting.GOLD + label, xRight - length, y, 0xFFFFFF); y += lineHeight + 4; } @@ -229,25 +233,31 @@ public class GuiAuctionBidding extends GuiAuctionInterface implements ISubGuiLis if (listing.hasBuyout()) { String buyoutLabel = StatCollector.translateToLocal("auction.info.buyoutPrice"); fontRendererObj.drawString(EnumChatFormatting.GREEN + buyoutLabel, x, y, 0xFFFFFF); - y += lineHeight; - fontRendererObj.drawString(EnumChatFormatting.GREEN + formatCurrency(listing.buyoutPrice) + " " + currencyName, x + 10, y, 0xFFFFFF); + + label = formatCurrency(listing.buyoutPrice); + length = fontRendererObj.getStringWidth(label); + fontRendererObj.drawString(EnumChatFormatting.GREEN + label, xRight - length, y, 0xFFFFFF); y += lineHeight + 4; } // Time Remaining String timeLabel = StatCollector.translateToLocal("auction.info.timeLeft"); fontRendererObj.drawString(EnumChatFormatting.GRAY + timeLabel, x, y, 0xFFFFFF); - y += lineHeight; long timeRemaining = listing.getTimeRemaining(); EnumChatFormatting timeColor = timeRemaining < 3600000 ? EnumChatFormatting.RED : EnumChatFormatting.WHITE; - fontRendererObj.drawString(timeColor + formatTimeRemaining(timeRemaining), x + 10, y, 0xFFFFFF); + + label = timeColor + formatTimeRemaining(timeRemaining); + length = fontRendererObj.getStringWidth(label); + fontRendererObj.drawString(label, xRight - length, y, 0xFFFFFF); y += lineHeight + 4; // Bid Count String bidCountLabel = StatCollector.translateToLocal("auction.info.bidCount"); fontRendererObj.drawString(EnumChatFormatting.GRAY + bidCountLabel, x, y, 0xFFFFFF); - y += lineHeight; - fontRendererObj.drawString(EnumChatFormatting.WHITE + String.valueOf(listing.bidCount), x + 10, y, 0xFFFFFF); + + label = String.valueOf(listing.bidCount); + length = fontRendererObj.getStringWidth(label); + fontRendererObj.drawString(EnumChatFormatting.WHITE + label, xRight - length, y, 0xFFFFFF); } @Override diff --git a/src/main/java/noppes/npcs/client/gui/player/GuiAuctionSell.java b/src/main/java/noppes/npcs/client/gui/player/GuiAuctionSell.java index 268ddc701..8fe8c2d9c 100644 --- a/src/main/java/noppes/npcs/client/gui/player/GuiAuctionSell.java +++ b/src/main/java/noppes/npcs/client/gui/player/GuiAuctionSell.java @@ -30,18 +30,18 @@ public class GuiAuctionSell extends GuiAuctionInterface implements ITextfieldLis private static final ResourceLocation ICON_COIN = new ResourceLocation("customnpcs", "textures/items/npcCoinBronze.png"); private static final ResourceLocation ICON_BAG = new ResourceLocation("customnpcs", "textures/items/npcBag.png"); - // Layout - non-final for testing/positioning - protected int contentX = 50; - protected int contentY = 50; - protected int confirmBtnX = 180; - protected int confirmBtnY = 120; - protected int navYBuyout = 109; + // Layout + private int contentX = 58; + private int contentY = 50; + private int confirmBtnX = 200; + private int confirmBtnY = 118; + private int navYBuyout = 109; // Component IDs - private static final int TXT_STARTING_PRICE = 1; - private static final int TXT_BUYOUT_PRICE = 2; - private static final int BTN_CONFIRM = 10; - private static final int BTN_ALLOW_BUYOUT = 11; + private int txtStartingPriceId = 1; + private int txtBuyoutPriceId = 2; + private int btnConfirmId = 10; + private int btnAllowBuyoutId = 11; private final ContainerAuctionSell sellContainer; private long startingPrice = 0; @@ -67,7 +67,7 @@ public class GuiAuctionSell extends GuiAuctionInterface implements ITextfieldLis // Starting Price label and field addLabel(new GuiNpcLabel(1, "auction.sell.startingPrice", guiLeft + contentX, y + 5, 0xFFFFFF)); - GuiNpcTextField startingField = new GuiNpcTextField(TXT_STARTING_PRICE, this, fontRendererObj, + GuiNpcTextField startingField = new GuiNpcTextField(txtStartingPriceId, this, fontRendererObj, guiLeft + contentX + 80, y, 80, 18, startingPrice > 0 ? String.valueOf(startingPrice) : ""); startingField.setIntegersOnly(); addTextField(startingField); @@ -78,20 +78,20 @@ public class GuiAuctionSell extends GuiAuctionInterface implements ITextfieldLis lblBuyout = new GuiNpcLabel(2, "auction.sell.buyoutPrice", guiLeft + contentX, y + 5, 0xFFFFFF); addLabel(lblBuyout); - txtBuyout = new GuiNpcTextField(TXT_BUYOUT_PRICE, this, fontRendererObj, + txtBuyout = new GuiNpcTextField(txtBuyoutPriceId, this, fontRendererObj, guiLeft + contentX + 80, y, 80, 18, buyoutPrice > 0 ? String.valueOf(buyoutPrice) : ""); txtBuyout.setIntegersOnly(); addTextField(txtBuyout); // Allow Buyout toggle button (18x18 with bag icon) - btnAllowBuyout = new GuiAuctionNavButton(BTN_ALLOW_BUYOUT, guiLeft + navX, guiTop + navYBuyout, + btnAllowBuyout = new GuiAuctionNavButton(btnAllowBuyoutId, guiLeft + navX, guiTop + navYBuyout, "auction.sell.allowBuyout", ICON_BAG); btnAllowBuyout.setToggle(true); // This is a toggle, not a nav button btnAllowBuyout.setSelected(allowBuyout); addButton(btnAllowBuyout); // Confirm button (custom textured button - using GuiAuctionNavButton) - GuiAuctionNavButton btnConfirm = new GuiAuctionNavButton(BTN_CONFIRM, guiLeft + confirmBtnX, guiTop + confirmBtnY, + GuiAuctionNavButton btnConfirm = new GuiAuctionNavButton(btnConfirmId, guiLeft + confirmBtnX - 20, guiTop + confirmBtnY - 10, "auction.sell.confirm", ICON_COIN); addButton(btnConfirm); @@ -141,13 +141,13 @@ public class GuiAuctionSell extends GuiAuctionInterface implements ITextfieldLis @Override public void unFocused(GuiNpcTextField textfield) { - if (textfield.id == TXT_STARTING_PRICE) { + if (textfield.id == txtStartingPriceId) { try { startingPrice = Long.parseLong(textfield.getText()); } catch (NumberFormatException e) { startingPrice = 0; } - } else if (textfield.id == TXT_BUYOUT_PRICE) { + } else if (textfield.id == txtBuyoutPriceId) { try { String text = textfield.getText().trim(); buyoutPrice = text.isEmpty() ? 0 : Long.parseLong(text); @@ -162,9 +162,9 @@ public class GuiAuctionSell extends GuiAuctionInterface implements ITextfieldLis public void actionPerformed(GuiButton button) { super.actionPerformed(button); - if (button.id == BTN_CONFIRM) { + if (button.id == btnConfirmId) { createListing(); - } else if (button.id == BTN_ALLOW_BUYOUT) { + } else if (button.id == btnAllowBuyoutId) { allowBuyout = !allowBuyout; btnAllowBuyout.setSelected(allowBuyout); updateBuyoutVisibility(); @@ -175,8 +175,8 @@ public class GuiAuctionSell extends GuiAuctionInterface implements ITextfieldLis private void createListing() { // Get latest values from text fields - GuiNpcTextField startingField = getTextField(TXT_STARTING_PRICE); - GuiNpcTextField buyoutField = getTextField(TXT_BUYOUT_PRICE); + GuiNpcTextField startingField = getTextField(txtStartingPriceId); + GuiNpcTextField buyoutField = getTextField(txtBuyoutPriceId); if (startingField != null) { try { @@ -234,21 +234,20 @@ public class GuiAuctionSell extends GuiAuctionInterface implements ITextfieldLis @Override protected void drawAuctionContent(float partialTicks, int mouseX, int mouseY) { // Draw sell slot background (centered in content area) - int slotX = guiLeft + contentX + 30; - int slotY = guiTop + contentY + 55; + int slotX = guiLeft + contentX; + int slotY = guiTop + contentY + 50; drawAuctionSlot(slotX, slotY); // Draw listing fee info (uses cached config from server) String feeText = StatCollector.translateToLocal("auction.sell.fee") .replace("%s", formatCurrency(AuctionClientConfig.getListingFee()) + " " + AuctionClientConfig.getCurrencyName()); - fontRendererObj.drawString(EnumChatFormatting.GRAY + feeText, guiLeft + contentX, guiTop + contentY + 80, 0xFFFFFF); + fontRendererObj.drawString(EnumChatFormatting.GRAY + feeText, guiLeft + contentX, guiTop + contentY + 77, 0xFFFFFF); // Draw error message if any if (errorMessage != null) { fontRendererObj.drawString(EnumChatFormatting.RED + errorMessage, guiLeft + contentX, guiTop + contentY + 95, 0xFFFFFF); } - // Draw item count indicator for sell slot drawSellSlotIndicator(slotX, slotY); } @@ -261,14 +260,11 @@ public class GuiAuctionSell extends GuiAuctionInterface implements ITextfieldLis // Color: green if valid, red if over limit int color = sellItem.stackSize <= inventoryCount ? 0x55FF55 : 0xFF5555; - - int textWidth = fontRendererObj.getStringWidth(countText); fontRendererObj.drawStringWithShadow(countText, slotX + 18 + 4, slotY + 5, color); } else { // Draw "Place item here" hint String hint = StatCollector.translateToLocal("auction.sell.placeItem"); - int hintWidth = fontRendererObj.getStringWidth(hint); - fontRendererObj.drawString(EnumChatFormatting.GRAY + hint, slotX + 9 - hintWidth / 2 + 20, slotY + 5, 0xFFFFFF); + fontRendererObj.drawString(EnumChatFormatting.GRAY + hint, slotX + 20, slotY + 5, 0xFFFFFF); } } @@ -324,7 +320,7 @@ public class GuiAuctionSell extends GuiAuctionInterface implements ITextfieldLis List tooltip = null; // Check all buttons - GuiNpcButton confirmBtn = getButton(BTN_CONFIRM); + GuiNpcButton confirmBtn = getButton(btnConfirmId); if (confirmBtn instanceof GuiAuctionNavButton) { GuiAuctionNavButton btn = (GuiAuctionNavButton) confirmBtn; if (btn.isHovered()) { diff --git a/src/main/java/noppes/npcs/client/gui/player/GuiAuctionTrades.java b/src/main/java/noppes/npcs/client/gui/player/GuiAuctionTrades.java index df9298426..2bcdce495 100644 --- a/src/main/java/noppes/npcs/client/gui/player/GuiAuctionTrades.java +++ b/src/main/java/noppes/npcs/client/gui/player/GuiAuctionTrades.java @@ -28,6 +28,7 @@ public class GuiAuctionTrades extends GuiAuctionInterface implements IGuiData { // Icon textures private static final ResourceLocation ICON_X = new ResourceLocation("customnpcs", "textures/gui/auction/x_icon.png"); private static final ResourceLocation ICON_CHECK = new ResourceLocation("customnpcs", "textures/gui/auction/check_icon.png"); + private static final ResourceLocation ICON_COIN = new ResourceLocation("customnpcs", "textures/items/npcCoinGold.png"); // Grid layout private static final int GRID_X = 56; @@ -37,9 +38,10 @@ public class GuiAuctionTrades extends GuiAuctionInterface implements IGuiData { private static final int TOTAL_SLOTS = COLS * ROWS; // Slot tint colors (ARGB) - private static final int TINT_BLUE = 0x303060FF; // Active listing/bid - private static final int TINT_GREEN = 0x3030FF60; // Claimable - private static final int TINT_RED = 0x30FF4040; // Refund + private static final int TINT_BLUE = 0x303060FF; // Active listing/bid (selling) + private static final int TINT_GREEN = 0x3030FF30; // Sold (currency claim) or Won (item claim) + private static final int TINT_YELLOW = 0x30FFFF30; // Outbid (refund claim) + private static final int TINT_RED = 0x30FF3030; // Expired/Returned (item returned to seller) private final ContainerAuctionTrades tradesContainer; private int maxTradeSlots; @@ -89,6 +91,10 @@ public class GuiAuctionTrades extends GuiAuctionInterface implements IGuiData { // Right-click on own listing = show cancel setPending(slot, PendingOp.CANCEL); NoppesUtil.clickSound(); + } else if (listing != null && tradesContainer.isBiddingAt(slot) && mouseButton == 0) { + // Left-click on active bid = open bidding GUI to increase bid + AuctionActionPacket.openBidding(listing.id); + NoppesUtil.clickSound(); } else if (claim != null && mouseButton == 0) { // Left-click on claim = show claim setPending(slot, PendingOp.CLAIM); @@ -156,7 +162,6 @@ public class GuiAuctionTrades extends GuiAuctionInterface implements IGuiData { int col = relX / 18; int row = relY / 18; if (col >= COLS || row >= ROWS) return -1; - if (relX % 18 >= 18 || relY % 18 >= 18) return -1; return col + row * COLS; } @@ -184,13 +189,19 @@ public class GuiAuctionTrades extends GuiAuctionInterface implements IGuiData { drawDarkenedOverlay(x, y); } - // Pending operation icon + // Pending operation icon or currency claim coin if (slot == pendingSlot) { if (pendingOp == PendingOp.CANCEL) { drawIconOverlay(x, y, ICON_X); } else if (pendingOp == PendingOp.CLAIM) { drawIconOverlay(x, y, ICON_CHECK); } + } else { + // Draw coin icon for currency/refund claims (no item in slot) + AuctionClaim claim = tradesContainer.getClaimAt(slot); + if (claim != null && (claim.type == EnumClaimType.CURRENCY || claim.type == EnumClaimType.REFUND)) { + drawIconOverlay(x, y, ICON_COIN); + } } } } @@ -202,9 +213,22 @@ public class GuiAuctionTrades extends GuiAuctionInterface implements IGuiData { AuctionClaim claim = tradesContainer.getClaimAt(slot); if (listing != null) { + // Active listing or bid - Blue return TINT_BLUE; } else if (claim != null) { - return (claim.type == EnumClaimType.REFUND) ? TINT_RED : TINT_GREEN; + switch (claim.type) { + case CURRENCY: + // Sold item - Green + return TINT_GREEN; + case REFUND: + // Outbid - Yellow + return TINT_YELLOW; + case ITEM: + // Won item (green) or Returned/Expired (red) + return claim.isReturned ? TINT_RED : TINT_GREEN; + default: + return TINT_GREEN; + } } return 0; } @@ -247,12 +271,37 @@ public class GuiAuctionTrades extends GuiAuctionInterface implements IGuiData { } else if (tradesContainer.isBiddingAt(slot)) { tooltip.add(EnumChatFormatting.AQUA + StatCollector.translateToLocal("auction.trades.activeBid")); tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.trades.winningBid")); + tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.trades.leftClickToIncrease")); } } else if (claim != null) { - if (claim.type == EnumClaimType.REFUND) { - tooltip.add(EnumChatFormatting.RED + StatCollector.translateToLocal("auction.trades.refundClaim")); - } else { - tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.pendingClaim")); + switch (claim.type) { + case CURRENCY: + tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.soldClaim")); + if (!claim.itemName.isEmpty()) { + tooltip.add(EnumChatFormatting.WHITE + claim.itemName); + } + if (!claim.otherPlayerName.isEmpty()) { + tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.trades.buyer") + ": " + EnumChatFormatting.AQUA + claim.otherPlayerName); + } + tooltip.add(EnumChatFormatting.GOLD + String.format("%,d", claim.currency) + " " + AuctionClientConfig.getCurrencyName()); + break; + case REFUND: + tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.trades.refundClaim")); + if (!claim.itemName.isEmpty()) { + tooltip.add(EnumChatFormatting.WHITE + claim.itemName); + } + if (!claim.otherPlayerName.isEmpty()) { + tooltip.add(EnumChatFormatting.GRAY + StatCollector.translateToLocal("auction.trades.outbidBy") + ": " + EnumChatFormatting.AQUA + claim.otherPlayerName); + } + tooltip.add(EnumChatFormatting.GOLD + String.format("%,d", claim.currency) + " " + AuctionClientConfig.getCurrencyName()); + break; + case ITEM: + if (claim.isReturned) { + tooltip.add(EnumChatFormatting.RED + StatCollector.translateToLocal("auction.trades.expiredClaim")); + } else { + tooltip.add(EnumChatFormatting.GREEN + StatCollector.translateToLocal("auction.trades.wonClaim")); + } + break; } tooltip.add(EnumChatFormatting.YELLOW + StatCollector.translateToLocal("auction.trades.leftClickToClaim")); } else if (slot >= maxTradeSlots) { @@ -280,7 +329,12 @@ public class GuiAuctionTrades extends GuiAuctionInterface implements IGuiData { @Override public void setGuiData(NBTTagCompound compound) { - if (compound.hasKey("TradesUpdate")) { + if (compound.hasKey("TradesData")) { + // Full trades data received from server + tradesContainer.setTradesData(compound); + clearPending(); + } else if (compound.hasKey("TradesUpdate")) { + // Legacy flag - should not happen now but keep for safety tradesContainer.refreshData(); } } diff --git a/src/main/java/noppes/npcs/client/gui/player/SubGuiAuctionBid.java b/src/main/java/noppes/npcs/client/gui/player/SubGuiAuctionBid.java index da828b7a9..de7ab50e4 100644 --- a/src/main/java/noppes/npcs/client/gui/player/SubGuiAuctionBid.java +++ b/src/main/java/noppes/npcs/client/gui/player/SubGuiAuctionBid.java @@ -16,9 +16,9 @@ import noppes.npcs.client.gui.util.SubGuiInterface; * Shows minimum bid, player balance, and input field. */ public class SubGuiAuctionBid extends SubGuiInterface implements ITextfieldListener { - private static final int TXT_BID = 1; - private static final int BTN_BID = 10; - private static final int BTN_CANCEL = 11; + private int txtBidId = 1; + private int btnBidId = 10; + private int btnCancelId = 11; private final String listingId; private final long minimumBid; @@ -47,25 +47,21 @@ public class SubGuiAuctionBid extends SubGuiInterface implements ITextfieldListe int y = guiTop + 15; String currencyName = AuctionClientConfig.getCurrencyName(); - // Title - addLabel(new GuiNpcLabel(0, "auction.bid.title", centerX - 50, y, 0xFFFFFF)); - y += 18; - // Minimum bid info String minText = StatCollector.translateToLocal("auction.bid.minimum") - .replace("%s", EnumChatFormatting.GOLD + formatCurrency(minimumBid) + " " + currencyName); - addLabel(new GuiNpcLabel(1, minText, guiLeft + 15, y, 0xAAAAAA)); + .replace("%s", EnumChatFormatting.DARK_RED + formatCurrency(minimumBid) + " " + currencyName); + addLabel(new GuiNpcLabel(1, minText, guiLeft + 15, y)); y += 14; // Your balance String balanceText = StatCollector.translateToLocal("auction.bid.yourBalance") - .replace("%s", EnumChatFormatting.WHITE + formatCurrency(playerBalance) + " " + currencyName); - addLabel(new GuiNpcLabel(2, balanceText, guiLeft + 15, y, 0xAAAAAA)); + .replace("%s", EnumChatFormatting.DARK_GREEN + formatCurrency(playerBalance) + " " + currencyName); + addLabel(new GuiNpcLabel(2, balanceText, guiLeft + 15, y)); y += 18; // Bid amount field - addLabel(new GuiNpcLabel(3, "auction.bid.yourBid", guiLeft + 15, y + 4, 0xFFFFFF)); - GuiNpcTextField bidField = new GuiNpcTextField(TXT_BID, this, fontRendererObj, + addLabel(new GuiNpcLabel(3, "auction.bid.yourBid", guiLeft + 15, y + 4)); + GuiNpcTextField bidField = new GuiNpcTextField(txtBidId, this, fontRendererObj, guiLeft + 80, y, 100, 18, String.valueOf(minimumBid)); bidField.setIntegersOnly(); addTextField(bidField); @@ -81,13 +77,13 @@ public class SubGuiAuctionBid extends SubGuiInterface implements ITextfieldListe int totalBtnWidth = btnWidth * 2 + btnSpacing; int btnX = centerX - totalBtnWidth / 2; - addButton(new GuiNpcButton(BTN_BID, btnX, y, btnWidth, 20, "auction.bid.placeBid")); - addButton(new GuiNpcButton(BTN_CANCEL, btnX + btnWidth + btnSpacing, y, btnWidth, 20, "gui.cancel")); + addButton(new GuiNpcButton(btnBidId, btnX, y, btnWidth, 20, "auction.bid.placeBid")); + addButton(new GuiNpcButton(btnCancelId, btnX + btnWidth + btnSpacing, y, btnWidth, 20, "gui.cancel")); } @Override public void unFocused(GuiNpcTextField textfield) { - if (textfield.id == TXT_BID) { + if (textfield.id == txtBidId) { try { bidAmount = Long.parseLong(textfield.getText()); } catch (NumberFormatException e) { @@ -100,19 +96,16 @@ public class SubGuiAuctionBid extends SubGuiInterface implements ITextfieldListe @Override public void buttonEvent(GuiButton button) { - switch (button.id) { - case BTN_BID: - placeBid(); - break; - case BTN_CANCEL: - close(); - break; + if (button.id == btnBidId) { + placeBid(); + } else if (button.id == btnCancelId) { + close(); } } private void placeBid() { // Get latest value from text field - GuiNpcTextField bidField = getTextField(TXT_BID); + GuiNpcTextField bidField = getTextField(txtBidId); if (bidField != null) { try { bidAmount = Long.parseLong(bidField.getText()); diff --git a/src/main/java/noppes/npcs/client/gui/player/SubGuiAuctionBuyNow.java b/src/main/java/noppes/npcs/client/gui/player/SubGuiAuctionBuyNow.java index 4f20b4ded..f4061299b 100644 --- a/src/main/java/noppes/npcs/client/gui/player/SubGuiAuctionBuyNow.java +++ b/src/main/java/noppes/npcs/client/gui/player/SubGuiAuctionBuyNow.java @@ -14,8 +14,8 @@ import noppes.npcs.client.gui.util.SubGuiInterface; * Shows buyout price, player balance, and confirmation buttons. */ public class SubGuiAuctionBuyNow extends SubGuiInterface { - private static final int BTN_CONFIRM = 10; - private static final int BTN_CANCEL = 11; + private int btnConfirmId = 10; + private int btnCancelId = 11; private final String listingId; private final long buyoutPrice; @@ -44,25 +44,25 @@ public class SubGuiAuctionBuyNow extends SubGuiInterface { // Title String title = StatCollector.translateToLocal("auction.buyout.title"); int titleWidth = fontRendererObj.getStringWidth(title); - addLabel(new GuiNpcLabel(0, title, centerX - titleWidth / 2, y, 0xFFFFFF)); + addLabel(new GuiNpcLabel(0, title, centerX - titleWidth / 2, y)); y += 20; // Confirmation message String confirmMsg = StatCollector.translateToLocal("auction.buyout.confirm"); int confirmWidth = fontRendererObj.getStringWidth(confirmMsg); - addLabel(new GuiNpcLabel(1, confirmMsg, centerX - confirmWidth / 2, y, 0xAAAAAA)); + addLabel(new GuiNpcLabel(1, confirmMsg, centerX - confirmWidth / 2, y)); y += 16; // Buyout price - String priceText = EnumChatFormatting.GREEN + formatCurrency(buyoutPrice) + " " + currencyName; + String priceText = EnumChatFormatting.DARK_RED + formatCurrency(buyoutPrice) + " " + currencyName; int priceWidth = fontRendererObj.getStringWidth(priceText); - addLabel(new GuiNpcLabel(2, priceText, centerX - priceWidth / 2, y, 0xFFFFFF)); + addLabel(new GuiNpcLabel(2, priceText, centerX - priceWidth / 2, y)); y += 20; // Your balance String balanceLabel = StatCollector.translateToLocal("auction.bid.yourBalance"); - String balanceText = balanceLabel.replace("%s", EnumChatFormatting.WHITE + formatCurrency(playerBalance) + " " + currencyName); - addLabel(new GuiNpcLabel(3, balanceText, guiLeft + 20, y, 0xAAAAAA)); + String balanceText = balanceLabel.replace("%s", EnumChatFormatting.DARK_GREEN + formatCurrency(playerBalance) + " " + currencyName); + addLabel(new GuiNpcLabel(3, balanceText, guiLeft + 20, y)); y += 14; // Insufficient funds warning @@ -78,23 +78,20 @@ public class SubGuiAuctionBuyNow extends SubGuiInterface { int totalBtnWidth = btnWidth * 2 + btnSpacing; int btnX = centerX - totalBtnWidth / 2; - GuiNpcButton confirmBtn = new GuiNpcButton(BTN_CONFIRM, btnX, y, btnWidth, 20, "gui.yes"); + GuiNpcButton confirmBtn = new GuiNpcButton(btnConfirmId, btnX, y, btnWidth, 20, "gui.yes"); confirmBtn.enabled = playerBalance >= buyoutPrice; addButton(confirmBtn); - addButton(new GuiNpcButton(BTN_CANCEL, btnX + btnWidth + btnSpacing, y, btnWidth, 20, "gui.cancel")); + addButton(new GuiNpcButton(btnCancelId, btnX + btnWidth + btnSpacing, y, btnWidth, 20, "gui.cancel")); } @Override public void buttonEvent(GuiButton button) { - switch (button.id) { - case BTN_CONFIRM: - if (playerBalance >= buyoutPrice) { - confirmPurchase(); - } - break; - case BTN_CANCEL: - close(); - break; + if (button.id == btnConfirmId) { + if (playerBalance >= buyoutPrice) { + confirmPurchase(); + } + } else if (button.id == btnCancelId) { + close(); } } diff --git a/src/main/java/noppes/npcs/containers/ContainerAuctionBidding.java b/src/main/java/noppes/npcs/containers/ContainerAuctionBidding.java index 1d1faebe9..2b40000ac 100644 --- a/src/main/java/noppes/npcs/containers/ContainerAuctionBidding.java +++ b/src/main/java/noppes/npcs/containers/ContainerAuctionBidding.java @@ -14,9 +14,9 @@ import noppes.npcs.entity.EntityNPCInterface; */ public class ContainerAuctionBidding extends ContainerAuction { // Display slot layout - public static final int ITEM_SLOT_X = 75; - public static final int ITEM_SLOT_Y = 70; - public static final int ITEM_SLOT_INDEX = PLAYER_INV_SLOT_COUNT; + private int itemSlotX = 71; + private int itemSlotY = 76; + private int itemSlotIndex = PLAYER_INV_SLOT_COUNT; private final IInventory displayInventory; private AuctionListing listing; @@ -26,7 +26,7 @@ public class ContainerAuctionBidding extends ContainerAuction { displayInventory = new InventoryBasic("Display", false, 1); // Display slot (read-only) - addSlotToContainer(new Slot(displayInventory, 0, ITEM_SLOT_X, ITEM_SLOT_Y) { + addSlotToContainer(new Slot(displayInventory, 0, itemSlotX, itemSlotY) { @Override public boolean isItemValid(ItemStack stack) { return false; } @Override @@ -58,6 +58,6 @@ public class ContainerAuctionBidding extends ContainerAuction { * Check if this is the display slot. */ public boolean isDisplaySlot(int slotIndex) { - return slotIndex == ITEM_SLOT_INDEX; + return slotIndex == itemSlotIndex; } } diff --git a/src/main/java/noppes/npcs/containers/ContainerAuctionSell.java b/src/main/java/noppes/npcs/containers/ContainerAuctionSell.java index a4bab6efe..14b381f40 100644 --- a/src/main/java/noppes/npcs/containers/ContainerAuctionSell.java +++ b/src/main/java/noppes/npcs/containers/ContainerAuctionSell.java @@ -13,10 +13,10 @@ import noppes.npcs.entity.EntityNPCInterface; * Items are cloned from inventory, not moved. Validation happens on submit. */ public class ContainerAuctionSell extends ContainerAuction { - public static final int SELL_SLOT_X = 101; - public static final int SELL_SLOT_Y = 106; - public static final int SELL_SLOT_INDEX = PLAYER_INV_SLOT_COUNT; - private static final int MAX_STACK = 64; + private int sellSlotX = 59; + private int sellSlotY = 101; + private int sellSlotIndex = PLAYER_INV_SLOT_COUNT; + private int maxStack = 64; private final IInventory sellInventory; @@ -25,7 +25,7 @@ public class ContainerAuctionSell extends ContainerAuction { sellInventory = new InventoryBasic("Sell", false, 1); // Sell slot blocks direct interaction - addSlotToContainer(new Slot(sellInventory, 0, SELL_SLOT_X, SELL_SLOT_Y) { + addSlotToContainer(new Slot(sellInventory, 0, sellSlotX, sellSlotY) { @Override public boolean isItemValid(ItemStack stack) { return false; } @Override @@ -59,11 +59,11 @@ public class ContainerAuctionSell extends ContainerAuction { if (sellStack == null) { // Empty slot - create new stack ItemStack newStack = source.copy(); - newStack.stackSize = Math.min(toAdd, MAX_STACK); + newStack.stackSize = Math.min(toAdd, maxStack); sellInventory.setInventorySlotContents(0, newStack); } else if (itemsMatch(sellStack, source)) { // Same item - add to existing (capped at 64) - int space = MAX_STACK - sellStack.stackSize; + int space = maxStack - sellStack.stackSize; int add = Math.min(toAdd, space); if (add > 0) { sellStack.stackSize += add; @@ -71,7 +71,7 @@ public class ContainerAuctionSell extends ContainerAuction { } else { // Different item - replace ItemStack newStack = source.copy(); - newStack.stackSize = Math.min(toAdd, MAX_STACK); + newStack.stackSize = Math.min(toAdd, maxStack); sellInventory.setInventorySlotContents(0, newStack); } } @@ -93,7 +93,7 @@ public class ContainerAuctionSell extends ContainerAuction { /** Check if sell slot is this container slot index */ public boolean isSellSlot(int slotIndex) { - return slotIndex == SELL_SLOT_INDEX; + return slotIndex == sellSlotIndex; } /** Check if two stacks match using NoppesUtilPlayer.compareItems (same item, damage, and NBT) */ diff --git a/src/main/java/noppes/npcs/containers/ContainerAuctionTrades.java b/src/main/java/noppes/npcs/containers/ContainerAuctionTrades.java index 8481612d5..2d871bced 100644 --- a/src/main/java/noppes/npcs/containers/ContainerAuctionTrades.java +++ b/src/main/java/noppes/npcs/containers/ContainerAuctionTrades.java @@ -2,6 +2,9 @@ package noppes.npcs.containers; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import noppes.npcs.constants.EnumClaimType; import noppes.npcs.controllers.AuctionController; import noppes.npcs.controllers.data.AuctionClaim; import noppes.npcs.controllers.data.AuctionListing; @@ -13,6 +16,7 @@ import java.util.List; /** * Container for viewing player's auction trades. * Shows active listings, bids, and claims in a 9x5 grid. + * Order: SOLD claims, OUTBID claims, WON claims, SELLING, BIDDING, EXPIRED claims */ public class ContainerAuctionTrades extends ContainerAuction { // Grid layout @@ -28,6 +32,10 @@ public class ContainerAuctionTrades extends ContainerAuction { private List claims = new ArrayList<>(); private int hiddenSlot = -1; + // Slot type tracking for proper accessor methods + private int[] slotTypes = new int[SLOT_COUNT]; // 0=empty, 1=selling, 2=bidding, 3=claim + private int[] slotDataIndex = new int[SLOT_COUNT]; // Index into respective list + public ContainerAuctionTrades(EntityNPCInterface npc, EntityPlayer player) { super(npc, player); displayInventory = new InventoryAuctionDisplay(SLOT_COUNT); @@ -44,34 +52,158 @@ public class ContainerAuctionTrades extends ContainerAuction { refreshData(); } - /** Refresh data from controller */ + /** Refresh data from controller (server-side only) */ public void refreshData() { + // Clear existing data + clearData(); + + // Only works on server where AuctionController exists + if (AuctionController.Instance == null) return; + + List newListings = AuctionController.Instance.getPlayerActiveListings(player.getUniqueID()); + List newBids = AuctionController.Instance.getPlayerActiveBids(player.getUniqueID()); + List newClaims = AuctionController.Instance.getPlayerClaims(player.getUniqueID()); + + populateDisplay(newListings, newBids, newClaims); + } + + /** Set data from NBT received from server (client-side) */ + public void setTradesData(NBTTagCompound compound) { + // Clear existing data + clearData(); + + List newListings = new ArrayList<>(); + List newBids = new ArrayList<>(); + List newClaims = new ArrayList<>(); + + // Parse active listings + if (compound.hasKey("ActiveListings")) { + NBTTagList listingsNBT = compound.getTagList("ActiveListings", 10); + for (int i = 0; i < listingsNBT.tagCount(); i++) { + newListings.add(AuctionListing.fromNBT(listingsNBT.getCompoundTagAt(i))); + } + } + + // Parse active bids + if (compound.hasKey("ActiveBids")) { + NBTTagList bidsNBT = compound.getTagList("ActiveBids", 10); + for (int i = 0; i < bidsNBT.tagCount(); i++) { + newBids.add(AuctionListing.fromNBT(bidsNBT.getCompoundTagAt(i))); + } + } + + // Parse claims + if (compound.hasKey("Claims")) { + NBTTagList claimsNBT = compound.getTagList("Claims", 10); + for (int i = 0; i < claimsNBT.tagCount(); i++) { + newClaims.add(AuctionClaim.fromNBT(claimsNBT.getCompoundTagAt(i))); + } + } + + populateDisplay(newListings, newBids, newClaims); + } + + /** Clear all data and reset display */ + private void clearData() { activeListings.clear(); activeBids.clear(); claims.clear(); displayInventory.clear(); - if (AuctionController.Instance == null) return; + // Reset slot tracking + for (int i = 0; i < SLOT_COUNT; i++) { + slotTypes[i] = 0; + slotDataIndex[i] = -1; + } + } - activeListings = AuctionController.Instance.getPlayerActiveListings(player.getUniqueID()); - activeBids = AuctionController.Instance.getPlayerActiveBids(player.getUniqueID()); - claims = AuctionController.Instance.getPlayerClaims(player.getUniqueID()); + /** Populate display from provided data lists */ + private void populateDisplay(List newListings, List newBids, List allClaims) { + activeListings = new ArrayList<>(newListings); + activeBids = new ArrayList<>(newBids); - // Populate display: listings first, then bids, then claims + // Sort claims into categories for proper display order + List soldClaims = new ArrayList<>(); // CURRENCY - sold items + List outbidClaims = new ArrayList<>(); // REFUND - outbid refunds + List wonClaims = new ArrayList<>(); // ITEM won (isReturned=false) + List expiredClaims = new ArrayList<>(); // ITEM returned (isReturned=true) + + for (AuctionClaim claim : allClaims) { + if (claim.type == EnumClaimType.CURRENCY) { + soldClaims.add(claim); + } else if (claim.type == EnumClaimType.REFUND) { + outbidClaims.add(claim); + } else if (claim.type == EnumClaimType.ITEM) { + if (claim.isReturned) { + expiredClaims.add(claim); + } else { + wonClaims.add(claim); + } + } + } + + // Populate display in order: SOLD, OUTBID, WON, SELLING, BIDDING, EXPIRED int slot = 0; + + // 1. Sold claims (green) - you have money to claim! + for (AuctionClaim claim : soldClaims) { + if (slot >= SLOT_COUNT) break; + claims.add(claim); + displayInventory.setClaimItem(slot, claim); + slotTypes[slot] = 3; // claim + slotDataIndex[slot] = claims.size() - 1; + slot++; + } + + // 2. Outbid claims (yellow) - refunds available + for (AuctionClaim claim : outbidClaims) { + if (slot >= SLOT_COUNT) break; + claims.add(claim); + displayInventory.setClaimItem(slot, claim); + slotTypes[slot] = 3; // claim + slotDataIndex[slot] = claims.size() - 1; + slot++; + } + + // 3. Won claims (green) - items to claim! + for (AuctionClaim claim : wonClaims) { + if (slot >= SLOT_COUNT) break; + claims.add(claim); + displayInventory.setClaimItem(slot, claim); + slotTypes[slot] = 3; // claim + slotDataIndex[slot] = claims.size() - 1; + slot++; + } + + // 4. Active listings (blue) - items you're selling + int listingIdx = 0; for (AuctionListing listing : activeListings) { if (slot >= SLOT_COUNT) break; if (listing.item != null) displayInventory.setInventorySlotContents(slot, listing.item.copy()); + slotTypes[slot] = 1; // selling + slotDataIndex[slot] = listingIdx; slot++; + listingIdx++; } + + // 5. Active bids (blue) - items you're bidding on + int bidIdx = 0; for (AuctionListing bid : activeBids) { if (slot >= SLOT_COUNT) break; if (bid.item != null) displayInventory.setInventorySlotContents(slot, bid.item.copy()); + slotTypes[slot] = 2; // bidding + slotDataIndex[slot] = bidIdx; slot++; + bidIdx++; } - for (AuctionClaim claim : claims) { + + // 6. Expired claims (red) - returned items (lowest priority) + for (AuctionClaim claim : expiredClaims) { if (slot >= SLOT_COUNT) break; + claims.add(claim); displayInventory.setClaimItem(slot, claim); + slotTypes[slot] = 3; // claim + slotDataIndex[slot] = claims.size() - 1; slot++; } } @@ -80,34 +212,39 @@ public class ContainerAuctionTrades extends ContainerAuction { /** Get listing at slot (or null if claim/empty) */ public AuctionListing getListingAt(int slot) { - if (slot >= 0 && slot < activeListings.size()) { - return activeListings.get(slot); - } - int bidIdx = slot - activeListings.size(); - if (bidIdx >= 0 && bidIdx < activeBids.size()) { - return activeBids.get(bidIdx); + if (slot < 0 || slot >= SLOT_COUNT) return null; + int type = slotTypes[slot]; + int idx = slotDataIndex[slot]; + if (idx < 0) return null; + + if (type == 1 && idx < activeListings.size()) { + return activeListings.get(idx); + } else if (type == 2 && idx < activeBids.size()) { + return activeBids.get(idx); } return null; } /** Get claim at slot (or null if listing/empty) */ public AuctionClaim getClaimAt(int slot) { - int claimIdx = slot - activeListings.size() - activeBids.size(); - if (claimIdx >= 0 && claimIdx < claims.size()) { - return claims.get(claimIdx); + if (slot < 0 || slot >= SLOT_COUNT) return null; + if (slotTypes[slot] == 3) { + int idx = slotDataIndex[slot]; + if (idx >= 0 && idx < claims.size()) { + return claims.get(idx); + } } return null; } /** Check if slot is player's active listing */ public boolean isSellingAt(int slot) { - return slot >= 0 && slot < activeListings.size(); + return slot >= 0 && slot < SLOT_COUNT && slotTypes[slot] == 1; } /** Check if slot is player's active bid */ public boolean isBiddingAt(int slot) { - int bidIdx = slot - activeListings.size(); - return bidIdx >= 0 && bidIdx < activeBids.size(); + return slot >= 0 && slot < SLOT_COUNT && slotTypes[slot] == 2; } /** Find listing matching item */ @@ -159,24 +296,29 @@ public class ContainerAuctionTrades extends ContainerAuction { hiddenSlot = -1; } - /** Restore item at slot from data */ + /** Restore item at slot from data using slot tracking */ private void restoreSlotItem(int slot) { - if (slot < activeListings.size()) { - AuctionListing listing = activeListings.get(slot); + if (slot < 0 || slot >= SLOT_COUNT) return; + + int type = slotTypes[slot]; + int idx = slotDataIndex[slot]; + if (idx < 0) return; + + if (type == 1 && idx < activeListings.size()) { + // Selling + AuctionListing listing = activeListings.get(idx); if (listing != null && listing.item != null) { displayInventory.setInventorySlotContents(slot, listing.item.copy()); } - } else if (slot < activeListings.size() + activeBids.size()) { - int bidIdx = slot - activeListings.size(); - AuctionListing bid = activeBids.get(bidIdx); + } else if (type == 2 && idx < activeBids.size()) { + // Bidding + AuctionListing bid = activeBids.get(idx); if (bid != null && bid.item != null) { displayInventory.setInventorySlotContents(slot, bid.item.copy()); } - } else { - int claimIdx = slot - activeListings.size() - activeBids.size(); - if (claimIdx < claims.size()) { - displayInventory.setClaimItem(slot, claims.get(claimIdx)); - } + } else if (type == 3 && idx < claims.size()) { + // Claim + displayInventory.setClaimItem(slot, claims.get(idx)); } } diff --git a/src/main/java/noppes/npcs/containers/InventoryAuctionDisplay.java b/src/main/java/noppes/npcs/containers/InventoryAuctionDisplay.java index 20c03f18f..0a15720fe 100644 --- a/src/main/java/noppes/npcs/containers/InventoryAuctionDisplay.java +++ b/src/main/java/noppes/npcs/containers/InventoryAuctionDisplay.java @@ -3,7 +3,6 @@ package noppes.npcs.containers; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; -import noppes.npcs.CustomItems; import noppes.npcs.constants.EnumClaimType; import noppes.npcs.controllers.data.AuctionClaim; import noppes.npcs.controllers.data.AuctionListing; @@ -49,19 +48,21 @@ public class InventoryAuctionDisplay implements IInventory { /** * Set a claim item in the inventory. - * For ITEM claims, shows the item. - * For CURRENCY/REFUND claims, shows a gold coin as visual representation. + * Shows actual item for ITEM claims, empty slot for CURRENCY/REFUND (icon drawn by GUI). */ public void setClaimItem(int slot, AuctionClaim claim) { if (slot >= 0 && slot < size) { claims.set(slot, claim); listings.set(slot, null); if (claim != null) { - if (claim.type == EnumClaimType.ITEM && claim.item != null) { - items[slot] = claim.item; + if (claim.type == EnumClaimType.CURRENCY || claim.type == EnumClaimType.REFUND) { + // No item - gold coin icon drawn by GUI + items[slot] = null; + } else if (claim.item != null) { + // Show actual item for ITEM claims + items[slot] = claim.item.copy(); } else { - // For currency claims, show a gold coin - items[slot] = new ItemStack(CustomItems.coinGold); + items[slot] = null; } } else { items[slot] = null; diff --git a/src/main/java/noppes/npcs/controllers/AuctionController.java b/src/main/java/noppes/npcs/controllers/AuctionController.java index 92f790b99..b78fa0649 100644 --- a/src/main/java/noppes/npcs/controllers/AuctionController.java +++ b/src/main/java/noppes/npcs/controllers/AuctionController.java @@ -164,9 +164,12 @@ public class AuctionController { removeFromPlayerBids(listing.highBidderUUID, listing.id); } + // Get item display name safely + String itemDisplayName = listing.item != null ? listing.item.getDisplayName() : "Unknown Item"; + if (listing.hasBids()) { // Auction sold - create claims - AuctionClaim itemClaim = AuctionClaim.createItemClaim( + AuctionClaim itemClaim = AuctionClaim.createItemWonClaim( listing.highBidderUUID, listing.highBidderName, listing.id, listing.item); addClaim(itemClaim); @@ -175,27 +178,28 @@ public class AuctionController { long sellerReceives = saleAmount - tax; AuctionClaim currencyClaim = AuctionClaim.createCurrencyClaim( - listing.sellerUUID, listing.sellerName, listing.id, sellerReceives); + listing.sellerUUID, listing.sellerName, listing.id, sellerReceives, + itemDisplayName, listing.highBidderName); addClaim(currencyClaim); // Notifications queueNotification(listing.highBidderUUID, EnumNotificationType.AUCTION_WON, listing.id, - "You won the auction for " + listing.item.getDisplayName() + "!"); + "You won the auction for " + itemDisplayName + "!"); queueNotification(listing.sellerUUID, EnumNotificationType.AUCTION_SOLD, listing.id, - "Your " + listing.item.getDisplayName() + " sold for " + saleAmount + " " + ConfigMarket.CurrencyName + "!"); + "Your " + itemDisplayName + " sold for " + saleAmount + " " + ConfigMarket.CurrencyName + "!"); - logAuction("SOLD", listing.sellerName, listing.item.getDisplayName(), saleAmount, + logAuction("SOLD", listing.sellerName, itemDisplayName, saleAmount, "Winner: " + listing.highBidderName + ", Tax: " + tax); } else { // No bids - return item to seller - AuctionClaim returnClaim = AuctionClaim.createItemClaim( + AuctionClaim returnClaim = AuctionClaim.createItemReturnedClaim( listing.sellerUUID, listing.sellerName, listing.id, listing.item); addClaim(returnClaim); queueNotification(listing.sellerUUID, EnumNotificationType.AUCTION_EXPIRED, listing.id, - "Your auction for " + listing.item.getDisplayName() + " expired with no bids."); + "Your auction for " + itemDisplayName + " expired with no bids."); - logAuction("EXPIRED", listing.sellerName, listing.item.getDisplayName(), listing.startingPrice, "No bids"); + logAuction("EXPIRED", listing.sellerName, itemDisplayName, listing.startingPrice, "No bids"); } } @@ -361,7 +365,8 @@ public class AuctionController { // Refund bidder AuctionClaim refundClaim = AuctionClaim.createRefundClaim( - listing.highBidderUUID, listing.highBidderName, listing.id, listing.currentBid); + listing.highBidderUUID, listing.highBidderName, listing.id, listing.currentBid, + listing.item.getDisplayName(), listing.sellerName); addClaim(refundClaim); // Remove from bidder's active bids @@ -371,7 +376,7 @@ public class AuctionController { "The auction for " + listing.item.getDisplayName() + " was cancelled. Your bid has been refunded."); // Return item to seller - AuctionClaim itemClaim = AuctionClaim.createItemClaim( + AuctionClaim itemClaim = AuctionClaim.createItemReturnedClaim( listing.sellerUUID, listing.sellerName, listing.id, listing.item); addClaim(itemClaim); @@ -380,7 +385,7 @@ public class AuctionController { (isAdmin ? ", Cancelled by admin: " + player.getCommandSenderName() : "")); } else { // No bids - just return item - AuctionClaim itemClaim = AuctionClaim.createItemClaim( + AuctionClaim itemClaim = AuctionClaim.createItemReturnedClaim( listing.sellerUUID, listing.sellerName, listing.id, listing.item); addClaim(itemClaim); @@ -462,7 +467,8 @@ public class AuctionController { // Refund previous bidder if any if (listing.hasBids() && previousBidder != null) { AuctionClaim refundClaim = AuctionClaim.createRefundClaim( - previousBidder, listing.highBidderName, listing.id, previousBid); + previousBidder, listing.highBidderName, listing.id, previousBid, + listing.item.getDisplayName(), playerName); addClaim(refundClaim); // Remove from previous bidder's active bids @@ -547,7 +553,8 @@ public class AuctionController { // Refund previous bidder if any if (listing.hasBids() && listing.highBidderUUID != null) { AuctionClaim refundClaim = AuctionClaim.createRefundClaim( - listing.highBidderUUID, listing.highBidderName, listing.id, listing.currentBid); + listing.highBidderUUID, listing.highBidderName, listing.id, listing.currentBid, + listing.item.getDisplayName(), playerName); addClaim(refundClaim); // Remove from previous bidder's active bids @@ -558,7 +565,7 @@ public class AuctionController { } // Create claims - AuctionClaim itemClaim = AuctionClaim.createItemClaim(playerUUID, playerName, listing.id, listing.item); + AuctionClaim itemClaim = AuctionClaim.createItemWonClaim(playerUUID, playerName, listing.id, listing.item); addClaim(itemClaim); long saleAmount = listing.buyoutPrice; @@ -566,7 +573,8 @@ public class AuctionController { long sellerReceives = saleAmount - tax; AuctionClaim currencyClaim = AuctionClaim.createCurrencyClaim( - listing.sellerUUID, listing.sellerName, listing.id, sellerReceives); + listing.sellerUUID, listing.sellerName, listing.id, sellerReceives, + listing.item.getDisplayName(), playerName); addClaim(currencyClaim); // Notifications diff --git a/src/main/java/noppes/npcs/controllers/data/AuctionClaim.java b/src/main/java/noppes/npcs/controllers/data/AuctionClaim.java index 7a4e45a5d..bbc1b06ac 100644 --- a/src/main/java/noppes/npcs/controllers/data/AuctionClaim.java +++ b/src/main/java/noppes/npcs/controllers/data/AuctionClaim.java @@ -12,18 +12,25 @@ public class AuctionClaim { public String playerName; public String listingId; public EnumClaimType type; - public ItemStack item; + public ItemStack item; // The item (for ITEM claims only) + public String itemName; // Display name of item (for CURRENCY/REFUND tooltip) + public String otherPlayerName; // Buyer name (CURRENCY) or who outbid (REFUND) public long currency; public long createdTime; public boolean claimed; + public boolean isReturned; // For ITEM claims: true if returned to seller, false if won by buyer public AuctionClaim() { this.id = UUID.randomUUID().toString(); this.createdTime = System.currentTimeMillis(); this.claimed = false; + this.isReturned = false; + this.itemName = ""; + this.otherPlayerName = ""; } - public static AuctionClaim createItemClaim(UUID playerUUID, String playerName, String listingId, ItemStack item) { + /** Create claim for winning an item (buyer won auction or buyout) */ + public static AuctionClaim createItemWonClaim(UUID playerUUID, String playerName, String listingId, ItemStack item) { AuctionClaim claim = new AuctionClaim(); claim.playerUUID = playerUUID; claim.playerName = playerName; @@ -31,28 +38,50 @@ public class AuctionClaim { claim.type = EnumClaimType.ITEM; claim.item = item != null ? item.copy() : null; claim.currency = 0; + claim.isReturned = false; return claim; } - public static AuctionClaim createCurrencyClaim(UUID playerUUID, String playerName, String listingId, long currency) { + /** Create claim for returned item (seller's expired/cancelled listing) */ + public static AuctionClaim createItemReturnedClaim(UUID playerUUID, String playerName, String listingId, ItemStack item) { + AuctionClaim claim = new AuctionClaim(); + claim.playerUUID = playerUUID; + claim.playerName = playerName; + claim.listingId = listingId; + claim.type = EnumClaimType.ITEM; + claim.item = item != null ? item.copy() : null; + claim.currency = 0; + claim.isReturned = true; + return claim; + } + + /** Create claim for seller's proceeds (stores item name for tooltip, not full item) */ + public static AuctionClaim createCurrencyClaim(UUID playerUUID, String playerName, String listingId, long currency, String itemName, String buyerName) { AuctionClaim claim = new AuctionClaim(); claim.playerUUID = playerUUID; claim.playerName = playerName; claim.listingId = listingId; claim.type = EnumClaimType.CURRENCY; - claim.item = null; + claim.item = null; // No item stored for currency claims + claim.itemName = itemName != null ? itemName : ""; + claim.otherPlayerName = buyerName != null ? buyerName : ""; claim.currency = currency; + claim.isReturned = false; return claim; } - public static AuctionClaim createRefundClaim(UUID playerUUID, String playerName, String listingId, long currency) { + /** Create claim for outbid refund (stores item name for tooltip, not full item) */ + public static AuctionClaim createRefundClaim(UUID playerUUID, String playerName, String listingId, long currency, String itemName, String outbidderName) { AuctionClaim claim = new AuctionClaim(); claim.playerUUID = playerUUID; claim.playerName = playerName; claim.listingId = listingId; claim.type = EnumClaimType.REFUND; - claim.item = null; + claim.item = null; // No item stored for refund claims + claim.itemName = itemName != null ? itemName : ""; + claim.otherPlayerName = outbidderName != null ? outbidderName : ""; claim.currency = currency; + claim.isReturned = false; return claim; } @@ -84,9 +113,12 @@ public class AuctionClaim { compound.setTag("Item", itemTag); } + compound.setString("ItemName", itemName != null ? itemName : ""); + compound.setString("OtherPlayerName", otherPlayerName != null ? otherPlayerName : ""); compound.setLong("Currency", currency); compound.setLong("CreatedTime", createdTime); compound.setBoolean("Claimed", claimed); + compound.setBoolean("IsReturned", isReturned); return compound; } @@ -107,9 +139,12 @@ public class AuctionClaim { item = null; } + itemName = compound.getString("ItemName"); + otherPlayerName = compound.getString("OtherPlayerName"); currency = compound.getLong("Currency"); createdTime = compound.getLong("CreatedTime"); claimed = compound.getBoolean("Claimed"); + isReturned = compound.getBoolean("IsReturned"); } public static AuctionClaim fromNBT(NBTTagCompound compound) { diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index 8c168168c..a77d6444b 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -1820,6 +1820,8 @@ auction.trades.confirmClaim=Claim Item? auction.trades.rightClickConfirm=Right-click again to confirm auction.trades.leftClickConfirm=Left-click again to confirm auction.trades.otherClickCancel=Any other click to cancel +auction.trades.buyer=Buyer +auction.trades.outbidBy=Outbid by # Sell Page - Buyout Toggle auction.sell.allowBuyout=Allow Buy Now