Files
Runelite-EssentialsPlus/item-charges.md
T
2026-09-18 17:18:57 +02:00

136 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Item charges – Taskbeschreibung
Implementiere einen **„Item charges"**-Helper in **Essentials Plus** (`com.example.essentialsplus.helpers`): Auf dem **Icon jedes Items** mit bekanntem Charge/Füllstand wird eine **Zahl** gerendert. Manche Items zeigen **mehrere Zahlen** (z. B. Toxic Blowpipe: Scales + Darts). Togglebar per Checkbox, anzeige in Inventar, Ausrüstung und (optional) Bank.
**Beispiele:**
- Toxic Blowpipe (mit Scales) → `scales`-Zahl; mit geladenen Darts → zweiter Wert (Darts-Marker, s. Hinweise).
- Dizan's Quiver → zwei Zahlen: Ammo (`CHARGES_DIZANAS_QUIVER_QUANTITY`) + Blessing (`CHARGES_DIZANAS_QUIVER_BLESSING`).
- Trident/Shadow/Tome/Bowfa etc. → eine Zahl.
- **Lootingsack**: Geldwert (Coins) als Zahl auf dem Sack-Icon.
- **Coal Bag**: Füllstand via Chat-Tracking + Persistenz (kein Varbit!).
**Format:** Ganzzahlen / Stack-Format (Geld: `QuantityFormatter.quantityToStackSize`, z. B. `1.2M`). Unbekannter Wert → `?`.
## Datenquellen & Mechanik
### 1. `CHARGES_*`-Varbits (Hauptquelle, charakterweit)
Werte via `client.getVarbitValue(id)` (`net.runelite.api.gameval.VarbitID`). Mapping Item-Gruppe → Varbit (verifiziert, Release 1.12.39). Varbit aus der **ID-Variante** des Items ableiten (s. Quelle 2), nicht aus einem Katalog — zwei identische Waffen haben nur EINEN Varbit-Wert (OSRS-Grenze!).
| Item-Gruppe (ItemID-Konstanten) | VarbitID-Konstante |
|---|---|
| `TOXIC_BLOWPIPE`, `TOXIC_BLOWPIPE_LOADED`, `TOXIC_BLOWPIPE_ORNAMENT`, `TOXIC_BLOWPIPE_LOADED_ORNAMENT` | `CHARGES_TOXIC_BLOWPIPE_QUANTITY` |
| `SERPENTINE_HELM`, `SERPENTINE_HELM_CHARGED`, `_CYAN`, `_CYAN_CHARGED`, `_RED`, `_RED_CHARGED` | `CHARGES_SERPENTINE_HELM_QUANTITY` |
| `WARPED_SCEPTRE`, `WARPED_SCEPTRE_UNCHARGED` | `CHARGES_WARPED_SCEPTRE_QUANTITY` |
| `TUMEKENS_SHADOW`, `TUMEKENS_SHADOW_UNCHARGED`, `TUMEKENS_HEKA`, `TUMEKENS_HEKA_UNCHARGED` | `CHARGES_TUMEKENS_SHADOW_QUANTITY` |
| `SANGUINESTI_STAFF`, `SANGUINESTI_STAFF_UNCHARGED`, `_OR`, `_UNCHARGED_OR` | `CHARGES_SANGUINESTI_STAFF_QUANTITY` |
| `TRIDENT_OF_THE_SEAS` (+ `_E`), jew. gecharge/un-gecharge-Varianten | `CHARGES_TRIDENT_OF_THE_SEAS_QUANTITY` bzw. `..._E_QUANTITY` |
| `TOXIC_STAFF_OF_THE_DEAD` (+ `_UNCHARGED`, `_OR`-Varianten) | `CHARGES_TOXIC_STAFF_OF_THE_DEAD_QUANTITY` |
| `BOW_OF_FAERDHINEN`, `BOW_OF_FAERDHINEN_INACTIVE`, `_INFINITE*` | `CHARGES_BOW_OF_FAERDHINEN_QUANTITY` (+ `CHARGES_BOW_OF_FAERDHINEN_CORRUPTION` für korrupte Variante) |
| `BLADE_OF_SAELDOR`, `BLADE_OF_SAELDOR_INACTIVE`, `_INFINITE` | `CHARGES_BLADE_OF_SAELDOR_QUANTITY` (+ `_CORRUPTION`) |
| `VENATOR_BOW`, `VENATOR_BOW_UNCHARGED`, `VENATOR_BOW_ORNAMENT(_UNCHARGED)` | `CHARGES_VENATOR_BOW_QUANTITY` |
| `TONALZTICS_OF_RALOS_CHARGED`, `_UNCHARGED` | `CHARGES_TONALZTICS_OF_RALOS_QUANTITY` |
| `ARCLIGHT` | `CHARGES_ARCLIGHT_QUANTITY` |
| `DIZANAS_QUIVER_CHARGED`, `_UNCHARGED`, `_INFINITE`, `_BROKEN`, Trouver-Varianten | `CHARGES_DIZANAS_QUIVER_QUANTITY` (Ammo) + `CHARGES_DIZANAS_QUIVER_BLESSING` (Blessing) — zwei Zahlen |
| `TOME_OF_FIRE`, `TOME_OF_FIRE_UNCHARGED` | `CHARGES_TOME_OF_FIRE_QUANTITY` |
| `TOME_OF_WATER`, `TOME_OF_WATER_UNCHARGED` | `CHARGES_TOME_OF_WATER_QUANTITY` |
| `TOME_OF_EARTH`, `TOME_OF_EARTH_UNCHARGED` | `CHARGES_TOME_OF_EARTH_QUANTITY` |
| `ASH_SANCTIFIER` | `CHARGES_ASH_SANCTIFIER_QUANTITY` |
| `BONECRUSHER` | `CHARGES_BONECRUSHER_QUANTITY` |
| `BRYOPHYTAS_STAFF` | `CHARGES_BRYOPHYTAS_STAFF_QUANTITY` |
| `CIRCLET_OF_WATER` | `CHARGES_CIRCLET_OF_WATER_QUANTITY` |
| `SOUL_BEARER` | `CHARGES_SOUL_BEARER_QUANTITY` |
| `CELESTIAL_RING`, `CELESTIAL_RING_CHARGED` | `CHARGES_CELESTIAL_RING_QUANTITY` |
| `WILD_CAVE_SCEPTRE_CHARGED`, `WILD_CAVE_SCEPTRE_UNCHARGED` | `CHARGES_WILDERNESS_WEAPON_QUANTITY` |
| `BRACELET_OF_ETHEREUM` | `CHARGES_BRACELET_OF_ETHEREUM_QUANTITY` |
| `RING_OF_SUFFERING` | `CHARGES_RING_OF_SUFFERING_QUANTITY` |
| `XERIC_TALISMAN`, `XERIC_TALISMAN_EMPTY` | `CHARGES_XERICS_TALISMAN_QUANTITY` |
| `RING_OF_SHADOWS`, `RING_OF_SHADOWS_UNCHARGED` | `CHARGES_RING_OF_SHADOWS_QUANTITY` |
| `ECHO_BOOTS` | `CHARGES_ECHO_BOOTS_QUANTITY` |
| `AMULET_OF_BLOOD_FURY` | `CHARGES_AMULET_OF_BLOOD_FURY_QUANTITY` |
| `GIANTSOUL_AMULET_CHARGED`, `_UNCHARGED` | `CHARGES_GIANTSOUL_AMULET_QUANTITY` |
| `EYE_OF_AYAK`, `EYE_OF_AYAK_UNCHARGED` | `CHARGES_EYE_OF_AYAK_QUANTITY` |
| `HORN_OF_PLENTY`, `_UNCHARGED`, `SAILORS_AMULET`, `_EMPTY`, `COWBELL_AMULET`, `_EMPTY`, `PENDANT_OF_ATES`, `_EMPTY` | jew. `CHARGES_*_QUANTITY`-Varbit |
| Crystal Armour/Tools (Crystal, `CRYSTAL_*` piece IDs) | `CHARGES_CRYSTAL_ARMOUR_QUANTITY`, `CHARGES_CRYSTAL_TOOLS_QUANTITY`, `CHARGES_CRYSTAL_WEAK_QUANTITY` |
| `ALCHEMIST_RING` | `CHARGES_ALCHEMIST_AMULET_QUANTITY` |
Vollständige Liste ergänzen per `javap -constants net.runelite.api.gameval.VarbitID | grep 'CHARGES_.*_QUANTITY'` gegen das runelite-api-Jar (release 1.12.39; Pfad s. **Referenzen**). Das sind ~55 Konstanten; es kommen auch Solo-/Sailing-Charges vor (`CHARGES_PLANK_SACK*`, `CHARGES_BOWSTRING_SPOOL*`), nur relevante übernehmen. Stichproben-Hinweis: `DBTableID$Charges$Row` (in runelite-api `net.runelite.api.gameval.DBTableID`) listet dieselben Charge-Typen als DB-Rows (Referenz/Querschnitt, nicht zwingend zur Laufzeit nötig).
Varianten-Ableitung für die Tabelle: Einträge in `net.runelite.api.gameval.ItemID` mit typischen Suffixen (`_CHARGED`, `_UNCHARGED`, `_INACTIVE`, `_EMPTY`, `_LOADED`, `_OR`, `_CYAN/_RED`, `_TROUVER`, `_INFINITE`, `_BROKEN`) — per `javap -constants ... ItemID | grep` verifizieren.
### 2. Item-ID-Statusvarianten (Ladungs-Status)
Viele Items wechseln die **Item-ID** bei leer/laden — mehrere exakte IDs auf dieselbe Charge abbilden bzw. „0"-Semantik:
- `TOXIC_BLOWPIPE` (12924) vs `TOXIC_BLOWPIPE_LOADED` (12926): **Darts geladen: ja/nein** (zweiter Wert, Marker; die exakte **Dart-Anzahl ist NICHT in der API** — kein Varbit/Container). `DRYGORE_BLOWPIPE` vs `DRYGORE_BLOWPIPE_LOADED` analog.
- `BOW_OF_FAERDHINEN_INACTIVE` → 0; `BLADE_OF_SAELDOR_INACTIVE` → 0; `*_INFINITE` → „∞".
- Alle `*_UNCHARGED`/`*_EMPTY` → 0 bzw. kein Varbit abfragen.
### 3. Container (Echtzeit; per GameTick gelesen)
- **Lootingsack**: `client.getItemContainer(InventoryID.LOOTING_BAG)` (auch `LOOTING_BAG_OPEN`). Geldwert = Σ qty von `ItemID.COINS` im Container. (Plugin hat bereits `readContainer()`/WealthStore für Loot — hier eigenes Lesen im Overlay-Tick, da das Overlay nicht an den Container-Event gebunden ist.)
- **Dizan's Quiver Ammo**: `InventoryID.DIZANAS_QUIVER_AMMO` als Crosscheck/Geld-Anzeige zum Varbit-Wert möglich.
- **Coal/Gem/Herb sack**: kein Container, kein Varbit (verifiziert) → Quelle 4.
### 4. Coal Bag — Chat-Tracking + Persistenz
- Kein Varbit/Container in `VarbitID`/`InventoryID` (verifiziert; nur `ItemID.COAL_BAG`/`COAL_BAG_OPEN`).
- Füllstand (max **27**) ableiten aus **Chat-Nachrichten** + **letzten bekannten Wert pro Account** in `configManager` persistieren (key `coalBag.<accountKey>`, Wert int 0–27; `accountKey`-Konvention wie bestehende Persistenz im Plugin).
- Zu verarbeitende Muster (ChatMessage-Filter `Game`): Check-/Fill-Feedback „[…]`X` coal in your coal bag]" o. Ä. *exakt in-game verifizieren* (2024 wurde Feedback von Popups auf Chat umgestellt); zusätzlich Empty → 0; bei unbekannter Nachricht/Login ohne Check → **`?`** anzeigen statt falscher Zahl.
- `ChatMessage`-Events: Fehler-tolerant parsen (Regex), bei Update sofort speichern. Beim `LOGOUT` aktuellen Wert speichern. Optional Rechtsklick „Coal bag vergessen" → Wert nicht aus Chat-Nachrichten ableiten.
## Neue Dateien
### `helpers/ItemCharges.java` (`@Singleton`, Logik)
- **Datenmodell**: `ChargeValue { String text; Color color; }` (+ optional 2. Wert). Lookup-API `List<ChargeValue> chargesFor(int itemId)`:
1. exakte `itemId` in Tabelle (Load/Empty/Inactive-Varianten); dann
2. `itemManager.canonicalize(itemId)` (Noten/Placeholder) erneut prüfen.
- **`CHARGE_MAP`**: `Map<Integer itemId, ChargeSource>` statisch aufgebaut aus den ItemID-Konstanten (Quelle 1+2); `ChargeSource` = Varbit-Konstante bzw. `COAL_BAG`-Typ bzw. `LOOTING_BAG`-Container-Typ.
- **State**: Coal-Bag-Count pro Account + `Map<Integer, ...>`-Cache nur so weit nötig (Varbit-Lesen ist billig; kein harter Cache zwingend).
- **Events** (im Plugin registriert, Muster `TreeCooldowns`):
- `GameTick` → die zwei Bag-Varbits/Container aktualisieren.
- `VarbitChanged` → relevante Varbit-Id → Value merken.
- `ChatMessage` → Coal-Bag-Messages parsen (s. o.), speichern.
- `GameStateChanged` → `!= LOGGED_IN` → `reset()`, Coal-Bag-Count aus `configManager` laden bzw. auf „?" zurücksetzen.
- **Reset**: Coal-Bag-Cache auf `?` beim Logout (Persistenz bleibt).
### `helpers/ItemChargesOverlay.java` (`WidgetItemOverlay`)
- `showOnInventory(); showOnEquipment(); showOnBank();` — Bank über Config-Sub-Key steuerbar (s. unten).
- `renderItemOverlay(g, itemId, WidgetItem)`:
- `if (!config.itemCharges()) return;`
- `List<ChargeValue>` aus `chargesFor(itemId)`; leer → nichts rendern.
- Zahl unten-links am Icon (offizielles RuneLite-Muster): `bounds.x - 1, bounds.y + 15`, mit Schatten (`Fonts.RUNESCAPE_SMALL` bzw. `TimerTextSize`), Farbe aus `itemChargesColor`; **2. Wert** (Blowpipe-Darts, Quiver-Blessing) darüber (`bounds.y - 3`) in `itemChargesSecondaryColor`.
- Geld (Lootingsack): Stack-Size-Format; Low-Charge-Färbung ab `itemChargesLowAt` → `itemChargesLowColor`.
- Coins/Loot-Bag-Wert: aus `chargesFor(LOOTING_BAG_OPEN/LOOTING_BAG)`-Container-Lesung im Overlay-Tick (Falls Container null → kein Wert).
## Zu ändernde Dateien
- **`EssentialsPlusConfig.java`**: `@ConfigItem itemCharges` (bool, default `false`, position ~37). Optionen als `configManager`-Keys via `setHelperOption` (Muster wie treeCooldowns):
- `itemChargesWeapons` (bool, true) — Varbit-Charges an/aus
- `itemChargesBags` (bool, true) — Loot-Bag-Geld + Coal-Bag-Füllstand an/aus
- `itemChargesBank` (bool, false) — Overlay auch in Bank (Achtung Hinweise!)
- `itemChargesSize` (`TimerTextSize`, SMALL), `itemChargesColor` (Color, weiß), `itemChargesSecondaryColor` (Color, hellgrau `0xC0C0C0`), `itemChargesLowColor` (Color, rot `0xFF3333`), `itemChargesLowAt` (int, 0 = aus)
- **`EssentialsPlusPlugin.java`**: `@Inject ItemCharges` + `ItemChargesOverlay`; `startUp()`: `eventBus.register(itemCharges)`, `overlayManager.add(itemChargesOverlay)`; `shutDown()`: spiegeln + `itemCharges.reset()`. `isHelperOption()`: `case "itemCharges"` → `config.itemCharges()`. `setHelperOption`/`onConfigChanged`: Bestände für `itemcharges*`-Keys refresh (Muster solcher Bestands-Abschnitte).
- **`HelpersCard.java`**: neues `JCheckBox "Item charges"` (Tooltip: „Charge-Zahl auf Item-Icons (Blowpipe, Trident, Tome, …) + Loot-Bag-Geld & Coal-Bag-Füllstand. Rechtsklick für Optionen"). Rechtsklick-Menü analog `styleMenu`/`treeMenu`: Toggles `itemChargesWeapons`/`itemChargesBags`/`itemChargesBank`, Größe, Farben, Schwellenwert, Reset. `refresh()` erweitern.
## Verifikation
- `./gradlew compileJava` (bzw. `./gradlew deploy` für Bolt).
- Manuell: Blowpipe mit Scales (nur Scales-Zahl) → mit Darts laden (2. Zahl erscheint); Trident/Shadow/Sang/Tome (Zahl); Lootingsack mit Coins betreten (Geld auf Sack-Icon); Coal Bag füllen → Zahl steigt, Empty → 0, Relog → Wert bleibt; Bank öffnen mit `itemChargesBank` → Zahlen auch dort; beide Blowpipes (leer + geladen) im Inventar → korrekte per-ID-Darstellung.
## Hinweise / Einschränkungen
- `CHARGES_*`-Varbits sind **charakterweit, nicht pro Item-Exemplar**: zwei unterschiedlich geladene Tridents zeigen denselben Wert (OSRS-Systemgrenze, deckungsgleich mit dem In-Game-Charge-Overlay).
- **Bank-Anzeige** dementsprechend unzuverlässig für „unbenutzte" Kopien; default daher `itemChargesBank = false`.
- Coal Bag: nur Heuristik (Chat + Persistenz), kein API-Wert; nach Login unbekannt → `?`.
- Blowpipe: Dart-**Anzahl** nicht in der API → 2. Wert ist Darts-geladen-Marker, keine Zahl.
- Keine Chat-Popup-abhängigkeit (Popup = ist abgeschaltet hat), ausschließlich ChatMessage-Events verwenden.
## Referenzen
- `tree-cooldown.md` (gleiche Struktur), `reflect.txt`/Cachierte Jars für Konstanten:
- runelite-api: `/home/hikyu/.gradle/caches/modules-2/files-2.1/net.runelite/runelite-api/1.12.39/<hash>/runelite-api-1.12.39.jar` → `net/runelite/api/gameval/{VarbitID,ItemID,InventoryID,DBTableID$Charges$Row}`
- client: `/home/hikyu/.gradle/caches/modules-2/files-2.1/net.runelite/client/1.12.39/<hash>/client-1.12.39.jar` → `net.runelite.client.ui.overlay.WidgetItemOverlay`, `components.TextComponent`, `ui.FontManager`
- Konventionen: `helpers/TreeCooldowns.java` (Logik+Events), `helpers/TreeCooldownOverlay.java`/`ItemPriceOverlay.java` (Overlay-Zeichnung), `HelpersCard.java` (Checkbox+Menü), `EssentialsPlusPlugin.isHelperOption/setHelperOption/onConfigChanged`.