Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ private void transferItems(Location hopperLoc, Location spawnerLoc) {
}

if (!itemsToRemove.isEmpty()) {
virtualInv.removeItems(itemsToRemove);
spawner.removeItemsAndUpdateSellValue(itemsToRemove);
}

if (inventoryChanged) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,10 @@ public ItemStack createSpawnerInfoItem(Player player, SpawnerData spawner) {
placeholders.put("raw_max_exp", String.valueOf(maxExp));
placeholders.put("formatted_exp", formattedPercentExp);

// Total sell price information
double totalSellPrice = spawner.getAccumulatedSellValue();
placeholders.put("total_sell_price", languageManager.formatNumber(totalSellPrice));

// Set display name with the specified placeholders
spawnerMeta.setDisplayName(languageManager.getGuiItemName("spawner_info_item.name", placeholders));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ private void handleItemDrop(Player player, SpawnerData spawner, Inventory invent
VirtualInventory virtualInv = spawner.getVirtualInventory();
List<ItemStack> itemsToRemove = new ArrayList<>();
itemsToRemove.add(droppedItem);
virtualInv.removeItems(itemsToRemove);
spawner.removeItemsAndUpdateSellValue(itemsToRemove);

int remaining = item.getAmount() - amountToDrop;
if (remaining <= 0) {
Expand Down Expand Up @@ -363,7 +363,7 @@ private void handleDropPageItems(Player player, SpawnerData spawner, Inventory i
}

VirtualInventory virtualInv = spawner.getVirtualInventory();
virtualInv.removeItems(pageItems);
spawner.removeItemsAndUpdateSellValue(pageItems);

dropItemsInDirection(player, pageItems);

Expand Down Expand Up @@ -445,7 +445,7 @@ private void takeSingleItem(Player player, Inventory sourceInv, int slot, ItemSt
);
if (result.getAmountMoved() > 0) {
updateInventorySlot(sourceInv, slot, item, result.getAmountMoved());
virtualInv.removeItems(result.getMovedItems());
spawner.removeItemsAndUpdateSellValue(result.getMovedItems());
player.updateInventory();

spawner.updateHologramData();
Expand Down Expand Up @@ -767,8 +767,8 @@ else if (targetItem.isSimilar(itemToMove)) {
}

if (!itemsToRemove.isEmpty()) {
virtualInv.removeItems(itemsToRemove);
StoragePageHolder holder = (StoragePageHolder) sourceInventory.getHolder(false);
holder.getSpawnerData().removeItemsAndUpdateSellValue(itemsToRemove);
holder.getSpawnerData().updateHologramData();
holder.updateOldUsedSlots();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,8 @@ private void initializeStaticButtons() {
));
}

// Create sell button
GuiButton sellButton = layout.getButton("sell_all");
if (sellButton != null) {
staticButtons.put("sell", createButton(
sellButton.getMaterial(),
languageManager.getGuiItemName("sell_button.name"),
languageManager.getGuiItemLoreAsList("sell_button.lore")
));
}
// Note: Sell button is created dynamically in updateDynamicButtons()
// to show the current total sell price
}

public Inventory createInventory(SpawnerData spawner, String title, int page, int totalPages) {
Expand Down Expand Up @@ -293,7 +286,7 @@ private void addNavigationButtons(Map<Integer, ItemStack> updates, SpawnerData s
// Add sell button if shop integration is available and button is enabled
if (layout.hasButton("sell_all")) {
GuiButton sellButton = layout.getButton("sell_all");
ItemStack sellIndicator = createSellButton(sellButton.getMaterial());
ItemStack sellIndicator = createSellButton(spawner, sellButton.getMaterial());
updates.put(sellButton.getSlot(), sellIndicator);
}
}
Expand Down Expand Up @@ -335,8 +328,13 @@ private ItemStack createNavigationButton(String type, int targetPage, Material m
return createButton(material, buttonName, Arrays.asList(buttonLore));
}

private ItemStack createSellButton(Material material) {
String name = languageManager.getGuiItemName("sell_button.name");
private ItemStack createSellButton(SpawnerData spawner, Material material) {
// Create placeholders for total sell price
Map<String, String> placeholders = new HashMap<>();
double totalSellPrice = spawner.getAccumulatedSellValue();
placeholders.put("total_sell_price", languageManager.formatNumber(totalSellPrice));

String name = languageManager.getGuiItemName("sell_button.name", placeholders);
List<String> lore = languageManager.getGuiItemLoreAsList("sell_button.lore");
return createButton(material, name, lore);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public void spawnLootToSpawner(SpawnerData spawner) {
}

if (!itemsToAdd.isEmpty()) {
spawner.getVirtualInventory().addItems(itemsToAdd);
spawner.addItemsAndUpdateSellValue(itemsToAdd);
changed = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ public class SpawnerData {
private SellResult lastSellResult;
@Getter
private boolean lastSellProcessed;

// Accumulated sell value for optimization
@Getter
private volatile double accumulatedSellValue;
private volatile boolean sellValueDirty;

private SpawnerHologram hologram;
@Getter @Setter
Expand Down Expand Up @@ -117,6 +122,8 @@ private void initializeDefaults() {
this.stackSize = 1;
this.lastSpawnTime = System.currentTimeMillis();
this.preferredSortItem = null; // Initialize sort preference as null
this.accumulatedSellValue = 0.0;
this.sellValueDirty = true;
}

public void loadConfigurationValues() {
Expand All @@ -135,6 +142,8 @@ public void recalculateAfterConfigReload() {
if (virtualInventory != null && virtualInventory.getMaxSlots() != maxSpawnerLootSlots) {
recreateVirtualInventory();
}
// Mark sell value as dirty after config reload since prices may have changed
this.sellValueDirty = true;
updateHologramData();

// Invalidate GUI cache after config reload
Expand Down Expand Up @@ -300,6 +309,8 @@ public boolean updateCapacityStatus() {
public void setEntityType(EntityType newType) {
this.entityType = newType;
this.lootConfig = lootRegistry.getLootConfig(newType);
// Mark sell value as dirty since entity type and prices changed
this.sellValueDirty = true;
updateHologramData();
}

Expand Down Expand Up @@ -333,6 +344,8 @@ public int getEntityExperienceValue() {

public void setLootConfig() {
this.lootConfig = lootRegistry.getLootConfig(entityType);
// Mark sell value as dirty since prices may have changed
this.sellValueDirty = true;
}

public void setLastSellResult(SellResult sellResult) {
Expand Down Expand Up @@ -360,4 +373,223 @@ public void updateLastInteractedPlayer(String playerName) {
this.lastInteractedPlayer = playerName;
markInteracted();
}

/**
* Marks the sell value as dirty, requiring recalculation
*/
public void markSellValueDirty() {
this.sellValueDirty = true;
}

/**
* Updates the accumulated sell value for specific items being added
* @param itemsAdded Map of item signatures to quantities added
* @param priceCache Price cache from loot config
*/
public void incrementSellValue(Map<VirtualInventory.ItemSignature, Long> itemsAdded,
Map<String, Double> priceCache) {
if (itemsAdded == null || itemsAdded.isEmpty()) {
return;
}

double addedValue = 0.0;
for (Map.Entry<VirtualInventory.ItemSignature, Long> entry : itemsAdded.entrySet()) {
ItemStack template = entry.getKey().getTemplate();
long amount = entry.getValue();
double itemPrice = findItemPrice(template, priceCache);
if (itemPrice > 0.0) {
addedValue += itemPrice * amount;
}
}

this.accumulatedSellValue += addedValue;
this.sellValueDirty = false;
}

/**
* Decrements the accumulated sell value when items are removed
* @param itemsRemoved List of items removed
* @param priceCache Price cache from loot config
*/
public void decrementSellValue(List<ItemStack> itemsRemoved, Map<String, Double> priceCache) {
if (itemsRemoved == null || itemsRemoved.isEmpty()) {
return;
}

// Consolidate removed items
Map<VirtualInventory.ItemSignature, Long> consolidated = new java.util.HashMap<>();
for (ItemStack item : itemsRemoved) {
if (item == null || item.getAmount() <= 0) continue;
VirtualInventory.ItemSignature sig = new VirtualInventory.ItemSignature(item);
consolidated.merge(sig, (long) item.getAmount(), Long::sum);
}

double removedValue = 0.0;
for (Map.Entry<VirtualInventory.ItemSignature, Long> entry : consolidated.entrySet()) {
ItemStack template = entry.getKey().getTemplate();
long amount = entry.getValue();
double itemPrice = findItemPrice(template, priceCache);
if (itemPrice > 0.0) {
removedValue += itemPrice * amount;
}
}

this.accumulatedSellValue = Math.max(0.0, this.accumulatedSellValue - removedValue);
}

/**
* Forces a full recalculation of the accumulated sell value
* Should be called when the cache is dirty or on spawner load
*/
public void recalculateSellValue() {
if (lootConfig == null) {
this.accumulatedSellValue = 0.0;
this.sellValueDirty = false;
return;
}

// Get price cache
Map<String, Double> priceCache = createPriceCache();

// Calculate from current inventory
Map<VirtualInventory.ItemSignature, Long> items = virtualInventory.getConsolidatedItems();
double totalValue = 0.0;

for (Map.Entry<VirtualInventory.ItemSignature, Long> entry : items.entrySet()) {
ItemStack template = entry.getKey().getTemplate();
long amount = entry.getValue();
double itemPrice = findItemPrice(template, priceCache);
if (itemPrice > 0.0) {
totalValue += itemPrice * amount;
}
}

this.accumulatedSellValue = totalValue;
this.sellValueDirty = false;
}

/**
* Gets the price cache from loot config
*/
public Map<String, Double> createPriceCache() {
if (lootConfig == null) {
return new java.util.HashMap<>();
}

Map<String, Double> cache = new java.util.HashMap<>();
java.util.List<LootItem> allLootItems = lootConfig.getAllItems();

for (LootItem lootItem : allLootItems) {
if (lootItem.getSellPrice() > 0.0) {
ItemStack template = lootItem.createItemStack(new java.util.Random());
if (template != null) {
String key = createItemKey(template);
cache.put(key, lootItem.getSellPrice());
}
}
}

return cache;
}

/**
* Finds item price using the cache
*/
private double findItemPrice(ItemStack item, Map<String, Double> priceCache) {
if (item == null || priceCache == null) {
return 0.0;
}
String itemKey = createItemKey(item);
Double price = priceCache.get(itemKey);
return price != null ? price : 0.0;
}

/**
* Creates a unique key for an item (same logic as SpawnerSellManager)
*/
private String createItemKey(ItemStack item) {
if (item == null) {
return "null";
}

StringBuilder key = new StringBuilder();
key.append(item.getType().name());

// Add enchantments if present
if (item.hasItemMeta() && item.getItemMeta().hasEnchants()) {
key.append("_enchants:");
item.getItemMeta().getEnchants().entrySet().stream()
.sorted(java.util.Map.Entry.comparingByKey(java.util.Comparator.comparing(enchantment -> enchantment.getKey().toString())))
.forEach(entry -> key.append(entry.getKey().getKey()).append(":").append(entry.getValue()).append(","));
}

// Add custom model data if present
if (item.hasItemMeta() && item.getItemMeta().hasCustomModelData()) {
key.append("_cmd:").append(item.getItemMeta().getCustomModelData());
}

// Add display name if present
if (item.hasItemMeta() && item.getItemMeta().hasDisplayName()) {
key.append("_name:").append(item.getItemMeta().getDisplayName());
}

return key.toString();
}

/**
* Checks if sell value needs recalculation
*/
public boolean isSellValueDirty() {
return sellValueDirty;
}

/**
* Adds items to virtual inventory and updates accumulated sell value
* This is the preferred method to add items to maintain accurate sell value cache
* @param items Items to add
*/
public void addItemsAndUpdateSellValue(List<ItemStack> items) {
if (items == null || items.isEmpty()) {
return;
}

// Consolidate items being added for efficient price lookup
Map<VirtualInventory.ItemSignature, Long> itemsToAdd = new java.util.HashMap<>();
for (ItemStack item : items) {
if (item == null || item.getAmount() <= 0) continue;
VirtualInventory.ItemSignature sig = new VirtualInventory.ItemSignature(item);
itemsToAdd.merge(sig, (long) item.getAmount(), Long::sum);
}

// Add to inventory
virtualInventory.addItems(items);

// Update sell value
if (!sellValueDirty) {
Map<String, Double> priceCache = createPriceCache();
incrementSellValue(itemsToAdd, priceCache);
}
}

/**
* Removes items from virtual inventory and updates accumulated sell value
* @param items Items to remove
* @return true if items were removed successfully
*/
public boolean removeItemsAndUpdateSellValue(List<ItemStack> items) {
if (items == null || items.isEmpty()) {
return true;
}

// Remove from inventory
boolean removed = virtualInventory.removeItems(items);

// Update sell value if removal was successful
if (removed && !sellValueDirty) {
Map<String, Double> priceCache = createPriceCache();
decrementSellValue(items, priceCache);
}

return removed;
}
}
Loading