285 lines
7.0 KiB
Java
285 lines
7.0 KiB
Java
package com.example.essentialsplus.loot.ui;
|
|
|
|
import com.example.essentialsplus.loot.LootRecord;
|
|
import com.example.essentialsplus.loot.droptable.DropTable;
|
|
import com.example.essentialsplus.ui.Ui;
|
|
import com.example.essentialsplus.wiki.WikiApi;
|
|
import java.awt.BorderLayout;
|
|
import java.awt.Dimension;
|
|
import java.util.Map;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Function;
|
|
import javax.swing.BorderFactory;
|
|
import javax.swing.JComboBox;
|
|
import javax.swing.JLabel;
|
|
import javax.swing.JPanel;
|
|
import javax.swing.SwingUtilities;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import net.runelite.client.game.ItemManager;
|
|
import net.runelite.http.api.item.ItemPrice;
|
|
|
|
/** Drops of the current target: the player's recorded loot (opens the loot view) and the wiki drop table. */
|
|
@Slf4j
|
|
class DropsSection extends JPanel
|
|
{
|
|
private static final Map<String, DropTable> TABLES = new ConcurrentHashMap<>();
|
|
/** item name (lower case) -> item id, -1 if not found */
|
|
private static final Map<String, Integer> IDS = new ConcurrentHashMap<>();
|
|
|
|
private final WikiApi api;
|
|
private final ExecutorService executor;
|
|
private final ItemManager itemManager;
|
|
private final Function<String, LootRecord> records;
|
|
private final Consumer<LootRecord> openLoot;
|
|
private final Consumer<String> openWiki;
|
|
|
|
private String name;
|
|
private long recordStamp = -1;
|
|
private int variant;
|
|
private boolean loading;
|
|
private String error;
|
|
|
|
DropsSection(WikiApi api, ExecutorService executor, ItemManager itemManager, Function<String, LootRecord> records,
|
|
Consumer<LootRecord> openLoot, Consumer<String> openWiki)
|
|
{
|
|
this.openWiki = openWiki;
|
|
this.api = api;
|
|
this.executor = executor;
|
|
this.itemManager = itemManager;
|
|
this.records = records;
|
|
this.openLoot = openLoot;
|
|
setLayout(new VerticalLayout(3));
|
|
setOpaque(false);
|
|
render();
|
|
}
|
|
|
|
/** Target name; rebuilds only when it or the recorded loot changes. */
|
|
void setTarget(String target)
|
|
{
|
|
LootRecord r = target == null ? null : records.apply(target);
|
|
long stamp = r == null ? 0 : r.getLastSeen() + r.getKills();
|
|
if (java.util.Objects.equals(target, name) && stamp == recordStamp)
|
|
{
|
|
return;
|
|
}
|
|
if (!java.util.Objects.equals(target, name))
|
|
{
|
|
variant = 0;
|
|
error = null;
|
|
}
|
|
name = target;
|
|
recordStamp = stamp;
|
|
render();
|
|
}
|
|
|
|
private void render()
|
|
{
|
|
removeAll();
|
|
if (name == null)
|
|
{
|
|
add(note("Attack a monster to see what it drops."));
|
|
refreshLayout();
|
|
return;
|
|
}
|
|
|
|
add(LiveFightCard.sectionLabel("YOUR LOOT"));
|
|
LootRecord record = records.apply(name);
|
|
if (record != null && record.getKills() + record.getItems().size() > 0)
|
|
{
|
|
add(new LootTargetRow(record, record.getTotalValue(), itemManager, openLoot));
|
|
}
|
|
else
|
|
{
|
|
add(note("No kills recorded yet."));
|
|
}
|
|
|
|
DropTable table = TABLES.get(name);
|
|
add(LiveFightCard.sectionLabel("DROP TABLE (WIKI)"));
|
|
if (table == null)
|
|
{
|
|
if (error != null)
|
|
{
|
|
add(note("Could not load drop table: " + error));
|
|
}
|
|
else
|
|
{
|
|
add(note("Loading..."));
|
|
load(name);
|
|
}
|
|
}
|
|
else if (table.isEmpty())
|
|
{
|
|
add(note("The wiki has no drop table for " + name + "."));
|
|
}
|
|
else
|
|
{
|
|
if (table.variants.size() > 1)
|
|
{
|
|
JComboBox<DropTable.Variant> box = new JComboBox<>(table.variants.toArray(new DropTable.Variant[0]));
|
|
box.setSelectedIndex(Math.min(variant, table.variants.size() - 1));
|
|
box.setFocusable(false);
|
|
box.addActionListener(e ->
|
|
{
|
|
variant = box.getSelectedIndex();
|
|
render();
|
|
});
|
|
add(box);
|
|
}
|
|
DropTable.Variant v = table.variants.get(Math.min(variant, table.variants.size() - 1));
|
|
for (DropTable.Rate rate : DropTable.rates(v).values())
|
|
{
|
|
add(row(rate));
|
|
}
|
|
}
|
|
refreshLayout();
|
|
}
|
|
|
|
private void refreshLayout()
|
|
{
|
|
revalidate();
|
|
repaint();
|
|
}
|
|
|
|
private JPanel row(DropTable.Rate rate)
|
|
{
|
|
JPanel row = new JPanel(new BorderLayout(6, 0))
|
|
{
|
|
@Override
|
|
protected void paintComponent(java.awt.Graphics g0)
|
|
{
|
|
java.awt.Graphics2D g = Ui.prepare(g0);
|
|
g.setColor(Ui.CARD);
|
|
g.fillRoundRect(0, 0, getWidth(), getHeight(), 6, 6);
|
|
g.dispose();
|
|
}
|
|
};
|
|
row.setOpaque(false);
|
|
row.setBorder(BorderFactory.createEmptyBorder(1, 3, 1, 7));
|
|
row.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR));
|
|
row.setToolTipText(rate.item + " · click to open the wiki");
|
|
row.addMouseListener(new java.awt.event.MouseAdapter()
|
|
{
|
|
@Override
|
|
public void mousePressed(java.awt.event.MouseEvent e)
|
|
{
|
|
if (SwingUtilities.isLeftMouseButton(e))
|
|
{
|
|
openWiki.accept(rate.item);
|
|
}
|
|
}
|
|
});
|
|
|
|
JLabel icon = new JLabel();
|
|
icon.setPreferredSize(new Dimension(32, 28));
|
|
icon.setHorizontalAlignment(JLabel.CENTER);
|
|
Integer id = IDS.get(DropTable.key(rate.item));
|
|
if (id != null && id > 0)
|
|
{
|
|
itemManager.getImage(id).addTo(icon);
|
|
}
|
|
row.add(icon, BorderLayout.WEST);
|
|
|
|
JLabel label = new JLabel(rate.item);
|
|
label.setFont(net.runelite.client.ui.FontManager.getRunescapeSmallFont());
|
|
label.setForeground(Ui.TEXT);
|
|
row.add(label, BorderLayout.CENTER);
|
|
|
|
JLabel chance = new JLabel(rate.text);
|
|
chance.setFont(net.runelite.client.ui.FontManager.getRunescapeSmallFont());
|
|
chance.setForeground(rarityColor(rate.perKill));
|
|
row.add(chance, BorderLayout.EAST);
|
|
return row;
|
|
}
|
|
|
|
private static java.awt.Color rarityColor(double perKill)
|
|
{
|
|
if (Double.isNaN(perKill))
|
|
{
|
|
return Ui.MUTED;
|
|
}
|
|
if (perKill >= 1)
|
|
{
|
|
return Ui.TEXT;
|
|
}
|
|
if (perKill >= 1 / 100.0)
|
|
{
|
|
return Ui.UP;
|
|
}
|
|
if (perKill >= 1 / 1000.0)
|
|
{
|
|
return Ui.GOLD;
|
|
}
|
|
return Ui.DOWN;
|
|
}
|
|
|
|
private void load(String target)
|
|
{
|
|
if (loading || api == null || executor == null)
|
|
{
|
|
return;
|
|
}
|
|
loading = true;
|
|
executor.execute(() ->
|
|
{
|
|
String err = null;
|
|
try
|
|
{
|
|
DropTable table = api.fetchDropTable(target);
|
|
// icons: item ids by name (tradeable items only)
|
|
for (DropTable.Variant v : table.variants)
|
|
{
|
|
for (DropTable.Rate r : DropTable.rates(v).values())
|
|
{
|
|
IDS.computeIfAbsent(DropTable.key(r.item), k -> findId(r.item));
|
|
}
|
|
}
|
|
TABLES.put(target, table);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
log.debug("drop table for {} failed", target, e);
|
|
err = e.getMessage() == null ? "wiki not reachable" : e.getMessage();
|
|
}
|
|
String failure = err;
|
|
SwingUtilities.invokeLater(() ->
|
|
{
|
|
loading = false;
|
|
if (target.equals(name))
|
|
{
|
|
error = failure;
|
|
render();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
private int findId(String item)
|
|
{
|
|
try
|
|
{
|
|
for (ItemPrice p : itemManager.search(item))
|
|
{
|
|
if (p.getName().equalsIgnoreCase(item))
|
|
{
|
|
return p.getId();
|
|
}
|
|
}
|
|
}
|
|
catch (RuntimeException e)
|
|
{
|
|
log.debug("item search for {} failed", item, e);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private static JLabel note(String text)
|
|
{
|
|
JLabel l = new JLabel("<html><body style='width:180px'>" + text.replace("&", "&").replace("<", "<") + "</html>");
|
|
l.setFont(net.runelite.client.ui.FontManager.getRunescapeSmallFont());
|
|
l.setForeground(Ui.FAINT);
|
|
return l;
|
|
}
|
|
}
|