Developer docs
Stats panel
The three classes
All three are in net.runelite.client.plugins.projectx.util.overlay.
| Class | What it does |
|---|---|
ScriptPanelOverlay | Base class for your overlay. Handles position (top left by default), layering and drawing. You implement build(). |
ScriptPanel | The panel itself, built with chained calls. Made afresh every frame; it is just a list of lines. |
SkillTracker | XP gained, XP an hour, levels gained and time to level, for every skill, since the run started. |
A complete overlay
public class MyCoolOverlay extends ScriptPanelOverlay {
private final MyCoolPlugin plugin;
private final Client client;
private final SkillTracker skills = new SkillTracker(); // one per overlay
@Inject
MyCoolOverlay(MyCoolPlugin plugin, Client client) {
super(plugin);
this.plugin = plugin;
this.client = client;
}
@Override
protected ScriptPanel build() {
MyCoolScript script = plugin.getScript();
long startedAt = script.getStartedAt(); // stored once, when the run started
skills.observe(client, startedAt);
return ScriptPanel.create("My Cool Script", MyCoolPlugin.version)
.theme(ScriptPanel.Theme.GREEN)
.startedAt(startedAt)
.status(script.getStatus())
.section("EXPERIENCE")
.skill(skills, Skill.WOODCUTTING)
.section("SESSION")
.row("Logs", ScriptPanel.withRate(script.getLogs(), skills.elapsedMs()))
.bar("Inventory", Inventory.fullSlotCount() / 28.0, Inventory.fullSlotCount() + "/28");
}
}Add it in startUp() with overlayManager.add(overlay) and remove it in shutDown(). If build() throws, the overlay draws a bare panel with the error as its status line instead of disappearing, and logs the error at most once a minute.
build() runs on the client thread, every frame. Read fields and return. Never wait, click, walk or make a network call from it.ScriptPanel reference
| Call | Shows |
|---|---|
ScriptPanel.create(name, version) | A panel titled “name V version”. |
.theme(ScriptPanel.Theme.X) | ORANGE, PURPLE, GREEN, BLUE, RED or GOLD. Same layout, different accent. |
.startedAt(epochMillis) | The run clock in the title bar. 0 shows 00:00:00. |
.status(text) | One line under the title. Null or blank shows “Idle”. |
.icon(image) | A small image in the title bar, such as the item being made. |
.section(title) | A heading in the accent colour. |
.row(label, value) | A label and value. A null value shows a muted dash. |
.row(label, value, color) | The same, coloured: ScriptPanel.WARN, GOOD or BAD. |
.bar(label, fraction, text) | A progress bar, fraction from 0 to 1, with text beside it. |
.skill(tracker, skill) | Two rows: XP gained and XP/hr, then levels gained and time to level. |
.trainedSkills(tracker, limit) | One row per skill trained this run, most XP first. For combat and other mixed scripts. |
.width(px), .labelWidth(px) | Panel width, and where values start. Widen the label column for long labels. |
Static helpers for your own rows:
| Helper | Example |
|---|---|
ScriptPanel.withRate(count, elapsedMs) | 1,204 (310/h) |
ScriptPanel.perHour(count, elapsedMs) | 310 |
ScriptPanel.compact(n) | 950, 54.5k, 1.2M |
ScriptPanel.clock(ms) | 24:04:09 |
ScriptPanel.duration(ms) | 6h 4m, or 4m 12s under an hour |
SkillTracker
Call observe(client, startedAt) at the top of every build(), then read gained(skill), perHour(skill), level(skill), levelsGained(skill), msToLevel(skill) (-1 when not gaining or at 99), trained() and elapsedMs().
startedAt you pass changes. Store System.currentTimeMillis() once when the script starts and pass that field every frame. Passing something worked out each frame, such as now - runtime, resets every number on every frame.With buttons
An overlay that also carries its own components, such as a Pause button, needs an OverlayPanel parent. Use ScriptPanelComponent: call setPanel(panel) each frame, add it before the other children, and give the parent no background or border so only the panel's frame is drawn. Keep scene drawing (highlighting tiles or NPCs) in a separate overlay.