feat: blat furnace and chat helper
This commit is contained in:
+2
-1
@@ -6,4 +6,5 @@ build
|
||||
.classpath
|
||||
nbactions.xml
|
||||
nb-configuration.xml
|
||||
nbproject/
|
||||
nbproject/
|
||||
.aider*
|
||||
|
||||
@@ -38,3 +38,18 @@ Bei **JEDEM** Update / Build müssen immer folgende Schritte ausgeführt werden:
|
||||
- **Wealth & Currency Store**: Vermögen, GE-Buy-Limits und Minigame-Währungen.
|
||||
- **Loot & Combat Tracker**: Drop Tables, Sessions, DPS-Rechner.
|
||||
- **Bank Highlighter**: Farbhervorhebung in Bank-Tabs.
|
||||
- **Blast Furnace Helper** (`com.example.essentialsplus.helpers.BlastFurnace*`):
|
||||
- Markiert strikt sequenziell immer nur genau ein Ziel ("niemals mehrere Dinge gleichzeitig"):
|
||||
- Bei geöffneter Bank: Barren einlagern -> leere Vials einlagern -> bei <= 30 Energie Stamina Potion trinken (Priorität: 1-Dose Extended > 1-Dose Normal > 2-4 Dose Extended > 2-4 Dose Normal; angebrochene Tränke werden vor dem Erz-Abheben zurück in die Bank gelegt) -> Coal-Bag füllen im Inventar & Ziel-Erz/Kohle in der Bank abheben werden gleichzeitig markiert (sodass man Coal Bag füllen und direkt das Erz/Kohle anklicken kann; Mindestbestand im Ofen mit +1 Puffer, z. B. 4x Erz-Slots = 108 Kohle bei Runite mit Coal Bag; Priorität: Runite > Adamantite > Mithril > Gold > Iron/Steel > Silver > Bronze nach Smithing-Level & Bank-Bestand; Erze mit < 27 Stück in der Bank werden ignoriert, um halbe Touren zu verhindern; wenn alle Erze < 27 sind, ist der Helper fertig und markiert nichts mehr).
|
||||
- Sobald das Inventar voll ist, wird das Förderband ("Put ore on belt" / "Put coal on belt") sofort in der 3D-Welt markiert – auch bei noch geöffneter Bank, sodass man mit 1 Klick direkt vom Bankfenster zum Band laufen kann.
|
||||
- Intelligentes Warten am Bar Dispenser: Nach dem Auflegen von Erzen auf das Band wird der Bar Dispenser ("Wait for bars" / "Collect bars") markiert, bis die Barren geschmolzen und abgeholt sind, anstatt voreilig zur Bank-Chest zu leiten. Erst mit Barren im Inventar geht es zur Bank-Chest ("Bank bars").
|
||||
- Rand-Markierung ("Border-only"): Items in Bank & Inventar werden mit einem klaren Rahmen ohne deckenden Fill umrandet, sodass jedes Icon deutlich erkennbar bleibt.
|
||||
- Einstellungen und Beschriftungen vollständig auf Englisch (Menüs im Helpers-Card per Rechtsklick, Panel-Zeilen, Tooltips).
|
||||
- Info-Panel: Coal in furnace, Bars in furnace, Coffer + Restzeit und berechnete Barren aus Bank-Ores.
|
||||
- **Chat Helper** (`com.example.essentialsplus.helpers.ChatHelper*`):
|
||||
- Automatisches Aufräumen (Auto-Clear) alter Chat-Nachrichten nach konfigurierbarer Dauer (Standard 60s / 1 Min., 30s, 2m, 5m). Selektiver Schutz für private Nachrichten & Clan-/Friends-Chats.
|
||||
- Duplikat-Kompaktierung: Aufeinanderfolgende gleiche Meldungen werden zu `(x2, x3...)` komprimiert.
|
||||
- Spam- & Noise-Filter: Unterdrückt überflüssigen Systemlärm ("Nothing interesting happens", etc.) und Werbebots.
|
||||
- Namen-Highlighting: Eigener Spielername wird in öffentlichen/Clan-Chats hervorgehoben (Farbe & Fettdruck).
|
||||
- Rechtsklick-Aktionen: "Copy message" (kopiert bereinigten Text) & "Clear chat" auf Tabs / Kontextmenü.
|
||||
|
||||
|
||||
@@ -271,6 +271,17 @@ public interface EssentialsPlusConfig extends Config
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "blastFurnace",
|
||||
name = "Blast Furnace helper",
|
||||
description = "Highlights conveyor belt, bar dispenser, and bank chest, and shows coal in furnace, coffer time, and craftable bars from bank ores. Right-click the toggle for options.",
|
||||
position = 38
|
||||
)
|
||||
default boolean blastFurnace()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "enemyAttackStyleText",
|
||||
name = "Enemy Attack Style: Text statt Icon",
|
||||
@@ -583,5 +594,140 @@ public interface EssentialsPlusConfig extends Config
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatHelper",
|
||||
name = "Chat helper",
|
||||
description = "Automatisches Aufräumen alter Nachrichten (nach 1 Minute), Duplikate (x2, x3), Spam-Filter, Namen-Highlight & Kopieren.",
|
||||
position = 90
|
||||
)
|
||||
default boolean chatHelper()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatAutoClear",
|
||||
name = "Chat: Auto-Clear",
|
||||
description = "Entfernt alte Nachrichten nach Ablauf der gewählten Zeit automatisch aus dem Chat.",
|
||||
position = 91
|
||||
)
|
||||
default boolean chatAutoClear()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatAutoClearTime",
|
||||
name = "Chat: Auto-Clear Zeit",
|
||||
description = "Zeit in Sekunden, nach der Nachrichten automatisch verschwinden.",
|
||||
position = 92
|
||||
)
|
||||
@Range(min = 10, max = 600)
|
||||
@Units(Units.SECONDS)
|
||||
default int chatAutoClearTime()
|
||||
{
|
||||
return 60;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatClearGame",
|
||||
name = "Chat: Spiel-Nachrichten leeren",
|
||||
description = "Spiel-, System- und Examine-Meldungen automatisch aufräumen.",
|
||||
position = 93
|
||||
)
|
||||
default boolean chatClearGame()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatClearPublic",
|
||||
name = "Chat: Öffentlichen Chat leeren",
|
||||
description = "Öffentliche Spielernachrichten automatisch aufräumen.",
|
||||
position = 94
|
||||
)
|
||||
default boolean chatClearPublic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatClearPrivate",
|
||||
name = "Chat: Private Nachrichten leeren",
|
||||
description = "Private Nachrichten (PMs) ebenfalls aufräumen (Standard: Aus, um Konversationen zu behalten).",
|
||||
position = 95
|
||||
)
|
||||
default boolean chatClearPrivate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatClearClan",
|
||||
name = "Chat: Clan- & Freunde-Chat leeren",
|
||||
description = "Clan- und Gruppen-Nachrichten ebenfalls aufräumen (Standard: Aus).",
|
||||
position = 96
|
||||
)
|
||||
default boolean chatClearClan()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatCollapseDuplicates",
|
||||
name = "Chat: Duplikate zusammenfassen",
|
||||
description = "Fasst aufeinanderfolgende gleiche Meldungen zu (x2, x3...) zusammen.",
|
||||
position = 97
|
||||
)
|
||||
default boolean chatCollapseDuplicates()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatFilterSpam",
|
||||
name = "Chat: Spam & Systemlärm filtern",
|
||||
description = "Filtert repetitive, unnötige Systemmeldungen ('Nothing interesting happens' etc.) und Spam.",
|
||||
position = 98
|
||||
)
|
||||
default boolean chatFilterSpam()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatHighlightName",
|
||||
name = "Chat: Eigenen Namen hervorheben",
|
||||
description = "Hebt deinen Spielernamen in Spielernachrichten farblich hervor.",
|
||||
position = 99
|
||||
)
|
||||
default boolean chatHighlightName()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatHighlightColor",
|
||||
name = "Chat: Highlight-Farbe",
|
||||
description = "Farbe für die Namens-Hervorhebung.",
|
||||
position = 100
|
||||
)
|
||||
default Color chatHighlightColor()
|
||||
{
|
||||
return new Color(0xFFD700);
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "chatCopyToClipboard",
|
||||
name = "Chat: Kopieren im Rechtsklick-Menü",
|
||||
description = "Fügt beim Rechtsklick auf eine Chat-Nachricht die Option 'Copy message' hinzu.",
|
||||
position = 101
|
||||
)
|
||||
default boolean chatCopyToClipboard()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -256,6 +256,21 @@ public class EssentialsPlusPlugin extends Plugin
|
||||
@Inject
|
||||
private com.example.essentialsplus.helpers.ItemChargesOverlay itemChargesOverlay;
|
||||
|
||||
@Inject
|
||||
private com.example.essentialsplus.helpers.BlastFurnace blastFurnace;
|
||||
|
||||
@Inject
|
||||
private com.example.essentialsplus.helpers.BlastFurnaceOverlay blastFurnaceOverlay;
|
||||
|
||||
@Inject
|
||||
private com.example.essentialsplus.helpers.BlastFurnacePanelOverlay blastFurnacePanelOverlay;
|
||||
|
||||
@Inject
|
||||
private com.example.essentialsplus.helpers.BlastFurnaceItemsOverlay blastFurnaceItemsOverlay;
|
||||
|
||||
@Inject
|
||||
private com.example.essentialsplus.helpers.ChatHelper chatHelper;
|
||||
|
||||
@Inject
|
||||
private net.runelite.client.eventbus.EventBus eventBus;
|
||||
|
||||
@@ -384,10 +399,17 @@ public class EssentialsPlusPlugin extends Plugin
|
||||
eventBus.register(attackStyles);
|
||||
eventBus.register(treeCooldowns);
|
||||
eventBus.register(itemCharges);
|
||||
eventBus.register(blastFurnace);
|
||||
eventBus.register(chatHelper);
|
||||
overlayManager.add(attackTimerOverlay);
|
||||
overlayManager.add(itemPriceOverlay);
|
||||
overlayManager.add(treeCooldownOverlay);
|
||||
overlayManager.add(itemChargesOverlay);
|
||||
overlayManager.add(blastFurnaceOverlay);
|
||||
overlayManager.add(blastFurnacePanelOverlay);
|
||||
overlayManager.add(blastFurnaceItemsOverlay);
|
||||
// only safe here: reading the config from the overlay constructor would run before the plugin fields exist
|
||||
blastFurnacePanelOverlay.applyPosition();
|
||||
currencyStore.load();
|
||||
currencyTracker = new com.example.essentialsplus.currency.CurrencyTracker(client, currencyStore);
|
||||
skillSaveTask = executor.scheduleWithFixedDelay(() ->
|
||||
@@ -402,11 +424,7 @@ public class EssentialsPlusPlugin extends Plugin
|
||||
com.example.essentialsplus.loot.gear.MonsterService monsters = new com.example.essentialsplus.loot.gear.MonsterService(
|
||||
okHttpClient, gson, wikiExecutor, () -> fightService.onMonsterLoaded());
|
||||
fightService = new com.example.essentialsplus.loot.gear.FightService(client, itemManager, combatTracker, attackStyles,
|
||||
monsters, wikiExecutor, () ->
|
||||
{
|
||||
AccountData acc = activeKey != null ? store.get(activeKey) : null;
|
||||
return acc != null ? acc.containers.get("bank") : null;
|
||||
});
|
||||
monsters, wikiExecutor, this::getActiveBank);
|
||||
|
||||
panel = new EssentialsPlusPanel(this);
|
||||
BufferedImage icon = loadIcon();
|
||||
@@ -459,14 +477,21 @@ public class EssentialsPlusPlugin extends Plugin
|
||||
overlayManager.remove(itemPriceOverlay);
|
||||
overlayManager.remove(treeCooldownOverlay);
|
||||
overlayManager.remove(itemChargesOverlay);
|
||||
overlayManager.remove(blastFurnaceOverlay);
|
||||
overlayManager.remove(blastFurnacePanelOverlay);
|
||||
overlayManager.remove(blastFurnaceItemsOverlay);
|
||||
eventBus.unregister(attackTimers);
|
||||
eventBus.unregister(attackStyles);
|
||||
eventBus.unregister(treeCooldowns);
|
||||
eventBus.unregister(itemCharges);
|
||||
eventBus.unregister(blastFurnace);
|
||||
eventBus.unregister(chatHelper);
|
||||
attackTimers.reset();
|
||||
attackStyles.reset();
|
||||
treeCooldowns.reset();
|
||||
itemCharges.reset();
|
||||
blastFurnace.reset();
|
||||
chatHelper.reset();
|
||||
if (skillSaveTask != null)
|
||||
{
|
||||
skillSaveTask.cancel(false);
|
||||
@@ -619,6 +644,10 @@ public class EssentialsPlusPlugin extends Plugin
|
||||
return config.treeCooldowns();
|
||||
case "itemCharges":
|
||||
return config.itemCharges();
|
||||
case "blastFurnace":
|
||||
return config.blastFurnace();
|
||||
case "chatHelper":
|
||||
return config.chatHelper();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -649,6 +678,18 @@ public class EssentialsPlusPlugin extends Plugin
|
||||
return itemCharges;
|
||||
}
|
||||
|
||||
public com.example.essentialsplus.helpers.ChatHelper getChatHelper()
|
||||
{
|
||||
return chatHelper;
|
||||
}
|
||||
|
||||
/** Last known bank contents of the logged in character (kept while the bank is closed), may be null. */
|
||||
public Map<Integer, Long> getActiveBank()
|
||||
{
|
||||
AccountData acc = activeKey != null ? store.get(activeKey) : null;
|
||||
return acc != null ? acc.containers.get("bank") : null;
|
||||
}
|
||||
|
||||
public net.runelite.client.ui.components.colorpicker.ColorPickerManager getColorPickerManager()
|
||||
{
|
||||
return colorPickerManager;
|
||||
@@ -1776,10 +1817,15 @@ public class EssentialsPlusPlugin extends Plugin
|
||||
{
|
||||
p.refreshSkillsLater();
|
||||
}
|
||||
if (p != null && (event.getKey().toLowerCase().contains("attacktimer") || event.getKey().toLowerCase().contains("attackstyle") || event.getKey().toLowerCase().contains("treecooldown") || event.getKey().toLowerCase().contains("itemcharge")))
|
||||
if ("blastFurnacePosition".equals(event.getKey()))
|
||||
{
|
||||
blastFurnacePanelOverlay.applyPosition();
|
||||
}
|
||||
if (p != null && (event.getKey().toLowerCase().contains("attacktimer") || event.getKey().toLowerCase().contains("attackstyle") || event.getKey().toLowerCase().contains("treecooldown") || event.getKey().toLowerCase().contains("itemcharge") || event.getKey().toLowerCase().contains("blastfurnace") || event.getKey().toLowerCase().contains("chat")))
|
||||
{
|
||||
p.refreshHelpersLater();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,196 @@
|
||||
package com.example.essentialsplus.helpers;
|
||||
|
||||
import com.example.essentialsplus.EssentialsPlusConfig;
|
||||
import com.example.essentialsplus.EssentialsPlusPlugin;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.gameval.InterfaceID;
|
||||
import net.runelite.api.gameval.ItemID;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetItem;
|
||||
import net.runelite.api.widgets.WidgetUtil;
|
||||
import net.runelite.client.ui.overlay.WidgetItemOverlay;
|
||||
|
||||
/**
|
||||
* Marks the active Blast Furnace item in bank and inventory: coal bag to fill, bars to deposit,
|
||||
* or the target ore in the bank to withdraw. Only a single item type is marked at any time,
|
||||
* with border-only highlighting to keep items clearly recognizable.
|
||||
*/
|
||||
public class BlastFurnaceItemsOverlay extends WidgetItemOverlay
|
||||
{
|
||||
private final EssentialsPlusConfig config;
|
||||
private final EssentialsPlusPlugin plugin;
|
||||
private final BlastFurnace blastFurnace;
|
||||
|
||||
@Inject
|
||||
public BlastFurnaceItemsOverlay(EssentialsPlusConfig config, EssentialsPlusPlugin plugin, BlastFurnace blastFurnace)
|
||||
{
|
||||
this.config = config;
|
||||
this.plugin = plugin;
|
||||
this.blastFurnace = blastFurnace;
|
||||
showOnInventory();
|
||||
showOnBank();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderItemOverlay(Graphics2D g, int itemId, WidgetItem widgetItem)
|
||||
{
|
||||
if (!config.blastFurnace() || !blastFurnace.isAtBlastFurnace())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Widget widget = widgetItem.getWidget();
|
||||
if (widget == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
boolean bank = isBank(widget);
|
||||
boolean bankOpen = blastFurnace.isBankOpen();
|
||||
|
||||
if (bankOpen)
|
||||
{
|
||||
// State 1: Deposit bars in inventory
|
||||
if (blastFurnace.inventoryHasBars())
|
||||
{
|
||||
if (bank || !plugin.getHelperValue("blastFurnaceMarkItems", Boolean.class, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!BlastFurnace.isBar(itemId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
// State 2: Deposit empty vials in inventory
|
||||
else if (blastFurnace.inventoryHasVials())
|
||||
{
|
||||
if (bank || !plugin.getHelperValue("blastFurnaceMarkItems", Boolean.class, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (itemId != ItemID.VIAL_EMPTY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
// State 3: Deposit leftover stamina potion in inventory (if energy is already > 30)
|
||||
else if (!blastFurnace.needsStamina() && blastFurnace.inventoryHasStaminaPotion())
|
||||
{
|
||||
if (bank || !plugin.getHelperValue("blastFurnaceMarkItems", Boolean.class, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!BlastFurnace.isStaminaPotion(itemId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
// State 4: Drink stamina potion if energy <= 30
|
||||
else if (plugin.getHelperValue("blastFurnaceStamina", Boolean.class, true) && blastFurnace.needsStamina())
|
||||
{
|
||||
if (blastFurnace.inventoryHasStaminaPotion())
|
||||
{
|
||||
// Drink from inventory
|
||||
if (bank || !plugin.getHelperValue("blastFurnaceMarkItems", Boolean.class, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (itemId != blastFurnace.getInventoryStaminaPotion())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Withdraw from bank
|
||||
if (!bank || !plugin.getHelperValue("blastFurnaceMarkBankItems", Boolean.class, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (itemId != blastFurnace.getTargetBankStaminaPotion())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// State 5: Inventory is full -> conveyor belt is marked in 3D world, nothing in bank/inventory
|
||||
else if (blastFurnace.isInventoryFull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// State 6: Fill coal bag in inventory and withdraw target ore/coal from bank simultaneously
|
||||
else
|
||||
{
|
||||
if (!bank)
|
||||
{
|
||||
// In inventory: highlight coal bag if it needs filling
|
||||
if (!plugin.getHelperValue("blastFurnaceMarkItems", Boolean.class, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!blastFurnace.coalBagNeedsFill())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (itemId != ItemID.COAL_BAG && itemId != ItemID.COAL_BAG_OPEN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// In bank: highlight target ore / coal to withdraw
|
||||
if (!plugin.getHelperValue("blastFurnaceMarkBankItems", Boolean.class, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
int targetOre = blastFurnace.getTargetBankOre();
|
||||
if (targetOre == -1 || itemId != targetOre)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Bank is closed:
|
||||
// If coal in bag and no ores in inventory (e.g. at belt to empty bag)
|
||||
if (blastFurnace.bagHasCoal() && !blastFurnace.inventoryHasOres() && blastFurnace.hasCoalBag())
|
||||
{
|
||||
if (bank || !plugin.getHelperValue("blastFurnaceMarkItems", Boolean.class, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (itemId != ItemID.COAL_BAG && itemId != ItemID.COAL_BAG_OPEN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle bounds = widgetItem.getCanvasBounds();
|
||||
if (bounds == null || bounds.width < 4 || bounds.height < 4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Color color = plugin.getHelperValue("blastFurnaceColor", Color.class, BlastFurnaceOverlay.DEFAULT_MARK_COLOR);
|
||||
g.setColor(color);
|
||||
g.drawRect(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
|
||||
g.drawRect(bounds.x + 1, bounds.y + 1, bounds.width - 3, bounds.height - 3);
|
||||
}
|
||||
|
||||
private static boolean isBank(Widget widget)
|
||||
{
|
||||
int group = WidgetUtil.componentToInterface(widget.getId());
|
||||
return group == InterfaceID.BANKMAIN || group == 12; // 12 = InterfaceID.BANK
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.example.essentialsplus.helpers;
|
||||
|
||||
import com.example.essentialsplus.EssentialsPlusConfig;
|
||||
import com.example.essentialsplus.EssentialsPlusPlugin;
|
||||
import com.example.essentialsplus.ui.Ui;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
/**
|
||||
* Marks the Blast Furnace objects that are worth clicking right now: the conveyor belt while
|
||||
* ores are waiting in the inventory, the bar dispenser when bars are ready and the bank chest
|
||||
* when the bars in the inventory should be banked.
|
||||
*/
|
||||
public class BlastFurnaceOverlay extends Overlay
|
||||
{
|
||||
public static final Color DEFAULT_MARK_COLOR = new Color(0xFFB300);
|
||||
public static final Color DEFAULT_READY_COLOR = new Color(0x66FF66);
|
||||
|
||||
private final EssentialsPlusConfig config;
|
||||
private final EssentialsPlusPlugin plugin;
|
||||
private final BlastFurnace blastFurnace;
|
||||
|
||||
@Inject
|
||||
public BlastFurnaceOverlay(EssentialsPlusConfig config, EssentialsPlusPlugin plugin, BlastFurnace blastFurnace)
|
||||
{
|
||||
this.config = config;
|
||||
this.plugin = plugin;
|
||||
this.blastFurnace = blastFurnace;
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_SCENE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D g)
|
||||
{
|
||||
if (!config.blastFurnace() || !blastFurnace.isAtBlastFurnace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean bankOpen = blastFurnace.isBankOpen();
|
||||
Color markColor = plugin.getHelperValue("blastFurnaceColor", Color.class, DEFAULT_MARK_COLOR);
|
||||
Color readyColor = plugin.getHelperValue("blastFurnaceReadyColor", Color.class, DEFAULT_READY_COLOR);
|
||||
boolean labels = plugin.getHelperValue("blastFurnaceLabels", Boolean.class, true);
|
||||
TimerTextSize size = plugin.getHelperValue("blastFurnaceTextSize", TimerTextSize.class, TimerTextSize.SMALL);
|
||||
|
||||
// 1. Bars in inventory -> bank chest (only if bank is closed)
|
||||
if (blastFurnace.inventoryHasBars())
|
||||
{
|
||||
if (!bankOpen && plugin.getHelperValue("blastFurnaceMarkBank", Boolean.class, true))
|
||||
{
|
||||
mark(g, blastFurnace.getBankChest(), markColor, labels ? "Bank bars" : null, size);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. Full inventory or ores in inventory -> conveyor belt (ALLOWED EVEN IF BANK IS OPEN!)
|
||||
if (blastFurnace.isInventoryFull() || blastFurnace.inventoryHasOres())
|
||||
{
|
||||
if (plugin.getHelperValue("blastFurnaceMarkBelt", Boolean.class, true))
|
||||
{
|
||||
String label = blastFurnace.inventoryHasCoal() ? "Put coal on belt" : "Put ore on belt";
|
||||
mark(g, blastFurnace.getConveyorBelt(), markColor, labels ? label : null, size);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// While bank is open and inventory is not full, player is interacting with bank items
|
||||
if (bankOpen)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. Bars ready to collect or waiting for ores to smelt -> bar dispenser!
|
||||
if (blastFurnace.isWaitingForBars())
|
||||
{
|
||||
if (plugin.getHelperValue("blastFurnaceMarkDispenser", Boolean.class, true))
|
||||
{
|
||||
boolean ready = blastFurnace.barsReady();
|
||||
Color color = ready ? readyColor : markColor;
|
||||
String label = ready ? "Collect bars" : "Wait for bars";
|
||||
mark(g, blastFurnace.getBarDispenser(), color, labels ? label : null, size);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 4. Coal in coal bag to empty onto belt
|
||||
if (blastFurnace.bagHasCoal() && blastFurnace.hasCoalBag())
|
||||
{
|
||||
if (plugin.getHelperValue("blastFurnaceMarkBelt", Boolean.class, true))
|
||||
{
|
||||
mark(g, blastFurnace.getConveyorBelt(), markColor, labels ? "Put coal on belt" : null, size);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// 5. Otherwise open bank
|
||||
if (plugin.getHelperValue("blastFurnaceMarkBank", Boolean.class, true))
|
||||
{
|
||||
mark(g, blastFurnace.getBankChest(), markColor, labels ? "Open bank" : null, size);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void mark(Graphics2D g, GameObject object, Color color, String label, TimerTextSize size)
|
||||
{
|
||||
if (object == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Shape shape = object.getClickbox();
|
||||
if (shape == null)
|
||||
{
|
||||
shape = object.getCanvasTilePoly();
|
||||
}
|
||||
if (shape != null)
|
||||
{
|
||||
g.setColor(color);
|
||||
g.draw(shape);
|
||||
g.setColor(Ui.alpha(color, 45));
|
||||
g.fill(shape);
|
||||
}
|
||||
|
||||
if (label == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Font font = size.font();
|
||||
g.setFont(font);
|
||||
Point anchor = object.getCanvasTextLocation(g, label, 30);
|
||||
if (anchor == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FontMetrics fm = g.getFontMetrics();
|
||||
int x = anchor.getX() - fm.stringWidth(label) / 2;
|
||||
int y = anchor.getY();
|
||||
g.setColor(Color.BLACK);
|
||||
g.drawString(label, x + 1, y + 1);
|
||||
g.setColor(color);
|
||||
g.drawString(label, x, y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.example.essentialsplus.helpers;
|
||||
|
||||
import com.example.essentialsplus.EssentialsPlusConfig;
|
||||
import com.example.essentialsplus.EssentialsPlusPlugin;
|
||||
import com.example.essentialsplus.ui.Ui;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.client.ui.overlay.OverlayPanel;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
/**
|
||||
* Panel with the Blast Furnace numbers: coal in the furnace, bars waiting in the dispenser,
|
||||
* coffer and remaining time, and how many bars the ores in the bank can still be turned into.
|
||||
*/
|
||||
public class BlastFurnacePanelOverlay extends OverlayPanel
|
||||
{
|
||||
private final EssentialsPlusPlugin plugin;
|
||||
private final EssentialsPlusConfig config;
|
||||
private final BlastFurnace blastFurnace;
|
||||
|
||||
@Inject
|
||||
BlastFurnacePanelOverlay(EssentialsPlusPlugin plugin, EssentialsPlusConfig config, BlastFurnace blastFurnace)
|
||||
{
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.blastFurnace = blastFurnace;
|
||||
setPosition(OverlayPosition.TOP_LEFT);
|
||||
panelComponent.setPreferredSize(new Dimension(190, 0));
|
||||
}
|
||||
|
||||
/** Moves the panel to the corner picked in the helper menu (kept out of render so dragging works). */
|
||||
public void applyPosition()
|
||||
{
|
||||
OverlayPosition position = plugin.getHelperValue("blastFurnacePosition", OverlayPosition.class, OverlayPosition.TOP_LEFT);
|
||||
if (position != null)
|
||||
{
|
||||
setPosition(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (!config.blastFurnace() || !blastFurnace.isAtBlastFurnace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!plugin.getHelperValue("blastFurnacePanel", Boolean.class, true))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<LineComponent> lines = new ArrayList<>();
|
||||
|
||||
if (plugin.getHelperValue("blastFurnaceShowCoal", Boolean.class, true))
|
||||
{
|
||||
int coal = blastFurnace.coalInFurnace();
|
||||
BlastFurnace.BarType target = blastFurnace.getTargetBarType();
|
||||
int needed = target != null ? blastFurnace.getRequiredFurnaceCoal(target) : 0;
|
||||
Color coalColor = (needed > 0 && coal < needed) ? Ui.WARN : Ui.TEXT;
|
||||
String text = Ui.gp(coal) + (needed > 0 ? " / " + needed : "");
|
||||
lines.add(line("Coal in furnace", text, coalColor));
|
||||
}
|
||||
|
||||
if (plugin.getHelperValue("blastFurnaceShowFurnaceBars", Boolean.class, true))
|
||||
{
|
||||
blastFurnace.barsInFurnace().forEach((type, amount) ->
|
||||
lines.add(line(type.getLabel() + " (Furnace)", Ui.gp(amount), Ui.UP)));
|
||||
}
|
||||
|
||||
long coffer = blastFurnace.cofferGp();
|
||||
if (plugin.getHelperValue("blastFurnaceShowCoffer", Boolean.class, true))
|
||||
{
|
||||
lines.add(line("Coffer", Ui.gp(coffer) + " gp", Ui.GOLD));
|
||||
}
|
||||
if (plugin.getHelperValue("blastFurnaceShowCofferTime", Boolean.class, true))
|
||||
{
|
||||
long millis = BlastFurnace.cofferMillis(coffer);
|
||||
Color color = millis < 5 * Ui.MINUTE ? Ui.DOWN : (millis < 30 * Ui.MINUTE ? Ui.WARN : Ui.TEXT);
|
||||
lines.add(line("Coffer time", duration(millis), color));
|
||||
}
|
||||
|
||||
if (plugin.getHelperValue("blastFurnaceShowBankBars", Boolean.class, true))
|
||||
{
|
||||
int limit = Math.max(0, plugin.getHelperValue("blastFurnaceBankLines", Integer.class, 3));
|
||||
List<BlastFurnace.Estimate> estimates = blastFurnace.remainingBars();
|
||||
for (int i = 0; i < Math.min(limit, estimates.size()); i++)
|
||||
{
|
||||
BlastFurnace.Estimate estimate = estimates.get(i);
|
||||
String value = Ui.gp(estimate.bars) + (estimate.coalLimited ? " (Coal)" : "");
|
||||
lines.add(line("Bank: " + estimate.type.getLabel(), value, estimate.coalLimited ? Ui.WARN : Ui.TEXT));
|
||||
}
|
||||
}
|
||||
|
||||
if (lines.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
panelComponent.getChildren().add(TitleComponent.builder()
|
||||
.text("Blast Furnace")
|
||||
.color(Ui.ACCENT)
|
||||
.build());
|
||||
panelComponent.getChildren().addAll(lines);
|
||||
return super.render(graphics);
|
||||
}
|
||||
|
||||
private static LineComponent line(String left, String right, Color rightColor)
|
||||
{
|
||||
return LineComponent.builder()
|
||||
.left(left)
|
||||
.leftColor(Ui.MUTED)
|
||||
.right(right)
|
||||
.rightColor(rightColor)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static String duration(long millis)
|
||||
{
|
||||
long seconds = Math.max(0, millis / 1000);
|
||||
long hours = seconds / 3600;
|
||||
long minutes = (seconds % 3600) / 60;
|
||||
if (hours > 0)
|
||||
{
|
||||
return hours + "h " + minutes + "m";
|
||||
}
|
||||
if (minutes > 0)
|
||||
{
|
||||
return minutes + "m " + (seconds % 60) + "s";
|
||||
}
|
||||
return seconds + "s";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,578 @@
|
||||
package com.example.essentialsplus.helpers;
|
||||
|
||||
import com.example.essentialsplus.EssentialsPlusConfig;
|
||||
import java.awt.Color;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.ChatLineBuffer;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.MenuAction;
|
||||
import net.runelite.api.MessageNode;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.MenuEntryAdded;
|
||||
import net.runelite.api.events.ScriptCallbackEvent;
|
||||
import net.runelite.api.gameval.InterfaceID;
|
||||
import net.runelite.api.widgets.ComponentID;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetUtil;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
import net.runelite.client.util.Text;
|
||||
|
||||
/**
|
||||
* Chat Helper:
|
||||
* - Auto-clears messages after a configurable duration (e.g. 1 minute) to keep the chat tidy.
|
||||
* - Collapses consecutive duplicate messages (x2, x3...).
|
||||
* - Filters repetitive game noise and spam messages.
|
||||
* - Highlights mentions of the player's name in speech chats.
|
||||
* - Adds right-click "Copy message" and "Clear chat" options.
|
||||
*/
|
||||
@Slf4j
|
||||
@Singleton
|
||||
public class ChatHelper
|
||||
{
|
||||
private static final Set<String> NOISE_MESSAGES = Set.of(
|
||||
"Nothing interesting happens.",
|
||||
"You can't reach that.",
|
||||
"You cannot reach that.",
|
||||
"I can't reach that!",
|
||||
"You can't do that here.",
|
||||
"You cannot do that here.",
|
||||
"You can't do that while moving.",
|
||||
"You cannot do that while moving.",
|
||||
"You can't do that right now.",
|
||||
"You cannot do that right now.",
|
||||
"You can't log out right now.",
|
||||
"You don't have enough inventory space.",
|
||||
"You do not have enough inventory space.",
|
||||
"There is no player by that name.",
|
||||
"That player is currently offline.",
|
||||
"You are not currently in a clan.",
|
||||
"You are not currently in a friends chat."
|
||||
);
|
||||
|
||||
private static final Pattern NOISE_PREFIX_PATTERN = Pattern.compile(
|
||||
"^(Your inventory is too full to hold any more|You need a higher level to|You need to be in a member's world to).*",
|
||||
Pattern.CASE_INSENSITIVE
|
||||
);
|
||||
|
||||
private static final Pattern PUBLIC_SPAM_PATTERN = Pattern.compile(
|
||||
"(?i).*(giveaway|free gp|quitting.*search|search on yt|search yt|sub to my yt|2b for|swap cc|dicing cc|roll 55|autoclicker|osrsgold|buy gp).*"
|
||||
);
|
||||
|
||||
private static class Duplicate
|
||||
{
|
||||
int messageId = -1;
|
||||
int count = 1;
|
||||
}
|
||||
|
||||
private final Client client;
|
||||
private final EssentialsPlusConfig config;
|
||||
private final ClientThread clientThread;
|
||||
|
||||
private final LinkedHashMap<String, Duplicate> duplicateChatCache = new LinkedHashMap<String, Duplicate>(100, 0.75f, true)
|
||||
{
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<String, Duplicate> eldest)
|
||||
{
|
||||
return size() > 100;
|
||||
}
|
||||
};
|
||||
|
||||
private String lastConsecutiveKey = null;
|
||||
private int lastCheckEpoch = 0;
|
||||
|
||||
@Inject
|
||||
public ChatHelper(Client client, EssentialsPlusConfig config, ClientThread clientThread)
|
||||
{
|
||||
this.client = client;
|
||||
this.config = config;
|
||||
this.clientThread = clientThread;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
duplicateChatCache.clear();
|
||||
lastConsecutiveKey = null;
|
||||
lastCheckEpoch = 0;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
if (event.getGameState() == GameState.LOGIN_SCREEN || event.getGameState() == GameState.HOPPING)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-clear expired chat messages based on configured retention time.
|
||||
*/
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
if (!config.chatHelper() || !config.chatAutoClear())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int timeoutSeconds = config.chatAutoClearTime();
|
||||
if (timeoutSeconds <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int now = (int) Instant.now().getEpochSecond();
|
||||
if (now - lastCheckEpoch < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
lastCheckEpoch = now;
|
||||
|
||||
Map<Integer, ChatLineBuffer> chatLineMap = client.getChatLineMap();
|
||||
if (chatLineMap == null || chatLineMap.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
boolean removedAny = false;
|
||||
for (ChatLineBuffer buffer : chatLineMap.values())
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MessageNode[] lines = buffer.getLines();
|
||||
if (lines == null || lines.length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MessageNode[] copy = lines.clone();
|
||||
for (MessageNode node : copy)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int ts = node.getTimestamp();
|
||||
if (ts > 0 && (now - ts) >= timeoutSeconds && shouldClearType(node.getType()))
|
||||
{
|
||||
buffer.removeMessageNode(node);
|
||||
removedAny = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (removedAny)
|
||||
{
|
||||
client.runScript(83);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldClearType(ChatMessageType type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GAMEMESSAGE:
|
||||
case ENGINE:
|
||||
case ITEM_EXAMINE:
|
||||
case NPC_EXAMINE:
|
||||
case OBJECT_EXAMINE:
|
||||
case SPAM:
|
||||
case BROADCAST:
|
||||
case CONSOLE:
|
||||
return config.chatClearGame();
|
||||
|
||||
case PUBLICCHAT:
|
||||
case MODCHAT:
|
||||
case AUTOTYPER:
|
||||
case TRADEREQ:
|
||||
case TRADE:
|
||||
return config.chatClearPublic();
|
||||
|
||||
case PRIVATECHAT:
|
||||
case PRIVATECHATOUT:
|
||||
case MODPRIVATECHAT:
|
||||
return config.chatClearPrivate();
|
||||
|
||||
case CLAN_CHAT:
|
||||
case CLAN_GUEST_CHAT:
|
||||
case FRIENDSCHAT:
|
||||
case CLAN_MESSAGE:
|
||||
case CLAN_GIM_CHAT:
|
||||
case CLAN_GIM_MESSAGE:
|
||||
return config.chatClearClan();
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks incoming chat messages for duplicate collapsing.
|
||||
*/
|
||||
@Subscribe
|
||||
public void onChatMessage(ChatMessage event)
|
||||
{
|
||||
if (!config.chatHelper() || !config.chatCollapseDuplicates())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChatMessageType type = event.getType();
|
||||
if (!isCollapsible(type))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String name = event.getName() != null ? event.getName() : "";
|
||||
String message = event.getMessage();
|
||||
String key = name + ":" + type.ordinal() + ":" + message;
|
||||
|
||||
if (!key.equals(lastConsecutiveKey))
|
||||
{
|
||||
lastConsecutiveKey = key;
|
||||
duplicateChatCache.remove(key);
|
||||
}
|
||||
|
||||
Duplicate dup = duplicateChatCache.get(key);
|
||||
if (dup == null)
|
||||
{
|
||||
dup = new Duplicate();
|
||||
dup.count = 1;
|
||||
dup.messageId = event.getMessageNode() != null ? event.getMessageNode().getId() : -1;
|
||||
duplicateChatCache.put(key, dup);
|
||||
}
|
||||
else
|
||||
{
|
||||
dup.count++;
|
||||
dup.messageId = event.getMessageNode() != null ? event.getMessageNode().getId() : -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercepts chat line rendering to filter spam, collapse duplicates, and highlight names.
|
||||
*/
|
||||
@Subscribe
|
||||
public void onScriptCallbackEvent(ScriptCallbackEvent event)
|
||||
{
|
||||
if (!"chatFilterCheck".equals(event.getEventName()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config.chatHelper())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int[] intStack = client.getIntStack();
|
||||
int intStackSize = client.getIntStackSize();
|
||||
Object[] objectStack = client.getObjectStack();
|
||||
int objectStackSize = client.getObjectStackSize();
|
||||
|
||||
if (intStackSize < 3 || objectStackSize < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int messageTypeVal = intStack[intStackSize - 2];
|
||||
int messageId = intStack[intStackSize - 1];
|
||||
String message = (String) objectStack[objectStackSize - 1];
|
||||
if (message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Spam & Noise filtering
|
||||
if (config.chatFilterSpam() && isSpamOrNoise(messageTypeVal, message))
|
||||
{
|
||||
intStack[intStackSize - 3] = 0; // hide message
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Duplicate collapsing
|
||||
if (config.chatCollapseDuplicates())
|
||||
{
|
||||
ChatMessageType type = ChatMessageType.of(messageTypeVal);
|
||||
if (isCollapsible(type))
|
||||
{
|
||||
MessageNode node = null;
|
||||
if (client.getMessages() != null)
|
||||
{
|
||||
node = (MessageNode) client.getMessages().get((long) messageId);
|
||||
}
|
||||
String name = node != null && node.getName() != null ? node.getName() : "";
|
||||
|
||||
String key = name + ":" + (type != null ? type.ordinal() : messageTypeVal) + ":" + message;
|
||||
Duplicate dup = duplicateChatCache.get(key);
|
||||
if (dup != null && dup.messageId != -1)
|
||||
{
|
||||
if (dup.messageId == messageId)
|
||||
{
|
||||
if (dup.count > 1)
|
||||
{
|
||||
message = message + " <col=888888>(x" + dup.count + ")</col>";
|
||||
objectStack[objectStackSize - 1] = message;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hide earlier duplicate instances
|
||||
intStack[intStackSize - 3] = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Highlight own name
|
||||
if (config.chatHighlightName() && isPlayerSpeechType(messageTypeVal))
|
||||
{
|
||||
String highlighted = highlightPlayerName(message);
|
||||
if (highlighted != null)
|
||||
{
|
||||
objectStack[objectStackSize - 1] = highlighted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCollapsible(ChatMessageType type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GAMEMESSAGE:
|
||||
case ENGINE:
|
||||
case SPAM:
|
||||
case PUBLICCHAT:
|
||||
case MODCHAT:
|
||||
case AUTOTYPER:
|
||||
case CLAN_CHAT:
|
||||
case FRIENDSCHAT:
|
||||
case ITEM_EXAMINE:
|
||||
case OBJECT_EXAMINE:
|
||||
case NPC_EXAMINE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPlayerSpeechType(int messageTypeVal)
|
||||
{
|
||||
ChatMessageType type = ChatMessageType.of(messageTypeVal);
|
||||
if (type == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case PUBLICCHAT:
|
||||
case MODCHAT:
|
||||
case FRIENDSCHAT:
|
||||
case CLAN_CHAT:
|
||||
case CLAN_GUEST_CHAT:
|
||||
case CLAN_GIM_CHAT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSpamOrNoise(int messageTypeVal, String message)
|
||||
{
|
||||
ChatMessageType type = ChatMessageType.of(messageTypeVal);
|
||||
if (type == null || message == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String clean = Text.removeTags(message).trim();
|
||||
|
||||
if (type == ChatMessageType.GAMEMESSAGE || type == ChatMessageType.ENGINE || type == ChatMessageType.SPAM)
|
||||
{
|
||||
if (NOISE_MESSAGES.contains(clean) || NOISE_PREFIX_PATTERN.matcher(clean).matches())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == ChatMessageType.PUBLICCHAT || type == ChatMessageType.MODCHAT || type == ChatMessageType.AUTOTYPER)
|
||||
{
|
||||
if (PUBLIC_SPAM_PATTERN.matcher(clean).matches())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String highlightPlayerName(String message)
|
||||
{
|
||||
Player local = client.getLocalPlayer();
|
||||
if (local == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
String name = local.getName();
|
||||
if (name == null || name.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
String lower = message.toLowerCase();
|
||||
String lowerName = name.toLowerCase();
|
||||
if (!lower.contains(lowerName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Color highlightColor = config.chatHighlightColor();
|
||||
String hex = ColorUtil.colorToHexCode(highlightColor != null ? highlightColor : new Color(0xFFD700));
|
||||
|
||||
Pattern pattern = Pattern.compile("(?i)(?<![a-zA-Z0-9_])" + Pattern.quote(name) + "(?![a-zA-Z0-9_])");
|
||||
return pattern.matcher(message).replaceAll("<col=" + hex + "><b>" + name + "</b></col>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds "Copy message" and "Clear chat" to right-click menus.
|
||||
*/
|
||||
@Subscribe
|
||||
public void onMenuEntryAdded(MenuEntryAdded event)
|
||||
{
|
||||
if (!config.chatHelper())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int type = event.getType();
|
||||
if (type != MenuAction.CC_OP.getId() && type != MenuAction.CC_OP_LOW_PRIORITY.getId())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int param1 = event.getActionParam1();
|
||||
int interfaceId = WidgetUtil.componentToInterface(param1);
|
||||
int childId = WidgetUtil.componentToId(param1);
|
||||
|
||||
if (interfaceId != InterfaceID.CHATBOX)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Copy message
|
||||
if (config.chatCopyToClipboard() && "Report".equals(event.getOption()))
|
||||
{
|
||||
Widget lineWidget = client.getWidget(interfaceId, childId);
|
||||
if (lineWidget != null && lineWidget.getParent() != null && lineWidget.getParent().getId() == ComponentID.CHATBOX_MESSAGE_LINES)
|
||||
{
|
||||
int lineIndex = (childId - WidgetUtil.componentToId(ComponentID.CHATBOX_FRAME)) * 4 + 1;
|
||||
Widget textWidget = lineWidget.getParent().getChild(lineIndex);
|
||||
if (textWidget != null && textWidget.getText() != null)
|
||||
{
|
||||
String fullText = textWidget.getText();
|
||||
client.createMenuEntry(1)
|
||||
.setOption("Copy message")
|
||||
.setTarget(event.getTarget())
|
||||
.setType(MenuAction.RUNELITE)
|
||||
.onClick(e -> copyToClipboard(fullText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Clear chat on chat tab buttons
|
||||
String option = event.getOption();
|
||||
if (option != null && (option.startsWith("Switch tab") || option.startsWith("Filter")))
|
||||
{
|
||||
client.createMenuEntry(-2)
|
||||
.setOption("Clear chat")
|
||||
.setType(MenuAction.RUNELITE_HIGH_PRIORITY)
|
||||
.onClick(e -> clearAllChat());
|
||||
}
|
||||
}
|
||||
|
||||
private void copyToClipboard(String text)
|
||||
{
|
||||
try
|
||||
{
|
||||
String cleanText = Text.removeTags(text);
|
||||
StringSelection selection = new StringSelection(cleanText);
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.warn("Failed to copy chat text to clipboard", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually empties all chat line buffers and rebuilds the chatbox widget.
|
||||
*/
|
||||
public void clearAllChat()
|
||||
{
|
||||
clientThread.invokeLater(() ->
|
||||
{
|
||||
Map<Integer, ChatLineBuffer> chatLineMap = client.getChatLineMap();
|
||||
if (chatLineMap == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (ChatLineBuffer buffer : chatLineMap.values())
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MessageNode[] lines = buffer.getLines();
|
||||
if (lines == null || lines.length == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MessageNode[] copy = lines.clone();
|
||||
for (MessageNode node : copy)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
buffer.removeMessageNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.runScript(83);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1320,6 +1320,14 @@ public class ItemCharges
|
||||
}
|
||||
}
|
||||
|
||||
/** Tracked amount for a key of {@link #cachedCounts} (e.g. "coal_bag"), 0 while unknown. */
|
||||
public int getTrackedCount(String key)
|
||||
{
|
||||
loadAccountDataIfNecessary();
|
||||
Integer val = cachedCounts.get(key);
|
||||
return val == null ? 0 : Math.max(0, val);
|
||||
}
|
||||
|
||||
public void decrementCount(String key, int amount)
|
||||
{
|
||||
loadAccountDataIfNecessary();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.example.essentialsplus.ui;
|
||||
|
||||
import com.example.essentialsplus.EssentialsPlusPlugin;
|
||||
import com.example.essentialsplus.helpers.BlastFurnaceOverlay;
|
||||
import com.example.essentialsplus.helpers.TimerTextSize;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
@@ -19,6 +20,7 @@ import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
/** Overview card with on/off switches for the small in-game helpers (attack timers, ...). */
|
||||
public class HelpersCard extends JPanel
|
||||
@@ -30,6 +32,8 @@ public class HelpersCard extends JPanel
|
||||
private final JCheckBox enemyAttackTimer = box("Enemy attack timer", "Ticks until attacking NPCs hit next, above their heads. Right-click for options");
|
||||
private final JCheckBox treeCooldowns = box("Tree cooldowns", "Respawn-Countdown auf gefällten Bäumen + Rest-Zeit auf langlebigen Bäumen. Rechtsklick für Optionen");
|
||||
private final JCheckBox itemCharges = box("Item charges", "Charge-Zahl auf Item-Icons (Blowpipe, Trident, Tome, …) + Loot-Bag-Geld & Coal-Bag-Füllstand. Rechtsklick für Optionen");
|
||||
private final JCheckBox blastFurnace = box("Blast Furnace", "Highlights conveyor belt, bar dispenser & bank chest, shows coal in furnace, coffer time, and bars craftable from bank ores. Right-click for options");
|
||||
private final JCheckBox chatHelper = box("Chat helper", "Auto-Clear nach 1 Min, Duplikate (x2, x3), Spam-Filter, Namen-Highlight & Kopieren. Rechtsklick für Optionen");
|
||||
|
||||
public HelpersCard(EssentialsPlusPlugin plugin)
|
||||
{
|
||||
@@ -102,6 +106,32 @@ public class HelpersCard extends JPanel
|
||||
}
|
||||
});
|
||||
add(itemCharges);
|
||||
blastFurnace.addActionListener(e -> plugin.setHelperOption("blastFurnace", blastFurnace.isSelected()));
|
||||
blastFurnace.addMouseListener(new java.awt.event.MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(java.awt.event.MouseEvent e)
|
||||
{
|
||||
if (e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e))
|
||||
{
|
||||
blastFurnaceMenu().show(blastFurnace, e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
});
|
||||
add(blastFurnace);
|
||||
chatHelper.addActionListener(e -> plugin.setHelperOption("chatHelper", chatHelper.isSelected()));
|
||||
chatHelper.addMouseListener(new java.awt.event.MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mousePressed(java.awt.event.MouseEvent e)
|
||||
{
|
||||
if (e.isPopupTrigger() || SwingUtilities.isRightMouseButton(e))
|
||||
{
|
||||
chatMenu().show(chatHelper, e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
});
|
||||
add(chatHelper);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@@ -114,8 +144,11 @@ public class HelpersCard extends JPanel
|
||||
itemPrices.setSelected(plugin.isHelperOption("itemPrices"));
|
||||
treeCooldowns.setSelected(plugin.isHelperOption("treeCooldowns"));
|
||||
itemCharges.setSelected(plugin.isHelperOption("itemCharges"));
|
||||
blastFurnace.setSelected(plugin.isHelperOption("blastFurnace"));
|
||||
chatHelper.setSelected(plugin.isHelperOption("chatHelper"));
|
||||
}
|
||||
|
||||
|
||||
private static final Color READY = new Color(0x66FF66);
|
||||
private static final int[] HEIGHTS = {10, 30, 60, 100};
|
||||
private static final String[] HEIGHT_LABELS = {"Low", "Normal", "High", "Very high"};
|
||||
@@ -369,6 +402,187 @@ public class HelpersCard extends JPanel
|
||||
return item;
|
||||
}
|
||||
|
||||
private static final int[] BF_BANK_LINES = {1, 2, 3, 5};
|
||||
private static final OverlayPosition[] BF_POSITIONS = {
|
||||
OverlayPosition.TOP_LEFT, OverlayPosition.TOP_RIGHT, OverlayPosition.BOTTOM_LEFT, OverlayPosition.BOTTOM_RIGHT
|
||||
};
|
||||
private static final String[] BF_POSITION_LABELS = {"Top left", "Top right", "Bottom left", "Bottom right"};
|
||||
|
||||
/** Right-click on the Blast Furnace switch: what gets marked and which numbers the panel shows. */
|
||||
private JPopupMenu blastFurnaceMenu()
|
||||
{
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
for (String[] opt : new String[][]{
|
||||
{"blastFurnaceMarkBelt", "Highlight conveyor belt"},
|
||||
{"blastFurnaceMarkDispenser", "Highlight bar dispenser"},
|
||||
{"blastFurnaceMarkBank", "Highlight bank chest"},
|
||||
{"blastFurnaceMarkBankItems", "Highlight ores in bank"},
|
||||
{"blastFurnaceMarkItems", "Highlight ores & bars in inventory"},
|
||||
{"blastFurnaceStamina", "Drink stamina potion at <= 30 energy"},
|
||||
{"blastFurnaceLabels", "Object labels"}
|
||||
})
|
||||
{
|
||||
menu.add(toggle(opt[0], opt[1]));
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
menu.add(toggle("blastFurnacePanel", "Show info panel"));
|
||||
|
||||
JMenu lines = new JMenu("Info panel: Rows");
|
||||
for (String[] opt : new String[][]{
|
||||
{"blastFurnaceShowCoal", "Coal in furnace"},
|
||||
{"blastFurnaceShowFurnaceBars", "Bars in furnace"},
|
||||
{"blastFurnaceShowCoffer", "Coffer"},
|
||||
{"blastFurnaceShowCofferTime", "Coffer time"},
|
||||
{"blastFurnaceShowBankBars", "Bars from bank ores"}
|
||||
})
|
||||
{
|
||||
lines.add(toggle(opt[0], opt[1]));
|
||||
}
|
||||
JMenu bankLines = new JMenu("Bank rows");
|
||||
int currentLines = plugin.getHelperValue("blastFurnaceBankLines", Integer.class, 3);
|
||||
for (int value : BF_BANK_LINES)
|
||||
{
|
||||
bankLines.add(radio(String.valueOf(value), value == currentLines, () -> plugin.setHelperOption("blastFurnaceBankLines", value)));
|
||||
}
|
||||
lines.add(bankLines);
|
||||
menu.add(lines);
|
||||
|
||||
JMenu position = new JMenu("Info panel: Position");
|
||||
OverlayPosition currentPosition = plugin.getHelperValue("blastFurnacePosition", OverlayPosition.class, OverlayPosition.TOP_LEFT);
|
||||
for (int i = 0; i < BF_POSITIONS.length; i++)
|
||||
{
|
||||
OverlayPosition value = BF_POSITIONS[i];
|
||||
position.add(radio(BF_POSITION_LABELS[i], value == currentPosition, () -> plugin.setHelperOption("blastFurnacePosition", value)));
|
||||
}
|
||||
menu.add(position);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
JMenu size = new JMenu("Label text size");
|
||||
TimerTextSize currentSize = plugin.getHelperValue("blastFurnaceTextSize", TimerTextSize.class, TimerTextSize.SMALL);
|
||||
for (TimerTextSize s : TimerTextSize.values())
|
||||
{
|
||||
size.add(radio(s.toString(), s == currentSize, () -> plugin.setHelperOption("blastFurnaceTextSize", s)));
|
||||
}
|
||||
menu.add(size);
|
||||
|
||||
menu.add(colorItem("Color...", "blastFurnaceColor", "Blast Furnace highlight color", BlastFurnaceOverlay.DEFAULT_MARK_COLOR));
|
||||
menu.add(colorItem("Ready color...", "blastFurnaceReadyColor", "Blast Furnace ready color", BlastFurnaceOverlay.DEFAULT_READY_COLOR));
|
||||
|
||||
menu.addSeparator();
|
||||
JMenuItem reset = new JMenuItem("Reset appearance");
|
||||
reset.addActionListener(e ->
|
||||
{
|
||||
for (String key : new String[]{
|
||||
"blastFurnaceMarkBelt", "blastFurnaceMarkDispenser", "blastFurnaceMarkBank", "blastFurnaceMarkBankItems",
|
||||
"blastFurnaceMarkItems", "blastFurnaceStamina", "blastFurnaceLabels", "blastFurnacePanel", "blastFurnaceShowCoal",
|
||||
"blastFurnaceShowFurnaceBars", "blastFurnaceShowCoffer", "blastFurnaceShowCofferTime",
|
||||
"blastFurnaceShowBankBars", "blastFurnaceBankLines", "blastFurnacePosition", "blastFurnaceTextSize",
|
||||
"blastFurnaceColor", "blastFurnaceReadyColor"})
|
||||
{
|
||||
plugin.setHelperOption(key, null);
|
||||
}
|
||||
});
|
||||
menu.add(reset);
|
||||
return menu;
|
||||
}
|
||||
|
||||
private static final int[] CHAT_CLEAR_TIMES = {0, 30, 60, 120, 300};
|
||||
private static final String[] CHAT_CLEAR_TIME_LABELS = {"Aus (0s)", "30 Sekunden", "1 Minute (Standard)", "2 Minuten", "5 Minuten"};
|
||||
|
||||
private JPopupMenu chatMenu()
|
||||
{
|
||||
JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
boolean autoClear = plugin.getHelperValue("chatAutoClear", Boolean.class, true);
|
||||
JCheckBoxMenuItem autoClearItem = new JCheckBoxMenuItem("Automatisch aufräumen", autoClear);
|
||||
autoClearItem.addActionListener(e -> plugin.setHelperOption("chatAutoClear", !autoClear));
|
||||
menu.add(autoClearItem);
|
||||
|
||||
JMenu clearTimeMenu = new JMenu("Auto-Clear Zeit");
|
||||
int currentTime = plugin.getHelperValue("chatAutoClearTime", Integer.class, 60);
|
||||
for (int i = 0; i < CHAT_CLEAR_TIMES.length; i++)
|
||||
{
|
||||
int time = CHAT_CLEAR_TIMES[i];
|
||||
clearTimeMenu.add(radio(CHAT_CLEAR_TIME_LABELS[i], time == currentTime, () -> plugin.setHelperOption("chatAutoClearTime", time)));
|
||||
}
|
||||
menu.add(clearTimeMenu);
|
||||
|
||||
JMenu typesMenu = new JMenu("Was wird aufgeräumt?");
|
||||
for (String[] opt : new String[][]{
|
||||
{"chatClearGame", "Spiel- & System-Meldungen", "true"},
|
||||
{"chatClearPublic", "Öffentlicher Chat (Public)", "true"},
|
||||
{"chatClearPrivate", "Private Nachrichten (PM)", "false"},
|
||||
{"chatClearClan", "Clan- & Freunde-Chat", "false"}
|
||||
})
|
||||
{
|
||||
boolean def = Boolean.parseBoolean(opt[2]);
|
||||
boolean on = plugin.getHelperValue(opt[0], Boolean.class, def);
|
||||
JCheckBoxMenuItem item = new JCheckBoxMenuItem(opt[1], on);
|
||||
item.addActionListener(e -> plugin.setHelperOption(opt[0], !on));
|
||||
typesMenu.add(item);
|
||||
}
|
||||
menu.add(typesMenu);
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
for (String[] opt : new String[][]{
|
||||
{"chatCollapseDuplicates", "Duplikate zusammenfassen (x2, x3...)"},
|
||||
{"chatFilterSpam", "Spam & Systemlärm filtern"},
|
||||
{"chatHighlightName", "Eigenen Namen hervorheben"},
|
||||
{"chatCopyToClipboard", "Rechtsklick 'Kopieren' aktivieren"}
|
||||
})
|
||||
{
|
||||
boolean on = plugin.getHelperValue(opt[0], Boolean.class, true);
|
||||
JCheckBoxMenuItem item = new JCheckBoxMenuItem(opt[1], on);
|
||||
item.addActionListener(e -> plugin.setHelperOption(opt[0], !on));
|
||||
menu.add(item);
|
||||
}
|
||||
|
||||
menu.add(colorItem("Highlight-Farbe...", "chatHighlightColor", "Chat Highlight-Farbe", new Color(0xFFD700)));
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
JMenuItem clearNow = new JMenuItem("Chat jetzt leeren");
|
||||
clearNow.addActionListener(e ->
|
||||
{
|
||||
if (plugin.getChatHelper() != null)
|
||||
{
|
||||
plugin.getChatHelper().clearAllChat();
|
||||
}
|
||||
});
|
||||
menu.add(clearNow);
|
||||
|
||||
JMenuItem reset = new JMenuItem("Einstellungen zurücksetzen");
|
||||
reset.addActionListener(e ->
|
||||
{
|
||||
for (String key : new String[]{
|
||||
"chatAutoClear", "chatAutoClearTime", "chatClearGame", "chatClearPublic",
|
||||
"chatClearPrivate", "chatClearClan", "chatCollapseDuplicates",
|
||||
"chatFilterSpam", "chatHighlightName", "chatHighlightColor", "chatCopyToClipboard"
|
||||
})
|
||||
{
|
||||
plugin.setHelperOption(key, null);
|
||||
}
|
||||
});
|
||||
menu.add(reset);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
|
||||
/** Checkbox for a switch that is on by default (turning it on stores the default explicitly). */
|
||||
private JCheckBoxMenuItem toggle(String key, String label)
|
||||
{
|
||||
boolean on = plugin.getHelperValue(key, Boolean.class, true);
|
||||
JCheckBoxMenuItem item = new JCheckBoxMenuItem(label, on);
|
||||
item.addActionListener(e -> plugin.setHelperOption(key, !on));
|
||||
return item;
|
||||
}
|
||||
|
||||
private JMenuItem colorItem(String label, String key, String title, Color def)
|
||||
{
|
||||
JMenuItem item = new JMenuItem(label);
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
package com.example.essentialsplus.helpers;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import net.runelite.api.gameval.ItemID;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BlastFurnaceTest
|
||||
{
|
||||
private static Map<Integer, Integer> ores(Object... pairs)
|
||||
{
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
for (int i = 0; i + 1 < pairs.length; i += 2)
|
||||
{
|
||||
map.put((Integer) pairs[i], (Integer) pairs[i + 1]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static BlastFurnace.Estimate estimate(BlastFurnace.BarType type, int coal, Object... pairs)
|
||||
{
|
||||
return BlastFurnace.estimate(type, ores(pairs), coal);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCoalCostsAreTheBlastFurnaceOnes()
|
||||
{
|
||||
// the Blast Furnace needs half the coal of a normal furnace
|
||||
assertEquals(0, BlastFurnace.BarType.IRON.getCoalPerBar());
|
||||
assertEquals(1, BlastFurnace.BarType.STEEL.getCoalPerBar());
|
||||
assertEquals(2, BlastFurnace.BarType.MITHRIL.getCoalPerBar());
|
||||
assertEquals(3, BlastFurnace.BarType.ADAMANTITE.getCoalPerBar());
|
||||
assertEquals(4, BlastFurnace.BarType.RUNITE.getCoalPerBar());
|
||||
assertEquals(0, BlastFurnace.BarType.GOLD.getCoalPerBar());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSteelBarsFromIronAndCoal()
|
||||
{
|
||||
BlastFurnace.Estimate e = estimate(BlastFurnace.BarType.STEEL, 5_000, ItemID.IRON_ORE, 1_000, ItemID.COAL, 5_000);
|
||||
|
||||
assertEquals(1_000, e.bars);
|
||||
assertFalse(e.coalLimited);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSteelBarsAreLimitedByCoal()
|
||||
{
|
||||
BlastFurnace.Estimate e = estimate(BlastFurnace.BarType.STEEL, 250, ItemID.IRON_ORE, 1_000);
|
||||
|
||||
assertEquals(250, e.bars);
|
||||
assertTrue(e.coalLimited);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIronBarsNeedNoCoal()
|
||||
{
|
||||
BlastFurnace.Estimate e = estimate(BlastFurnace.BarType.IRON, 0, ItemID.IRON_ORE, 742);
|
||||
|
||||
assertEquals(742, e.bars);
|
||||
assertFalse(e.coalLimited);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMithrilBarsUseTwoCoalPerBar()
|
||||
{
|
||||
assertEquals(75, estimate(BlastFurnace.BarType.MITHRIL, 150, ItemID.MITHRIL_ORE, 100).bars);
|
||||
assertEquals(100, estimate(BlastFurnace.BarType.MITHRIL, 200, ItemID.MITHRIL_ORE, 100).bars);
|
||||
assertTrue(estimate(BlastFurnace.BarType.MITHRIL, 150, ItemID.MITHRIL_ORE, 100).coalLimited);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingOreMeansNoBars()
|
||||
{
|
||||
assertEquals(0, estimate(BlastFurnace.BarType.RUNITE, 10_000, ItemID.ADAMANTITE_ORE, 500).bars);
|
||||
assertEquals(0, estimate(BlastFurnace.BarType.BRONZE, 10_000, ItemID.COPPER_ORE, 100).bars);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOresNeedingMoreThanOneItem()
|
||||
{
|
||||
// bronze: 1 copper + 1 tin
|
||||
assertEquals(4, estimate(BlastFurnace.BarType.BRONZE, 0, ItemID.COPPER_ORE, 10, ItemID.TIN_ORE, 4).bars);
|
||||
|
||||
// lead: 2 lead ore per bar
|
||||
assertEquals(4, estimate(BlastFurnace.BarType.LEAD, 0, ItemID.LEAD_ORE, 9).bars);
|
||||
|
||||
// cupronickel: 2 copper + 1 nickel per bar
|
||||
assertEquals(3, estimate(BlastFurnace.BarType.CUPRONICKEL, 0, ItemID.COPPER_ORE, 10, ItemID.NICKEL_ORE, 3).bars);
|
||||
assertEquals(5, estimate(BlastFurnace.BarType.CUPRONICKEL, 0, ItemID.COPPER_ORE, 10, ItemID.NICKEL_ORE, 9).bars);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNegativeAmountsAreClamped()
|
||||
{
|
||||
assertEquals(0, estimate(BlastFurnace.BarType.STEEL, -20, ItemID.IRON_ORE, 100).bars);
|
||||
assertEquals(0, estimate(BlastFurnace.BarType.STEEL, 100, ItemID.IRON_ORE, -5).bars);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCofferTimeIsFiftyMillisecondsPerGp()
|
||||
{
|
||||
assertEquals(3_600_000L, BlastFurnace.cofferMillis(72_000));
|
||||
assertEquals(0L, BlastFurnace.cofferMillis(0));
|
||||
assertEquals(0L, BlastFurnace.cofferMillis(-1_000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOreAndBarLookup()
|
||||
{
|
||||
assertTrue(BlastFurnace.isOre(ItemID.RUNITE_ORE));
|
||||
assertTrue(BlastFurnace.isOre(ItemID.COAL));
|
||||
assertFalse(BlastFurnace.isOre(ItemID.RUNITE_BAR));
|
||||
|
||||
assertTrue(BlastFurnace.isBar(ItemID.RUNITE_BAR));
|
||||
assertFalse(BlastFurnace.isBar(ItemID.RUNITE_ORE));
|
||||
assertEquals(BlastFurnace.BarType.RUNITE, BlastFurnace.barTypeOf(ItemID.RUNITE_BAR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiredFurnaceCoalFormula()
|
||||
{
|
||||
// Runite: 4 coal per bar. With coal bag + buffer: 4x ore slots = 4 * 27 = 108
|
||||
assertEquals(108, BlastFurnace.getRequiredFurnaceCoal(BlastFurnace.BarType.RUNITE, 27, true));
|
||||
// Runite without coal bag: 5 * 27 = 135
|
||||
assertEquals(135, BlastFurnace.getRequiredFurnaceCoal(BlastFurnace.BarType.RUNITE, 27, false));
|
||||
|
||||
// Adamantite: 3 coal per bar. With coal bag + buffer: 3 * 27 = 81
|
||||
assertEquals(81, BlastFurnace.getRequiredFurnaceCoal(BlastFurnace.BarType.ADAMANTITE, 27, true));
|
||||
assertEquals(108, BlastFurnace.getRequiredFurnaceCoal(BlastFurnace.BarType.ADAMANTITE, 27, false));
|
||||
|
||||
// Mithril: 2 coal per bar. With coal bag + buffer: 2 * 27 = 54
|
||||
assertEquals(54, BlastFurnace.getRequiredFurnaceCoal(BlastFurnace.BarType.MITHRIL, 27, true));
|
||||
|
||||
// Steel: 1 coal per bar. With coal bag + buffer: 1 * 27 = 27
|
||||
assertEquals(27, BlastFurnace.getRequiredFurnaceCoal(BlastFurnace.BarType.STEEL, 27, true));
|
||||
assertEquals(54, BlastFurnace.getRequiredFurnaceCoal(BlastFurnace.BarType.STEEL, 27, false));
|
||||
|
||||
// Gold: 0 coal
|
||||
assertEquals(0, BlastFurnace.getRequiredFurnaceCoal(BlastFurnace.BarType.GOLD, 27, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTargetBankOrePriorityRuniteFirstThenAdamantite()
|
||||
{
|
||||
Map<Integer, Integer> bank = new HashMap<>();
|
||||
bank.put(ItemID.RUNITE_ORE, 100);
|
||||
bank.put(ItemID.ADAMANTITE_ORE, 500);
|
||||
bank.put(ItemID.MITHRIL_ORE, 1000);
|
||||
bank.put(ItemID.COAL, 5000);
|
||||
|
||||
// With 99 Smithing, coal bag, and 120 coal in furnace (>= 108) -> Runite Ore
|
||||
assertEquals(ItemID.RUNITE_ORE, BlastFurnace.pickTargetBankOre(99, 120, 27, true, id -> bank.getOrDefault(id, 0)));
|
||||
|
||||
// If furnace has only 80 coal (< 108 needed for Runite) -> Coal!
|
||||
assertEquals(ItemID.COAL, BlastFurnace.pickTargetBankOre(99, 80, 27, true, id -> bank.getOrDefault(id, 0)));
|
||||
|
||||
// If no Runite present, but 90+ coal in furnace (>= 81 for Adamantite) -> Adamantite
|
||||
bank.remove(ItemID.RUNITE_ORE);
|
||||
assertEquals(ItemID.ADAMANTITE_ORE, BlastFurnace.pickTargetBankOre(99, 90, 27, true, id -> bank.getOrDefault(id, 0)));
|
||||
|
||||
// If no Runite present, and only 60 coal in furnace (< 81 for Adamantite) -> Coal!
|
||||
assertEquals(ItemID.COAL, BlastFurnace.pickTargetBankOre(99, 60, 27, true, id -> bank.getOrDefault(id, 0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTargetBankOreRespectsSmithingLevel()
|
||||
{
|
||||
Map<Integer, Integer> bank = new HashMap<>();
|
||||
bank.put(ItemID.RUNITE_ORE, 100);
|
||||
bank.put(ItemID.ADAMANTITE_ORE, 500);
|
||||
bank.put(ItemID.MITHRIL_ORE, 1000);
|
||||
bank.put(ItemID.COAL, 5000);
|
||||
|
||||
// At level 75 (cannot smelt Runite 85), should pick Adamantite when furnace has 90 coal (>= 81)
|
||||
assertEquals(ItemID.ADAMANTITE_ORE, BlastFurnace.pickTargetBankOre(75, 90, 27, true, id -> bank.getOrDefault(id, 0)));
|
||||
|
||||
// At level 55 (cannot smelt Adamantite 70), should pick Mithril when furnace has 60 coal (>= 54)
|
||||
assertEquals(ItemID.MITHRIL_ORE, BlastFurnace.pickTargetBankOre(55, 60, 27, true, id -> bank.getOrDefault(id, 0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaminaAndVialItemIds()
|
||||
{
|
||||
assertTrue(ItemID.VIAL_EMPTY > 0);
|
||||
assertTrue(ItemID._1DOSESTAMINA > 0);
|
||||
assertTrue(ItemID._2DOSESTAMINA > 0);
|
||||
assertTrue(ItemID._3DOSESTAMINA > 0);
|
||||
assertTrue(ItemID._4DOSESTAMINA > 0);
|
||||
assertTrue(ItemID._1DOSE2STAMINA > 0);
|
||||
assertTrue(ItemID._2DOSE2STAMINA > 0);
|
||||
assertTrue(ItemID._3DOSE2STAMINA > 0);
|
||||
assertTrue(ItemID._4DOSE2STAMINA > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaminaPotionRankingPrefersOneDoseAndExtended()
|
||||
{
|
||||
// 1-dose extended is #1
|
||||
assertEquals(ItemID._1DOSE2STAMINA, BlastFurnace.STAMINA_POTION_PRIORITY[0]);
|
||||
// 1-dose normal is #2 (1-dose prioritized)
|
||||
assertEquals(ItemID._1DOSESTAMINA, BlastFurnace.STAMINA_POTION_PRIORITY[1]);
|
||||
// Extended 2-4 before normal 2-4
|
||||
assertEquals(ItemID._2DOSE2STAMINA, BlastFurnace.STAMINA_POTION_PRIORITY[2]);
|
||||
assertEquals(ItemID._3DOSE2STAMINA, BlastFurnace.STAMINA_POTION_PRIORITY[3]);
|
||||
assertEquals(ItemID._4DOSE2STAMINA, BlastFurnace.STAMINA_POTION_PRIORITY[4]);
|
||||
assertEquals(ItemID._2DOSESTAMINA, BlastFurnace.STAMINA_POTION_PRIORITY[5]);
|
||||
assertEquals(ItemID._3DOSESTAMINA, BlastFurnace.STAMINA_POTION_PRIORITY[6]);
|
||||
assertEquals(ItemID._4DOSESTAMINA, BlastFurnace.STAMINA_POTION_PRIORITY[7]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinimumOreBatchOf27()
|
||||
{
|
||||
Map<Integer, Integer> bank = new HashMap<>();
|
||||
bank.put(ItemID.RUNITE_ORE, 26); // Less than 27 -> not used!
|
||||
bank.put(ItemID.ADAMANTITE_ORE, 50); // >= 27 -> used!
|
||||
bank.put(ItemID.COAL, 5000);
|
||||
|
||||
// With 26 Runite ore, Runite is skipped, falling back to Adamantite
|
||||
assertEquals(BlastFurnace.BarType.ADAMANTITE, BlastFurnace.pickTargetBarType(99, id -> bank.getOrDefault(id, 0)));
|
||||
assertEquals(ItemID.ADAMANTITE_ORE, BlastFurnace.pickTargetBankOre(99, 90, 27, true, id -> bank.getOrDefault(id, 0)));
|
||||
|
||||
// If Runite has exactly 27, it IS used
|
||||
bank.put(ItemID.RUNITE_ORE, 27);
|
||||
assertEquals(BlastFurnace.BarType.RUNITE, BlastFurnace.pickTargetBarType(99, id -> bank.getOrDefault(id, 0)));
|
||||
assertEquals(ItemID.RUNITE_ORE, BlastFurnace.pickTargetBankOre(99, 120, 27, true, id -> bank.getOrDefault(id, 0)));
|
||||
|
||||
// If all ores have < 27, finished! (null / -1)
|
||||
bank.put(ItemID.RUNITE_ORE, 26);
|
||||
bank.put(ItemID.ADAMANTITE_ORE, 15);
|
||||
bank.put(ItemID.MITHRIL_ORE, 20);
|
||||
org.junit.Assert.assertNull(BlastFurnace.pickTargetBarType(99, id -> bank.getOrDefault(id, 0)));
|
||||
assertEquals(-1, BlastFurnace.pickTargetBankOre(99, 120, 27, true, id -> bank.getOrDefault(id, 0)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
package com.example.essentialsplus.helpers;
|
||||
|
||||
import com.example.essentialsplus.EssentialsPlusConfig;
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.runelite.api.ChatLineBuffer;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.IterableHashTable;
|
||||
import net.runelite.api.MessageNode;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.ScriptCallbackEvent;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ChatHelperTest
|
||||
{
|
||||
private Client client;
|
||||
private EssentialsPlusConfig config;
|
||||
private ClientThread clientThread;
|
||||
private ChatHelper chatHelper;
|
||||
|
||||
private final Map<Integer, ChatLineBuffer> chatLineMap = new HashMap<>();
|
||||
private final List<Object[]> scriptsRun = new ArrayList<>();
|
||||
private final Map<String, Object> configValues = new HashMap<>();
|
||||
private final Map<Long, MessageNode> messagesMap = new HashMap<>();
|
||||
private int[] intStack;
|
||||
private Object[] objStack;
|
||||
private Player localPlayer;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> T mockInterface(Class<T> iface, java.lang.reflect.InvocationHandler handler)
|
||||
{
|
||||
return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class<?>[]{iface}, handler);
|
||||
}
|
||||
|
||||
private MessageNode createMessageNode(int id, ChatMessageType type, String name, String value, int timestamp)
|
||||
{
|
||||
final String[] valRef = new String[]{value};
|
||||
final int[] tsRef = new int[]{timestamp};
|
||||
|
||||
return mockInterface(MessageNode.class, (proxy, method, args) ->
|
||||
{
|
||||
switch (method.getName())
|
||||
{
|
||||
case "getId":
|
||||
return id;
|
||||
case "getType":
|
||||
return type;
|
||||
case "getName":
|
||||
return name;
|
||||
case "getValue":
|
||||
return valRef[0];
|
||||
case "setValue":
|
||||
valRef[0] = (String) args[0];
|
||||
return null;
|
||||
case "getTimestamp":
|
||||
return tsRef[0];
|
||||
case "setTimestamp":
|
||||
tsRef[0] = (Integer) args[0];
|
||||
return null;
|
||||
case "equals":
|
||||
return proxy == args[0];
|
||||
case "hashCode":
|
||||
return System.identityHashCode(proxy);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ChatLineBuffer createChatLineBuffer(List<MessageNode> initialLines)
|
||||
{
|
||||
List<MessageNode> list = new ArrayList<>(initialLines);
|
||||
return mockInterface(ChatLineBuffer.class, (proxy, method, args) ->
|
||||
{
|
||||
switch (method.getName())
|
||||
{
|
||||
case "getLines":
|
||||
return list.toArray(new MessageNode[0]);
|
||||
case "getLength":
|
||||
return list.size();
|
||||
case "removeMessageNode":
|
||||
list.remove(args[0]);
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
chatLineMap.clear();
|
||||
scriptsRun.clear();
|
||||
configValues.clear();
|
||||
messagesMap.clear();
|
||||
|
||||
// Default config values
|
||||
configValues.put("chatHelper", true);
|
||||
configValues.put("chatAutoClear", true);
|
||||
configValues.put("chatAutoClearTime", 60);
|
||||
configValues.put("chatClearGame", true);
|
||||
configValues.put("chatClearPublic", true);
|
||||
configValues.put("chatClearPrivate", false);
|
||||
configValues.put("chatClearClan", false);
|
||||
configValues.put("chatCollapseDuplicates", true);
|
||||
configValues.put("chatFilterSpam", true);
|
||||
configValues.put("chatHighlightName", true);
|
||||
configValues.put("chatHighlightColor", new Color(0xFFD700));
|
||||
configValues.put("chatCopyToClipboard", true);
|
||||
|
||||
config = mockInterface(EssentialsPlusConfig.class, (proxy, method, args) ->
|
||||
{
|
||||
if (configValues.containsKey(method.getName()))
|
||||
{
|
||||
return configValues.get(method.getName());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
localPlayer = mockInterface(Player.class, (proxy, method, args) ->
|
||||
{
|
||||
if ("getName".equals(method.getName()))
|
||||
{
|
||||
return "Zezima";
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
clientThread = new ClientThread()
|
||||
{
|
||||
@Override
|
||||
public void invoke(Runnable r)
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeLater(Runnable r)
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
};
|
||||
|
||||
client = mockInterface(Client.class, (proxy, method, args) ->
|
||||
{
|
||||
switch (method.getName())
|
||||
{
|
||||
case "getChatLineMap":
|
||||
return chatLineMap;
|
||||
case "runScript":
|
||||
scriptsRun.add((Object[]) args);
|
||||
return null;
|
||||
case "getLocalPlayer":
|
||||
return localPlayer;
|
||||
case "getIntStack":
|
||||
return intStack;
|
||||
case "getIntStackSize":
|
||||
return intStack != null ? intStack.length : 0;
|
||||
case "getObjectStack":
|
||||
return objStack;
|
||||
case "getObjectStackSize":
|
||||
return objStack != null ? objStack.length : 0;
|
||||
case "getMessages":
|
||||
return mockInterface(IterableHashTable.class, (p2, m2, a2) ->
|
||||
{
|
||||
if ("get".equals(m2.getName()))
|
||||
{
|
||||
return messagesMap.get((Long) a2[0]);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
chatHelper = new ChatHelper(client, config, clientThread);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAutoClearOldMessages()
|
||||
{
|
||||
int now = (int) Instant.now().getEpochSecond();
|
||||
MessageNode oldNode = createMessageNode(1, ChatMessageType.GAMEMESSAGE, "", "You cut a willow log.", now - 65);
|
||||
MessageNode recentNode = createMessageNode(2, ChatMessageType.GAMEMESSAGE, "", "You cut an oak log.", now - 10);
|
||||
|
||||
List<MessageNode> bufferList = new ArrayList<>();
|
||||
bufferList.add(oldNode);
|
||||
bufferList.add(recentNode);
|
||||
chatLineMap.put(0, createChatLineBuffer(bufferList));
|
||||
|
||||
chatHelper.onGameTick(new GameTick());
|
||||
|
||||
ChatLineBuffer buf = chatLineMap.get(0);
|
||||
Assert.assertEquals(1, buf.getLines().length);
|
||||
Assert.assertEquals("You cut an oak log.", buf.getLines()[0].getValue());
|
||||
Assert.assertFalse(scriptsRun.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrivateMessageProtection()
|
||||
{
|
||||
int now = (int) Instant.now().getEpochSecond();
|
||||
MessageNode pmNode = createMessageNode(1, ChatMessageType.PRIVATECHAT, "Friend", "Meet me at GE", now - 120);
|
||||
|
||||
List<MessageNode> bufferList = new ArrayList<>();
|
||||
bufferList.add(pmNode);
|
||||
chatLineMap.put(1, createChatLineBuffer(bufferList));
|
||||
|
||||
chatHelper.onGameTick(new GameTick());
|
||||
|
||||
// Private messages should NOT be cleared because chatClearPrivate is false
|
||||
ChatLineBuffer buf = chatLineMap.get(1);
|
||||
Assert.assertEquals(1, buf.getLines().length);
|
||||
Assert.assertEquals("Meet me at GE", buf.getLines()[0].getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpamFilter()
|
||||
{
|
||||
intStack = new int[]{1, ChatMessageType.GAMEMESSAGE.getType(), 10};
|
||||
objStack = new Object[]{"Nothing interesting happens."};
|
||||
|
||||
ScriptCallbackEvent event = new ScriptCallbackEvent();
|
||||
event.setEventName("chatFilterCheck");
|
||||
chatHelper.onScriptCallbackEvent(event);
|
||||
|
||||
// intStack[0] (display flag) should be set to 0 (hidden)
|
||||
Assert.assertEquals(0, intStack[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNameHighlight()
|
||||
{
|
||||
intStack = new int[]{1, ChatMessageType.PUBLICCHAT.getType(), 20};
|
||||
objStack = new Object[]{"Hey Zezima where are you going?"};
|
||||
|
||||
ScriptCallbackEvent event = new ScriptCallbackEvent();
|
||||
event.setEventName("chatFilterCheck");
|
||||
chatHelper.onScriptCallbackEvent(event);
|
||||
|
||||
String result = (String) objStack[0];
|
||||
Assert.assertTrue(result.contains("<col=ffd700><b>Zezima</b></col>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearAllChat()
|
||||
{
|
||||
int now = (int) Instant.now().getEpochSecond();
|
||||
MessageNode node1 = createMessageNode(1, ChatMessageType.GAMEMESSAGE, "", "msg1", now);
|
||||
MessageNode node2 = createMessageNode(2, ChatMessageType.PUBLICCHAT, "p1", "msg2", now);
|
||||
|
||||
List<MessageNode> list = new ArrayList<>();
|
||||
list.add(node1);
|
||||
list.add(node2);
|
||||
chatLineMap.put(0, createChatLineBuffer(list));
|
||||
|
||||
chatHelper.clearAllChat();
|
||||
|
||||
Assert.assertEquals(0, chatLineMap.get(0).getLines().length);
|
||||
Assert.assertFalse(scriptsRun.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDuplicateCollapsing()
|
||||
{
|
||||
MessageNode node1 = createMessageNode(101, ChatMessageType.GAMEMESSAGE, "", "You catch a trout.", 1000);
|
||||
MessageNode node2 = createMessageNode(102, ChatMessageType.GAMEMESSAGE, "", "You catch a trout.", 1001);
|
||||
messagesMap.put(101L, node1);
|
||||
messagesMap.put(102L, node2);
|
||||
|
||||
ChatMessage chatEvent1 = new ChatMessage(node1, ChatMessageType.GAMEMESSAGE, "", "You catch a trout.", "", 1000);
|
||||
ChatMessage chatEvent2 = new ChatMessage(node2, ChatMessageType.GAMEMESSAGE, "", "You catch a trout.", "", 1001);
|
||||
|
||||
chatHelper.onChatMessage(chatEvent1);
|
||||
chatHelper.onChatMessage(chatEvent2);
|
||||
|
||||
// Check earlier duplicate
|
||||
intStack = new int[]{1, ChatMessageType.GAMEMESSAGE.getType(), 101};
|
||||
objStack = new Object[]{"You catch a trout."};
|
||||
ScriptCallbackEvent sc1 = new ScriptCallbackEvent();
|
||||
sc1.setEventName("chatFilterCheck");
|
||||
chatHelper.onScriptCallbackEvent(sc1);
|
||||
Assert.assertEquals(0, intStack[0]);
|
||||
|
||||
// Check latest duplicate
|
||||
intStack = new int[]{1, ChatMessageType.GAMEMESSAGE.getType(), 102};
|
||||
objStack = new Object[]{"You catch a trout."};
|
||||
ScriptCallbackEvent sc2 = new ScriptCallbackEvent();
|
||||
sc2.setEventName("chatFilterCheck");
|
||||
chatHelper.onScriptCallbackEvent(sc2);
|
||||
Assert.assertEquals(1, intStack[0]);
|
||||
Assert.assertTrue(((String) objStack[0]).contains("(x2)"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user