Merge pull request #5 from Ganom/master

fork: rebase.
This commit is contained in:
Ganom
2019-11-07 02:34:34 -05:00
committed by GitHub
712 changed files with 53505 additions and 52997 deletions

View File

@@ -58,7 +58,7 @@ public class ClientSessionManager
}
@Schedule(period = 10, unit = ChronoUnit.MINUTES, asynchronous = true)
private void ping()
public void ping()
{
if (sessionId == null)
{

View File

@@ -59,6 +59,7 @@ import net.runelite.client.eventbus.EventBus;
import net.runelite.client.game.ClanManager;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.LootManager;
import net.runelite.client.game.XpDropManager;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.graphics.ModelOutlineRenderer;
import net.runelite.client.menus.MenuManager;
@@ -151,6 +152,9 @@ public class RuneLite
@Inject
private Provider<LootManager> lootManager;
@Inject
private Provider<XpDropManager> xpDropManager;
@Inject
private Provider<ChatboxPanelManager> chatboxPanelManager;
@@ -323,20 +327,19 @@ public class RuneLite
}
// Load user configuration
RuneLiteSplashScreen.stage(.57, "Loading user config");
configManager.load();
// Load the session, including saved configuration
sessionManager.loadSession();
RuneLiteSplashScreen.stage(.58, "Loading session data");
// Begin watching for new plugins
pluginManager.watch();
sessionManager.loadSession();
// Tell the plugin manager if client is outdated or not
pluginManager.setOutdated(isOutdated);
// Load external plugins
pluginManager.loadExternalPlugins();
// Load the plugins, but does not start them yet.
// This will initialize configuration
pluginManager.loadCorePlugins();
@@ -369,6 +372,7 @@ public class RuneLite
chatMessageManager.get();
commandManager.get();
lootManager.get();
xpDropManager.get();
chatboxPanelManager.get();
eventBus.subscribe(GameStateChanged.class, this, hooks::onGameStateChanged);

View File

@@ -24,8 +24,8 @@
*/
package net.runelite.client.chat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
@@ -42,7 +42,7 @@ import net.runelite.client.events.PrivateMessageInput;
@Singleton
public class ChatCommandManager implements ChatboxInputListener
{
private final Map<String, ChatCommand> commands = new HashMap<>();
private final Map<String, ChatCommand> commands = new ConcurrentHashMap<>();
private final Client client;
private final ScheduledExecutorService scheduledExecutorService;

View File

@@ -558,16 +558,9 @@ public class ChatMessageManager
public void process()
{
if (!queuedMessages.isEmpty())
for (QueuedMessage msg; (msg = queuedMessages.poll()) != null; )
{
try
{
queuedMessages.forEach(this::add);
}
finally
{
queuedMessages.clear();
}
add(msg);
}
}

View File

@@ -25,9 +25,9 @@
*/
package net.runelite.client.chat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
@@ -54,7 +54,7 @@ public class CommandManager
private final ClientThread clientThread;
private boolean sending;
private final List<ChatboxInputListener> chatboxInputListenerList = new ArrayList<>();
private final List<ChatboxInputListener> chatboxInputListenerList = new CopyOnWriteArrayList<>();
@Inject
private CommandManager(

View File

@@ -164,7 +164,7 @@ public interface ChatColorConfig extends Config
position = 45,
keyName = "opaqueServerMessage",
name = "Server message",
description = "Color of Server Messages (eg. 'Welcome to Runescape')"
description = "Color of Server Messages (eg. 'Welcome to RuneScape')"
)
Color opaqueServerMessage();
@@ -404,7 +404,7 @@ public interface ChatColorConfig extends Config
position = 75,
keyName = "transparentServerMessage",
name = "Server message (transparent)",
description = "Color of Server Messages (eg. 'Welcome to Runescape') (transparent)"
description = "Color of Server Messages (eg. 'Welcome to RuneScape') (transparent)"
)
Color transparentServerMessage();

View File

@@ -26,9 +26,30 @@
*/
package net.runelite.client.config;
import java.awt.Color;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ConfigGroup("openosrs")
public interface OpenOSRSConfig extends Config
{
@Getter(AccessLevel.PUBLIC)
@AllArgsConstructor
enum SortStyle
{
CATEGORY("Category"),
ALPHABETICALLY("Alphabetically");
private String name;
@Override
public String toString()
{
return getName();
}
}
@ConfigTitleSection(
keyName = "pluginsTitle",
name = "Plugins",
@@ -40,12 +61,215 @@ public interface OpenOSRSConfig extends Config
return new Title();
}
@ConfigTitleSection(
keyName = "pluginSortingTitle",
name = "Sorting",
description = "",
position = 2,
titleSection = "pluginsTitle"
)
default Title pluginSortingTitle()
{
return new Title();
}
@ConfigItem(
position = 3,
keyName = "pluginSortMode",
name = "Sorting mode",
description = "Sorts plugins ",
titleSection = "pluginSortingTitle"
)
default SortStyle pluginSortMode()
{
return SortStyle.CATEGORY;
}
@ConfigTitleSection(
keyName = "hidePluginsTitle",
name = "Hide By Type",
description = "",
position = 4,
titleSection = "pluginsTitle"
)
default Title hidePluginsTitle()
{
return new Title();
}
@ConfigItem(
position = 5,
keyName = "hidePlugins",
name = "Hide All Plugins",
description = "Hides all OpenOSRS plugins if checked",
titleSection = "hidePluginsTitle",
hide = "hidePvmPlugins || hidePvpPlugins || hideSkillingPlugins || hideUtilityPlugins || hideExternalPlugins"
)
default boolean hidePlugins()
{
return false;
}
@ConfigItem(
position = 6,
keyName = "hideExternalPlugins",
name = "Hide External Plugins",
description = "Hides all OpenOSRS external plugins if checked",
titleSection = "hidePluginsTitle",
hide = "hidePlugins"
)
default boolean hideExternalPlugins()
{
return false;
}
@ConfigItem(
position = 7,
keyName = "hidePvmPlugins",
name = "Hide PvM Plugins",
description = "Hides all OpenOSRS PvM plugins if checked",
titleSection = "hidePluginsTitle",
hide = "hidePlugins"
)
default boolean hidePvmPlugins()
{
return false;
}
@ConfigItem(
position = 8,
keyName = "hideSkillingPlugins",
name = "Hide Skilling Plugins",
description = "Hides all OpenOSRS skilling plugins if checked",
titleSection = "hidePluginsTitle",
hide = "hidePlugins"
)
default boolean hideSkillingPlugins()
{
return false;
}
@ConfigItem(
position = 9,
keyName = "hidePvpPlugins",
name = "Hide PvP Plugins",
description = "Hides all OpenOSRS Pvp plugins if checked",
titleSection = "hidePluginsTitle",
hide = "hidePlugins"
)
default boolean hidePvpPlugins()
{
return false;
}
@ConfigItem(
position = 10,
keyName = "hideUtilityPlugins",
name = "Hide Utility Plugins",
description = "Hides all OpenOSRS utility plugins if checked",
titleSection = "hidePluginsTitle",
hide = "hidePlugins"
)
default boolean hideUtilityPlugins()
{
return false;
}
@ConfigTitleSection(
keyName = "pluginsColorTitle",
name = "Colors",
description = "",
position = 11,
titleSection = "pluginsTitle"
)
default Title pluginsColorTitle()
{
return new Title();
}
@Alpha
@ConfigItem(
position = 12,
keyName = "externalColor",
name = "External color",
description = "Configure the color of external plugins",
titleSection = "pluginsColorTitle"
)
default Color externalColor()
{
return new Color(177, 156, 217, 255);
}
@Alpha
@ConfigItem(
position = 13,
keyName = "pvmColor",
name = "PVM color",
description = "Configure the color of PVM related plugins",
titleSection = "pluginsColorTitle"
)
default Color pvmColor()
{
return new Color(119, 221, 119, 255);
}
@Alpha
@ConfigItem(
position = 14,
keyName = "pvpColor",
name = "PVP color",
description = "Configure the color of PVP related plugins",
titleSection = "pluginsColorTitle"
)
default Color pvpColor()
{
return new Color(255, 105, 97, 255);
}
@Alpha
@ConfigItem(
position = 15,
keyName = "skillingColor",
name = "Skilling color",
description = "Configure the color of Skilling related plugins",
titleSection = "pluginsColorTitle"
)
default Color skillingColor()
{
return new Color(252, 252, 100, 255);
}
@Alpha
@ConfigItem(
position = 16,
keyName = "utilityColor",
name = "Utility color",
description = "Configure the color of Utility related plugins",
titleSection = "pluginsColorTitle"
)
default Color utilityColor()
{
return new Color(144, 212, 237, 255);
}
@ConfigTitleSection(
keyName = "externalPluginsTitle",
name = "External",
description = "",
position = 17,
titleSection = "pluginsTitle"
)
default Title externalPluginsTitle()
{
return new Title();
}
@ConfigItem(
keyName = "enablePlugins",
name = "Enable loading of external plugins",
description = "Enable loading of external plugins",
position = 2,
titleSection = "pluginsTitle"
position = 18,
titleSection = "externalPluginsTitle"
)
default boolean enablePlugins()
{
@@ -56,7 +280,7 @@ public interface OpenOSRSConfig extends Config
keyName = "opacityTitle",
name = "Opacity",
description = "",
position = 3
position = 19
)
default Title opacityTitle()
{
@@ -67,7 +291,7 @@ public interface OpenOSRSConfig extends Config
keyName = "enableOpacity",
name = "Enable opacity",
description = "Enables opacity for the whole window.<br>NOTE: This only stays enabled if your pc supports this!",
position = 4,
position = 20,
titleSection = "opacityTitle"
)
default boolean enableOpacity()
@@ -83,7 +307,7 @@ public interface OpenOSRSConfig extends Config
keyName = "opacityPercentage",
name = "Opacity percentage",
description = "Changes the opacity of the window if opacity is enabled",
position = 5,
position = 21,
titleSection = "opacityTitle"
)
default int opacityPercentage()
@@ -95,7 +319,7 @@ public interface OpenOSRSConfig extends Config
keyName = "miscTitle",
name = "Miscellaneous",
description = "",
position = 6
position = 22
)
default Title miscTitle()
{
@@ -106,7 +330,7 @@ public interface OpenOSRSConfig extends Config
keyName = "keyboardPin",
name = "Keyboard bank pin",
description = "Enables you to type your bank pin",
position = 7,
position = 23,
titleSection = "miscTitle"
)
default boolean keyboardPin()
@@ -118,7 +342,7 @@ public interface OpenOSRSConfig extends Config
keyName = "detachHotkey",
name = "Detach Cam",
description = "Detach Camera hotkey, press this and it will activate detatched camera.",
position = 8,
position = 24,
titleSection = "miscTitle"
)
default Keybind detachHotkey()

View File

@@ -52,7 +52,18 @@ public class EventBus implements EventBusInterface
Disposable disposable = getSubject(eventClass)
.filter(Objects::nonNull) // Filter out null objects, better safe than sorry
.cast(eventClass) // Cast it for easier usage
.subscribe(action, error -> log.error("Error in eventbus", error));
.subscribe(action, error ->
{
if (error instanceof RuntimeException)
{
log.error("Runtime Exception in eventbus", error);
System.exit(0);
}
else
{
log.error("Exception in eventbus", error);
}
});
getCompositeDisposable(lifecycle).add(disposable);
subscriptionList.put(lifecycle, eventClass);
@@ -71,7 +82,18 @@ public class EventBus implements EventBusInterface
.cast(eventClass) // Cast it for easier usage
.take(takeUntil)
.doFinally(() -> unregister(lifecycle))
.subscribe(action, error -> log.error("Error in eventbus", error));
.subscribe(action, error ->
{
if (error instanceof RuntimeException)
{
log.error("Runtime Exception in eventbus", error);
System.exit(0);
}
else
{
log.error("Exception in eventbus", error);
}
});
getCompositeDisposable(lifecycle).add(disposable);
subscriptionList.put(lifecycle, eventClass);

View File

@@ -326,12 +326,21 @@ public class ItemManager
return (int) Math.max(1, getItemDefinition(itemID).getPrice() * HIGH_ALCHEMY_MULTIPLIER);
}
public int getBrokenValue(int itemId)
public int getRepairValue(int itemId)
{
PvPValueBrokenItem b = PvPValueBrokenItem.of(itemId);
return getRepairValue(itemId, false);
}
public int getRepairValue(int itemId, boolean fullValue)
{
final ItemReclaimCost b = ItemReclaimCost.of(itemId);
if (b != null)
{
if (fullValue || b.getItemID() == GRANITE_MAUL_24225 || b.getItemID() == GRANITE_MAUL_24227)
{
return b.getValue();
}
return (int) (b.getValue() * (75.0f / 100.0f));
}

View File

@@ -85,6 +85,16 @@ public enum ItemMapping
ITEM_DRAGON_BOOTS(DRAGON_BOOTS, DRAGON_BOOTS_G),
ITEM_DRAGON_BOOTS_ORNAMENT_KIT(DRAGON_BOOTS_ORNAMENT_KIT, DRAGON_BOOTS_G),
// Rune ornament kits
ITEM_RUNE_SCIMITAR_GUTHIX(RUNE_SCIMITAR, RUNE_SCIMITAR_23330),
ITEM_RUNE_SCIMITAR_ORNAMENT_KIT_GUTHIX(RUNE_SCIMITAR_ORNAMENT_KIT_GUTHIX, RUNE_SCIMITAR_23330),
ITEM_RUNE_SCIMITAR_SARADOMIN(RUNE_SCIMITAR, RUNE_SCIMITAR_23332),
ITEM_RUNE_SCIMITAR_ORNAMENT_KIT_SARADOMIN(RUNE_SCIMITAR_ORNAMENT_KIT_SARADOMIN, RUNE_SCIMITAR_23332),
ITEM_RUNE_SCIMITAR_ZAMORAK(RUNE_SCIMITAR, RUNE_SCIMITAR_23334),
ITEM_RUNE_SCIMITAR_ORNAMENT_KIT_ZAMORAK(RUNE_SCIMITAR_ORNAMENT_KIT_ZAMORAK, RUNE_SCIMITAR_23334),
ITEM_RUNE_DEFENDER(RUNE_DEFENDER, RUNE_DEFENDER_T),
ITEM_RUNE_DEFENDER_ORNAMENT_KIT(RUNE_DEFENDER_ORNAMENT_KIT, RUNE_DEFENDER_T),
// Godsword ornament kits
ITEM_ARMADYL_GODSWORD(ARMADYL_GODSWORD, ARMADYL_GODSWORD_OR),
ITEM_ARMADYL_GODSWORD_ORNAMENT_KIT(ARMADYL_GODSWORD_ORNAMENT_KIT, ARMADYL_GODSWORD_OR),
@@ -106,6 +116,8 @@ public enum ItemMapping
ITEM_FURY_ORNAMENT_KIT(FURY_ORNAMENT_KIT, AMULET_OF_FURY_OR),
ITEM_TORMENTED_BRACELET(TORMENTED_BRACELET, TORMENTED_BRACELET_OR),
ITEM_TORMENTED_ORNAMENT_KIT(TORMENTED_ORNAMENT_KIT, TORMENTED_BRACELET_OR),
ITEM_BERSERKER_NECKLACE(BERSERKER_NECKLACE, BERSERKER_NECKLACE_OR),
ITEM_BERSERKER_NECKLACE_ORNAMENT_KIT(BERSERKER_NECKLACE_ORNAMENT_KIT, BERSERKER_NECKLACE_OR),
// Ensouled heads
ITEM_ENSOULED_GOBLIN_HEAD(ENSOULED_GOBLIN_HEAD_13448, ENSOULED_GOBLIN_HEAD),
@@ -194,6 +206,8 @@ public enum ItemMapping
ITEM_MALEDICTION_WARD(MALEDICTION_WARD, MALEDICTION_WARD_12806),
ITEM_STEAM_BATTLESTAFF(STEAM_BATTLESTAFF, STEAM_BATTLESTAFF_12795),
ITEM_LAVA_BATTLESTAFF(LAVA_BATTLESTAFF, LAVA_BATTLESTAFF_21198),
ITEM_TZHAARKETOM(TZHAARKETOM, TZHAARKETOM_T),
ITEM_TZHAARKETOM_ORNAMENT_KIT(TZHAARKETOM_ORNAMENT_KIT, TZHAARKETOM_T),
// Slayer helm/black mask
ITEM_BLACK_MASK(

View File

@@ -37,15 +37,21 @@ import net.runelite.api.ItemID;
*/
@AllArgsConstructor
@Getter
public enum PvPValueBrokenItem
public enum ItemReclaimCost
{
// Capes
FIRE_CAPE(ItemID.FIRE_CAPE, 50000),
FIRE_MAX_CAPE(ItemID.FIRE_MAX_CAPE, 50000),
FIRE_MAX_CAPE(ItemID.FIRE_MAX_CAPE, 99000),
INFERNAL_CAPE(ItemID.INFERNAL_CAPE, 50000),
INFERNAL_MAX_CAPE(ItemID.INFERNAL_MAX_CAPE, 50000),
INFERNAL_MAX_CAPE(ItemID.INFERNAL_MAX_CAPE, 99000),
AVAS_ASSEMBLER(ItemID.AVAS_ASSEMBLER, 75000),
ASSEMBLER_MAX_CAPE(ItemID.ASSEMBLER_MAX_CAPE, 75000),
ASSEMBLER_MAX_CAPE(ItemID.ASSEMBLER_MAX_CAPE, 99000),
IMBUED_GUTHIX_CAPE(ItemID.IMBUED_GUTHIX_CAPE, 75000),
IMBUED_GUTHIX_MAX_CAPE(ItemID.GUTHIX_MAX_CAPE, 99000),
IMBUED_SARADOMIN_CAPE(ItemID.IMBUED_SARADOMIN_CAPE, 75000),
IMBUED_SARADOMIN_MAX_CAPE(ItemID.SARADOMIN_MAX_CAPE, 99000),
IMBUED_ZAMORAK_CAPE(ItemID.IMBUED_ZAMORAK_CAPE, 75000),
IMBUED_ZAMORAK_MAX_CAPE(ItemID.ZAMORAK_MAX_CAPE, 99000),
// Defenders
BRONZE_DEFENDER(ItemID.BRONZE_DEFENDER, 1000),
@@ -90,15 +96,19 @@ public enum PvPValueBrokenItem
GOLD_DECORATIVE_LEGS(ItemID.DECORATIVE_ARMOUR_4510, 5000),
GOLD_DECORATIVE_SKIRT(ItemID.DECORATIVE_ARMOUR_11895, 5000),
GOLD_DECORATIVE_SHIELD(ItemID.DECORATIVE_SHIELD_4512, 5000),
GOLD_DECORATIVE_SWORD(ItemID.DECORATIVE_SWORD_4508, 5000);
GOLD_DECORATIVE_SWORD(ItemID.DECORATIVE_SWORD_4508, 5000),
private static final ImmutableMap<Integer, PvPValueBrokenItem> idMap;
// Granite Maul
GRANITE_MAUL(ItemID.GRANITE_MAUL_24225, 375000),
GRANITE_MAUL_OR(ItemID.GRANITE_MAUL_24227, 375000);
private static final ImmutableMap<Integer, ItemReclaimCost> idMap;
static
{
ImmutableMap.Builder<Integer, PvPValueBrokenItem> builder = ImmutableMap.builder();
ImmutableMap.Builder<Integer, ItemReclaimCost> builder = ImmutableMap.builder();
for (PvPValueBrokenItem items : values())
for (ItemReclaimCost items : values())
{
builder.put(items.itemID, items);
}
@@ -110,7 +120,7 @@ public enum PvPValueBrokenItem
private final int value;
@Nullable
public static PvPValueBrokenItem of(int itemId)
public static ItemReclaimCost of(int itemId)
{
return idMap.get(itemId);
}

View File

@@ -1,322 +1,340 @@
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.game;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.AnimationID;
import net.runelite.api.Client;
import net.runelite.api.TileItem;
import net.runelite.api.ItemID;
import net.runelite.api.NPC;
import net.runelite.api.NpcID;
import net.runelite.api.Player;
import net.runelite.api.Tile;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.ItemDespawned;
import net.runelite.api.events.ItemQuantityChanged;
import net.runelite.api.events.ItemSpawned;
import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.PlayerDespawned;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.events.NpcLootReceived;
import net.runelite.client.events.PlayerLootReceived;
@Singleton
@Slf4j
public class LootManager
{
private static final Map<Integer, Integer> NPC_DEATH_ANIMATIONS = ImmutableMap.of(
NpcID.CAVE_KRAKEN, AnimationID.CAVE_KRAKEN_DEATH
);
private final EventBus eventBus;
private final Client client;
private final ListMultimap<Integer, ItemStack> itemSpawns = ArrayListMultimap.create();
private final Set<LocalPoint> killPoints = new HashSet<>();
private WorldPoint playerLocationLastTick;
private WorldPoint krakenPlayerLocation;
@Inject
private LootManager(
final EventBus eventBus,
final Client client
)
{
this.eventBus = eventBus;
this.client = client;
eventBus.subscribe(GameTick.class, this, this::onGameTick);
eventBus.subscribe(NpcDespawned.class, this, this::onNpcDespawned);
eventBus.subscribe(PlayerDespawned.class, this, this::onPlayerDespawned);
eventBus.subscribe(ItemSpawned.class, this, this::onItemSpawned);
eventBus.subscribe(ItemDespawned.class, this, this::onItemDespawned);
eventBus.subscribe(ItemQuantityChanged.class, this, this::onItemQuantityChanged);
eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged);
}
private void onNpcDespawned(NpcDespawned npcDespawned)
{
final NPC npc = npcDespawned.getNpc();
if (!npc.isDead())
{
int id = npc.getId();
switch (id)
{
case NpcID.GARGOYLE:
case NpcID.GARGOYLE_413:
case NpcID.GARGOYLE_1543:
case NpcID.MARBLE_GARGOYLE:
case NpcID.MARBLE_GARGOYLE_7408:
case NpcID.DUSK_7888:
case NpcID.DUSK_7889:
case NpcID.ROCKSLUG:
case NpcID.ROCKSLUG_422:
case NpcID.GIANT_ROCKSLUG:
case NpcID.SMALL_LIZARD:
case NpcID.SMALL_LIZARD_463:
case NpcID.DESERT_LIZARD:
case NpcID.DESERT_LIZARD_460:
case NpcID.DESERT_LIZARD_461:
case NpcID.LIZARD:
case NpcID.ZYGOMITE:
case NpcID.ZYGOMITE_1024:
case NpcID.ANCIENT_ZYGOMITE:
// these monsters die with >0 hp, so we just look for coincident
// item spawn with despawn
break;
default:
return;
}
}
processNpcLoot(npc);
}
private void onPlayerDespawned(PlayerDespawned playerDespawned)
{
final Player player = playerDespawned.getPlayer();
// Only care about dead Players
if (player.getHealthRatio() != 0)
{
return;
}
final LocalPoint location = LocalPoint.fromWorld(client, player.getWorldLocation());
if (location == null || killPoints.contains(location))
{
return;
}
final int x = location.getSceneX();
final int y = location.getSceneY();
final int packed = x << 8 | y;
final Collection<ItemStack> items = itemSpawns.get(packed);
if (items.isEmpty())
{
return;
}
killPoints.add(location);
eventBus.post(PlayerLootReceived.class, new PlayerLootReceived(player, items));
}
private void onItemSpawned(ItemSpawned itemSpawned)
{
final TileItem item = itemSpawned.getItem();
final Tile tile = itemSpawned.getTile();
final LocalPoint location = tile.getLocalLocation();
final int packed = location.getSceneX() << 8 | location.getSceneY();
itemSpawns.put(packed, new ItemStack(item.getId(), item.getQuantity(), location));
log.debug("Item spawn {} ({}) location {}", item.getId(), item.getQuantity(), location);
}
private void onItemDespawned(ItemDespawned itemDespawned)
{
final TileItem item = itemDespawned.getItem();
final LocalPoint location = itemDespawned.getTile().getLocalLocation();
log.debug("Item despawn {} ({}) location {}", item.getId(), item.getQuantity(), location);
}
private void onItemQuantityChanged(ItemQuantityChanged itemQuantityChanged)
{
final TileItem item = itemQuantityChanged.getItem();
final Tile tile = itemQuantityChanged.getTile();
final LocalPoint location = tile.getLocalLocation();
final int packed = location.getSceneX() << 8 | location.getSceneY();
final int diff = itemQuantityChanged.getNewQuantity() - itemQuantityChanged.getOldQuantity();
if (diff <= 0)
{
return;
}
itemSpawns.put(packed, new ItemStack(item.getId(), diff, location));
}
private void onAnimationChanged(AnimationChanged e)
{
if (!(e.getActor() instanceof NPC))
{
return;
}
final NPC npc = (NPC) e.getActor();
int id = npc.getId();
// We only care about certain NPCs
final Integer deathAnim = NPC_DEATH_ANIMATIONS.get(id);
// Current animation is death animation?
if (deathAnim != null && deathAnim == npc.getAnimation())
{
if (id == NpcID.CAVE_KRAKEN)
{
// Big Kraken drops loot wherever player is standing when animation starts.
krakenPlayerLocation = client.getLocalPlayer().getWorldLocation();
}
else
{
// These NPCs drop loot on death animation, which is right now.
processNpcLoot(npc);
}
}
}
private void onGameTick(GameTick gameTick)
{
playerLocationLastTick = client.getLocalPlayer().getWorldLocation();
itemSpawns.clear();
killPoints.clear();
}
private void processNpcLoot(NPC npc)
{
final LocalPoint location = LocalPoint.fromWorld(client, getDropLocation(npc, npc.getWorldLocation()));
if (location == null || killPoints.contains(location))
{
return;
}
final int x = location.getSceneX();
final int y = location.getSceneY();
final int size = npc.getDefinition().getSize();
// Some NPCs drop items onto multiple tiles
final List<ItemStack> allItems = new ArrayList<>();
for (int i = 0; i < size; ++i)
{
for (int j = 0; j < size; ++j)
{
final int packed = (x + i) << 8 | (y + j);
final Collection<ItemStack> items = itemSpawns.get(packed);
allItems.addAll(items);
}
}
if (allItems.isEmpty())
{
return;
}
killPoints.add(location);
eventBus.post(NpcLootReceived.class, new NpcLootReceived(npc, allItems));
}
private WorldPoint getDropLocation(NPC npc, WorldPoint worldLocation)
{
switch (npc.getId())
{
case NpcID.KRAKEN:
case NpcID.KRAKEN_6640:
case NpcID.KRAKEN_6656:
worldLocation = playerLocationLastTick;
break;
case NpcID.CAVE_KRAKEN:
worldLocation = krakenPlayerLocation;
break;
case NpcID.ZULRAH: // Green
case NpcID.ZULRAH_2043: // Red
case NpcID.ZULRAH_2044: // Blue
for (Map.Entry<Integer, ItemStack> entry : itemSpawns.entries())
{
if (entry.getValue().getId() == ItemID.ZULRAHS_SCALES)
{
int packed = entry.getKey();
int unpackedX = packed >> 8;
int unpackedY = packed & 0xFF;
worldLocation = WorldPoint.fromScene(client, unpackedX, unpackedY, worldLocation.getPlane());
break;
}
}
break;
case NpcID.VORKATH:
case NpcID.VORKATH_8058:
case NpcID.VORKATH_8059:
case NpcID.VORKATH_8060:
case NpcID.VORKATH_8061:
int x = worldLocation.getX() + 3;
int y = worldLocation.getY() + 3;
if (playerLocationLastTick.getX() < x)
{
x -= 4;
}
else if (playerLocationLastTick.getX() > x)
{
x += 4;
}
if (playerLocationLastTick.getY() < y)
{
y -= 4;
}
else if (playerLocationLastTick.getY() > y)
{
y += 4;
}
worldLocation = new WorldPoint(x, y, worldLocation.getPlane());
break;
}
return worldLocation;
}
}
/*
* Copyright (c) 2018, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.game;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.AnimationID;
import net.runelite.api.Client;
import net.runelite.api.TileItem;
import net.runelite.api.ItemID;
import net.runelite.api.NPC;
import net.runelite.api.NpcID;
import net.runelite.api.Player;
import net.runelite.api.Tile;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.ItemDespawned;
import net.runelite.api.events.ItemQuantityChanged;
import net.runelite.api.events.ItemSpawned;
import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.PlayerDespawned;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.events.NpcLootReceived;
import net.runelite.client.events.PlayerLootReceived;
@Singleton
@Slf4j
public class LootManager
{
private static final Map<Integer, Integer> NPC_DEATH_ANIMATIONS = ImmutableMap.<Integer, Integer>builder()
.put(NpcID.CAVE_KRAKEN, AnimationID.CAVE_KRAKEN_DEATH)
.put(NpcID.CRYSTALLINE_BAT, AnimationID.CRYSTALLINE_BAT_DEATH)
.put(NpcID.CRYSTALLINE_RAT, AnimationID.CRYSTALLINE_RAT_DEATH)
.put(NpcID.CRYSTALLINE_SPIDER, AnimationID.CRYSTALLINE_SPIDER_DEATH)
.put(NpcID.CRYSTALLINE_WOLF, AnimationID.CRYSTALLINE_WOLF_DEATH)
.put(NpcID.CRYSTALLINE_UNICORN, AnimationID.CRYSTALLINE_UNICORN_DEATH)
.put(NpcID.CRYSTALLINE_SCORPION, AnimationID.CORRUPTED_SCORPION_DEATH)
.put(NpcID.CRYSTALLINE_DRAGON, AnimationID.CRYSTALLINE_DRAGON_DEATH)
.put(NpcID.CRYSTALLINE_DARK_BEAST, AnimationID.CRYSTALLINE_DARK_BEAST_DEATH)
.put(NpcID.CRYSTALLINE_BEAR, AnimationID.CRYSTALLINE_BEAR_DEATH)
.put(NpcID.CORRUPTED_BAT, AnimationID.CRYSTALLINE_BAT_DEATH)
.put(NpcID.CORRUPTED_RAT, AnimationID.CRYSTALLINE_RAT_DEATH)
.put(NpcID.CORRUPTED_SPIDER, AnimationID.CRYSTALLINE_SPIDER_DEATH)
.put(NpcID.CORRUPTED_WOLF, AnimationID.CRYSTALLINE_WOLF_DEATH)
.put(NpcID.CORRUPTED_UNICORN, AnimationID.CRYSTALLINE_UNICORN_DEATH)
.put(NpcID.CORRUPTED_SCORPION, AnimationID.CORRUPTED_SCORPION_DEATH)
.put(NpcID.CORRUPTED_DRAGON, AnimationID.CRYSTALLINE_DRAGON_DEATH)
.put(NpcID.CORRUPTED_DARK_BEAST, AnimationID.CRYSTALLINE_DARK_BEAST_DEATH)
.put(NpcID.CORRUPTED_BEAR, AnimationID.CRYSTALLINE_BEAR_DEATH)
.build();
private final EventBus eventBus;
private final Client client;
private final ListMultimap<Integer, ItemStack> itemSpawns = ArrayListMultimap.create();
private final Set<LocalPoint> killPoints = new HashSet<>();
private WorldPoint playerLocationLastTick;
private WorldPoint krakenPlayerLocation;
@Inject
private LootManager(
final EventBus eventBus,
final Client client
)
{
this.eventBus = eventBus;
this.client = client;
eventBus.subscribe(GameTick.class, this, this::onGameTick);
eventBus.subscribe(NpcDespawned.class, this, this::onNpcDespawned);
eventBus.subscribe(PlayerDespawned.class, this, this::onPlayerDespawned);
eventBus.subscribe(ItemSpawned.class, this, this::onItemSpawned);
eventBus.subscribe(ItemDespawned.class, this, this::onItemDespawned);
eventBus.subscribe(ItemQuantityChanged.class, this, this::onItemQuantityChanged);
eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged);
}
private void onNpcDespawned(NpcDespawned npcDespawned)
{
final NPC npc = npcDespawned.getNpc();
if (!npc.isDead())
{
int id = npc.getId();
switch (id)
{
case NpcID.GARGOYLE:
case NpcID.GARGOYLE_413:
case NpcID.GARGOYLE_1543:
case NpcID.MARBLE_GARGOYLE:
case NpcID.MARBLE_GARGOYLE_7408:
case NpcID.DUSK_7888:
case NpcID.DUSK_7889:
case NpcID.ROCKSLUG:
case NpcID.ROCKSLUG_422:
case NpcID.GIANT_ROCKSLUG:
case NpcID.SMALL_LIZARD:
case NpcID.SMALL_LIZARD_463:
case NpcID.DESERT_LIZARD:
case NpcID.DESERT_LIZARD_460:
case NpcID.DESERT_LIZARD_461:
case NpcID.LIZARD:
case NpcID.ZYGOMITE:
case NpcID.ZYGOMITE_1024:
case NpcID.ANCIENT_ZYGOMITE:
// these monsters die with >0 hp, so we just look for coincident
// item spawn with despawn
break;
default:
return;
}
}
processNpcLoot(npc);
}
private void onPlayerDespawned(PlayerDespawned playerDespawned)
{
final Player player = playerDespawned.getPlayer();
// Only care about dead Players
if (player.getHealthRatio() != 0)
{
return;
}
final LocalPoint location = LocalPoint.fromWorld(client, player.getWorldLocation());
if (location == null || killPoints.contains(location))
{
return;
}
final int x = location.getSceneX();
final int y = location.getSceneY();
final int packed = x << 8 | y;
final Collection<ItemStack> items = itemSpawns.get(packed);
if (items.isEmpty())
{
return;
}
killPoints.add(location);
eventBus.post(PlayerLootReceived.class, new PlayerLootReceived(player, items));
}
private void onItemSpawned(ItemSpawned itemSpawned)
{
final TileItem item = itemSpawned.getItem();
final Tile tile = itemSpawned.getTile();
final LocalPoint location = tile.getLocalLocation();
final int packed = location.getSceneX() << 8 | location.getSceneY();
itemSpawns.put(packed, new ItemStack(item.getId(), item.getQuantity(), location));
log.debug("Item spawn {} ({}) location {}", item.getId(), item.getQuantity(), location);
}
private void onItemDespawned(ItemDespawned itemDespawned)
{
final TileItem item = itemDespawned.getItem();
final LocalPoint location = itemDespawned.getTile().getLocalLocation();
log.debug("Item despawn {} ({}) location {}", item.getId(), item.getQuantity(), location);
}
private void onItemQuantityChanged(ItemQuantityChanged itemQuantityChanged)
{
final TileItem item = itemQuantityChanged.getItem();
final Tile tile = itemQuantityChanged.getTile();
final LocalPoint location = tile.getLocalLocation();
final int packed = location.getSceneX() << 8 | location.getSceneY();
final int diff = itemQuantityChanged.getNewQuantity() - itemQuantityChanged.getOldQuantity();
if (diff <= 0)
{
return;
}
itemSpawns.put(packed, new ItemStack(item.getId(), diff, location));
}
private void onAnimationChanged(AnimationChanged e)
{
if (!(e.getActor() instanceof NPC))
{
return;
}
final NPC npc = (NPC) e.getActor();
int id = npc.getId();
// We only care about certain NPCs
final Integer deathAnim = NPC_DEATH_ANIMATIONS.get(id);
// Current animation is death animation?
if (deathAnim != null && deathAnim == npc.getAnimation())
{
if (id == NpcID.CAVE_KRAKEN)
{
// Big Kraken drops loot wherever player is standing when animation starts.
krakenPlayerLocation = client.getLocalPlayer().getWorldLocation();
}
else
{
// These NPCs drop loot on death animation, which is right now.
processNpcLoot(npc);
}
}
}
private void onGameTick(GameTick gameTick)
{
playerLocationLastTick = client.getLocalPlayer().getWorldLocation();
itemSpawns.clear();
killPoints.clear();
}
private void processNpcLoot(NPC npc)
{
final LocalPoint location = LocalPoint.fromWorld(client, getDropLocation(npc, npc.getWorldLocation()));
if (location == null || killPoints.contains(location))
{
return;
}
final int x = location.getSceneX();
final int y = location.getSceneY();
final int size = npc.getDefinition().getSize();
// Some NPCs drop items onto multiple tiles
final List<ItemStack> allItems = new ArrayList<>();
for (int i = 0; i < size; ++i)
{
for (int j = 0; j < size; ++j)
{
final int packed = (x + i) << 8 | (y + j);
final Collection<ItemStack> items = itemSpawns.get(packed);
allItems.addAll(items);
}
}
if (allItems.isEmpty())
{
return;
}
killPoints.add(location);
eventBus.post(NpcLootReceived.class, new NpcLootReceived(npc, allItems));
}
private WorldPoint getDropLocation(NPC npc, WorldPoint worldLocation)
{
switch (npc.getId())
{
case NpcID.KRAKEN:
case NpcID.KRAKEN_6640:
case NpcID.KRAKEN_6656:
worldLocation = playerLocationLastTick;
break;
case NpcID.CAVE_KRAKEN:
worldLocation = krakenPlayerLocation;
break;
case NpcID.ZULRAH: // Green
case NpcID.ZULRAH_2043: // Red
case NpcID.ZULRAH_2044: // Blue
for (Map.Entry<Integer, ItemStack> entry : itemSpawns.entries())
{
if (entry.getValue().getId() == ItemID.ZULRAHS_SCALES)
{
int packed = entry.getKey();
int unpackedX = packed >> 8;
int unpackedY = packed & 0xFF;
worldLocation = WorldPoint.fromScene(client, unpackedX, unpackedY, worldLocation.getPlane());
break;
}
}
break;
case NpcID.VORKATH:
case NpcID.VORKATH_8058:
case NpcID.VORKATH_8059:
case NpcID.VORKATH_8060:
case NpcID.VORKATH_8061:
int x = worldLocation.getX() + 3;
int y = worldLocation.getY() + 3;
if (playerLocationLastTick.getX() < x)
{
x -= 4;
}
else if (playerLocationLastTick.getX() > x)
{
x += 4;
}
if (playerLocationLastTick.getY() < y)
{
y -= 4;
}
else if (playerLocationLastTick.getY() > y)
{
y += 4;
}
worldLocation = new WorldPoint(x, y, worldLocation.getPlane());
break;
}
return worldLocation;
}
}

View File

@@ -53,7 +53,18 @@ public enum UntradeableItemMapping
CRYSTAL_LEGS(ItemID.CRYSTAL_LEGS, 2, ItemID.CRYSTAL_ARMOUR_SEED),
CRYSTAL_LEGS_INACTIVE(ItemID.CRYSTAL_LEGS_INACTIVE, 2, ItemID.CRYSTAL_ARMOUR_SEED),
CRYSTAL_BODY(ItemID.CRYSTAL_BODY, 3, ItemID.CRYSTAL_ARMOUR_SEED),
CRYSTAL_BODY_INACTIVE(ItemID.CRYSTAL_BODY_INACTIVE, 3, ItemID.CRYSTAL_ARMOUR_SEED);
CRYSTAL_BODY_INACTIVE(ItemID.CRYSTAL_BODY_INACTIVE, 3, ItemID.CRYSTAL_ARMOUR_SEED),
TATTERED_MOON_PAGE(ItemID.TATTERED_MOON_PAGE, 1000, ItemID.COINS_995),
TATTERED_SUN_PAGE(ItemID.TATTERED_SUN_PAGE, 1000, ItemID.COINS_995),
TATTERED_TEMPLE_PAGE(ItemID.TATTERED_TEMPLE_PAGE, 1000, ItemID.COINS_995),
LONG_BONE(ItemID.LONG_BONE, 1000, ItemID.COINS_995),
CURVED_BONE(ItemID.CURVED_BONE, 2000, ItemID.COINS_995),
PERFECT_SHELL(ItemID.PERFECT_SHELL, 600, ItemID.COINS_995),
PERFECT_SNAIL_SHELL(ItemID.PERFECT_SNAIL_SHELL, 600, ItemID.COINS_995),
SNAIL_SHELL(ItemID.SNAIL_SHELL, 600, ItemID.COINS_995),
TORTOISE_SHELL(ItemID.TORTOISE_SHELL, 250, ItemID.COINS_995);
private static final ImmutableMap<Integer, UntradeableItemMapping> UNTRADEABLE_RECLAIM_MAP;

View File

@@ -0,0 +1,12 @@
package net.runelite.client.game;
import lombok.Data;
import net.runelite.api.Skill;
import net.runelite.api.events.Event;
@Data
public class XpDropEvent implements Event
{
private Skill skill;
private int exp;
}

View File

@@ -0,0 +1,60 @@
package net.runelite.client.game;
import java.util.EnumMap;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.Skill;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.StatChanged;
import net.runelite.client.eventbus.EventBus;
@Singleton
public class XpDropManager
{
private final Map<Skill, Integer> previousSkillExpTable = new EnumMap<>(Skill.class);
@Getter(AccessLevel.PACKAGE)
private int damage = 0;
@Getter(AccessLevel.PACKAGE)
private int tickShow = 0;
private int previousExpGained;
private Client client;
private EventBus eventBus;
@Inject
private XpDropManager(
final EventBus eventBus,
final Client client
)
{
this.client = client;
this.eventBus = eventBus;
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
eventBus.subscribe(StatChanged.class, this, this::onStatChanged);
}
private void onGameStateChanged(GameStateChanged event)
{
damage = 0;
tickShow = 0;
}
private void onStatChanged(StatChanged event)
{
final Skill skill = event.getSkill();
final int xp = client.getSkillExperience(skill);
Integer previous = previousSkillExpTable.put(skill, xp);
if (previous != null)
{
previousExpGained = xp - previous;
XpDropEvent xpDropEvent = new XpDropEvent();
xpDropEvent.setExp(previousExpGained);
xpDropEvent.setSkill(skill);
eventBus.post(XpDropEvent.class, xpDropEvent);
}
}
}

View File

@@ -91,6 +91,8 @@ public class MenuManager
private MenuEntry leftClickEntry = null;
private MenuEntry firstEntry = null;
private int playerAttackIdx = -1;
@Inject
private MenuManager(Client client, EventBus eventBus)
{
@@ -240,11 +242,16 @@ public class MenuManager
// Need to set the event entries to prevent conflicts
event.setMenuEntries(arrayEntries);
event.setModified(true);
event.setModified();
}
private void onMenuEntryAdded(MenuEntryAdded event)
{
if (client.isSpellSelected())
{
return;
}
for (AbstractComparableEntry e : hiddenEntries)
{
if (e.matches(event))
@@ -500,6 +507,29 @@ public class MenuManager
return index;
}
public int getPlayerAttackOpcode()
{
final String[] playerMenuOptions = client.getPlayerOptions();
if (playerAttackIdx != -1 && playerMenuOptions[playerAttackIdx].equals("Attack"))
{
return client.getPlayerMenuTypes()[playerAttackIdx];
}
playerAttackIdx = -1;
for (int i = IDX_LOWER; i < IDX_UPPER; i++)
{
if ("Attack".equals(playerMenuOptions[i]))
{
playerAttackIdx = i;
break;
}
}
return playerAttackIdx >= 0 ? client.getPlayerMenuTypes()[playerAttackIdx] : -1;
}
/**
* Adds to the set of menu entries which when present, will remove all entries except for this one
*/
@@ -522,7 +552,7 @@ public class MenuManager
AbstractComparableEntry entry = newBaseComparableEntry(option, target);
priorityEntries.removeIf(entry::equals);
priorityEntries.remove(entry);
}
@@ -562,7 +592,7 @@ public class MenuManager
public void removePriorityEntry(AbstractComparableEntry entry)
{
priorityEntries.removeIf(entry::equals);
priorityEntries.remove(entry);
}
public void removePriorityEntry(String option)
@@ -571,7 +601,7 @@ public class MenuManager
AbstractComparableEntry entry = newBaseComparableEntry(option, "", false);
priorityEntries.removeIf(entry::equals);
priorityEntries.remove(entry);
}
public void removePriorityEntry(String option, boolean strictOption)
@@ -581,7 +611,17 @@ public class MenuManager
AbstractComparableEntry entry =
newBaseComparableEntry(option, "", -1, -1, false, strictOption);
priorityEntries.removeIf(entry::equals);
priorityEntries.remove(entry);
}
public void addPriorityEntries(Collection<AbstractComparableEntry> entries)
{
priorityEntries.addAll(entries);
}
public void removePriorityEntries(Collection<AbstractComparableEntry> entries)
{
priorityEntries.removeAll(entries);
}
/**
@@ -889,7 +929,7 @@ public class MenuManager
}
@AllArgsConstructor
private class SortMapping implements Comparable<SortMapping>
private static class SortMapping implements Comparable<SortMapping>
{
private final int priority;
private final MenuEntry entry;

View File

@@ -30,18 +30,10 @@ import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URLClassLoader;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;
import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLite;
import net.runelite.client.config.Config;
@@ -50,106 +42,33 @@ import net.runelite.client.config.OpenOSRSConfig;
@Singleton
@Slf4j
public class PluginWatcher extends Thread
public class ExternalPluginLoader
{
private static final File BASE = RuneLite.PLUGIN_DIR;
private final OpenOSRSConfig OpenOSRSConfig;
private final PluginManager pluginManager;
private final WatchService watchService;
private final WatchKey watchKey;
@Inject
private ConfigManager configManager;
@Inject
public PluginWatcher(OpenOSRSConfig OpenOSRSConfig, PluginManager pluginManager) throws IOException
public ExternalPluginLoader(OpenOSRSConfig OpenOSRSConfig, PluginManager pluginManager)
{
this.OpenOSRSConfig = OpenOSRSConfig;
this.pluginManager = pluginManager;
setName("Plugin Watcher");
setDaemon(true);
watchService = FileSystems.getDefault().newWatchService();
BASE.mkdirs();
Path dir = BASE.toPath();
watchKey = dir.register(watchService, ENTRY_MODIFY, ENTRY_DELETE);
}
public void cancel()
public void scanAndLoad()
{
watchKey.cancel();
}
@Override
public void run()
{
if (OpenOSRSConfig.enablePlugins())
if (!OpenOSRSConfig.enablePlugins())
{
scan();
return;
}
for (; ; )
{
try
{
WatchKey key = watchService.take();
Thread.sleep(50);
if (!OpenOSRSConfig.enablePlugins())
{
key.reset();
continue;
}
for (WatchEvent<?> event : key.pollEvents())
{
Kind<?> kind = event.kind();
Path path = (Path) event.context();
File file = new File(BASE, path.toFile().getName());
log.debug("Event {} file {}", kind, file);
if (kind == ENTRY_MODIFY)
{
Plugin existing = findPluginForFile(file);
if (existing != null)
{
log.info("Reloading plugin {}", file);
unload(existing);
}
else
{
log.info("Loading plugin {}", file);
}
load(file);
}
else if (kind == ENTRY_DELETE)
{
Plugin existing = findPluginForFile(file);
if (existing != null)
{
log.info("Unloading plugin {}", file);
unload(existing);
}
}
}
key.reset();
}
catch (InterruptedException ex)
{
log.warn("error polling for plugins", ex);
}
}
}
private void scan()
{
for (File file : BASE.listFiles())
for (File file : Objects.requireNonNull(BASE.listFiles()))
{
if (!file.getName().endsWith(".jar"))
{
@@ -160,18 +79,6 @@ public class PluginWatcher extends Thread
}
}
private Plugin findPluginForFile(File file)
{
for (Plugin plugin : pluginManager.getPlugins())
{
if (plugin.file != null && plugin.file.equals(file))
{
return plugin;
}
}
return null;
}
private void load(File pluginFile)
{
PluginClassLoader loader;
@@ -242,22 +149,6 @@ public class PluginWatcher extends Thread
pluginManager.add(plugin);
}
private void unload(Plugin plugin)
{
try
{
pluginManager.stopPlugin(plugin);
}
catch (PluginInstantiationException ex)
{
log.warn("unable to stop plugin", ex);
}
pluginManager.remove(plugin); // remove it regardless
close(plugin.loader);
}
private void close(URLClassLoader classLoader)
{
try

View File

@@ -99,7 +99,7 @@ public class PluginManager
.getAnnotation(ConfigGroup.class).value();
@Inject
PluginWatcher pluginWatcher;
ExternalPluginLoader externalPluginLoader;
@Setter
boolean isOutdated;
@@ -128,11 +128,6 @@ public class PluginManager
}
}
public void watch()
{
pluginWatcher.start();
}
private void onSessionOpen(SessionOpen event)
{
refreshPlugins();
@@ -210,6 +205,11 @@ public class PluginManager
}
}
public void loadExternalPlugins()
{
externalPluginLoader.scanAndLoad();
}
public void loadCorePlugins() throws IOException
{
plugins.addAll(scanAndInstantiate(getClass().getClassLoader(), PLUGIN_PACKAGE));

View File

@@ -8,6 +8,5 @@ public enum PluginType
UTILITY,
GENERAL_USE,
EXTERNAL,
PLUGIN_ORGANIZER,
IMPORTANT
}

View File

@@ -120,9 +120,6 @@ public class KaramjaDiaryRequirement extends GenericDiaryRequirement
new CombatLevelRequirement(100),
new SkillRequirement(Skill.SLAYER, 50),
new QuestRequirement(Quest.SHILO_VILLAGE));
add("Kill a metal dragon in Brimhaven Dungeon.",
new SkillRequirement(Skill.AGILITY, 12),
new SkillRequirement(Skill.WOODCUTTING, 34));
// ELITE
add("Craft 56 Nature runes at once.",

View File

@@ -78,7 +78,7 @@ public class WesternDiaryRequirement extends GenericDiaryRequirement
add("Make a Chocolate Bomb at the Grand Tree.",
new SkillRequirement(Skill.COOKING, 42));
add("Complete a delivery for the Gnome Restaurant.",
new SkillRequirement(Skill.COOKING, 42));
new SkillRequirement(Skill.COOKING, 29));
add("Turn your small crystal seed into a Crystal saw.",
new QuestRequirement(Quest.THE_EYES_OF_GLOUPHRIE));
add("Mine some Gold ore underneath the Grand Tree.",

View File

@@ -50,12 +50,10 @@ import net.runelite.api.TileItem;
import net.runelite.api.TileObject;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.BeforeRender;
import net.runelite.api.events.BoostedLevelChanged;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.DecorativeObjectChanged;
import net.runelite.api.events.DecorativeObjectDespawned;
import net.runelite.api.events.DecorativeObjectSpawned;
import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.GameObjectChanged;
import net.runelite.api.events.GameObjectDespawned;
import net.runelite.api.events.GameObjectSpawned;
@@ -67,6 +65,7 @@ import net.runelite.api.events.GroundObjectSpawned;
import net.runelite.api.events.ItemDespawned;
import net.runelite.api.events.ItemSpawned;
import net.runelite.api.events.MenuOpened;
import net.runelite.api.events.StatChanged;
import net.runelite.api.events.WallObjectChanged;
import net.runelite.api.events.WallObjectDespawned;
import net.runelite.api.events.WallObjectSpawned;
@@ -201,8 +200,7 @@ public class AgilityPlugin extends Plugin
{
eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
eventBus.subscribe(ExperienceChanged.class, this, this::onExperienceChanged);
eventBus.subscribe(BoostedLevelChanged.class, this, this::onBoostedLevelChanged);
eventBus.subscribe(StatChanged .class, this, this::onStatChanged);
eventBus.subscribe(ItemSpawned.class, this, this::onItemSpawned);
eventBus.subscribe(ItemDespawned.class, this, this::onItemDespawned);
eventBus.subscribe(GameTick.class, this, this::onGameTick);
@@ -295,9 +293,16 @@ public class AgilityPlugin extends Plugin
this.showAgilityArenaTimer = config.showAgilityArenaTimer();
}
private void onExperienceChanged(ExperienceChanged event)
public void onStatChanged(StatChanged statChanged)
{
if (event.getSkill() != AGILITY || !this.showLapCount)
if (statChanged.getSkill() != AGILITY)
{
return;
}
agilityLevel = statChanged.getBoostedLevel();
if (!this.showLapCount)
{
return;
}
@@ -330,15 +335,6 @@ public class AgilityPlugin extends Plugin
}
}
private void onBoostedLevelChanged(BoostedLevelChanged boostedLevelChanged)
{
Skill skill = boostedLevelChanged.getSkill();
if (skill == AGILITY)
{
agilityLevel = client.getBoostedSkillLevel(skill);
}
}
private void onItemSpawned(ItemSpawned itemSpawned)
{
if (obstacles.isEmpty())
@@ -541,7 +537,10 @@ public class AgilityPlugin extends Plugin
changed |= checkAndModify(entry);
}
event.setModified(changed);
if (changed)
{
event.setModified();
}
}
private boolean checkAndModify(MenuEntry old)

View File

@@ -28,6 +28,7 @@ package net.runelite.client.plugins.bank;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.HashMultiset;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multiset;
import com.google.inject.Provides;
@@ -50,12 +51,17 @@ import net.runelite.api.ItemDefinition;
import net.runelite.api.ItemID;
import net.runelite.api.MenuEntry;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.SpriteID;
import net.runelite.api.VarClientInt;
import net.runelite.api.VarClientStr;
import net.runelite.api.Varbits;
import net.runelite.api.events.ItemContainerChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuShouldLeftClick;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.VarClientStrChanged;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.vars.InputType;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
@@ -139,6 +145,7 @@ public class BankPlugin extends Plugin
private boolean forceRightClickFlag;
private boolean largePinNumbers;
private Multiset<Integer> itemQuantities; // bank item quantities for bank value search
private String searchString;
@Provides
BankConfig getConfig(ConfigManager configManager)
@@ -179,6 +186,8 @@ public class BankPlugin extends Plugin
eventBus.subscribe(ScriptCallbackEvent.class, this, this::onScriptCallbackEvent);
eventBus.subscribe(WidgetLoaded.class, this, this::onWidgetLoaded);
eventBus.subscribe(ItemContainerChanged.class, this, this::onItemContainerChanged);
eventBus.subscribe(VarClientStrChanged.class, this, this::onVarClientStrChanged);
searchString = "";
}
private void onMenuShouldLeftClick(MenuShouldLeftClick event)
@@ -266,7 +275,34 @@ public class BankPlugin extends Plugin
updateSeedVaultTotal();
}
private void onItemContainerChanged(ItemContainerChanged event)
public void onVarClientStrChanged(VarClientStrChanged event)
{
String searchVar = client.getVar(VarClientStr.INPUT_TEXT);
if (!searchVar.equals(searchString))
{
Widget searchButtonBackground = client.getWidget(WidgetInfo.BANK_SEARCH_BUTTON_BACKGROUND);
if (searchButtonBackground != null && searchButtonBackground.hasListener())
{
searchButtonBackground.setOnTimerListener((Object[]) null);
searchButtonBackground.setHasListener(false);
}
clientThread.invokeLater(() -> bankSearch.layoutBank());
searchString = searchVar;
}
if (client.getVar(VarClientInt.INPUT_TYPE) != InputType.SEARCH.getType() && Strings.isNullOrEmpty(client.getVar(VarClientStr.INPUT_TEXT)))
{
Widget searchBackground = client.getWidget(WidgetInfo.BANK_SEARCH_BUTTON_BACKGROUND);
if (searchBackground != null)
{
searchBackground.setSpriteId(SpriteID.EQUIPMENT_SLOT_TILE);
}
}
}
public void onItemContainerChanged(ItemContainerChanged event)
{
int containerId = event.getContainerId();

View File

@@ -54,18 +54,6 @@ public class BankSearch
{
clientThread.invoke(() ->
{
Widget bankContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
if (bankContainer == null || bankContainer.isHidden())
{
return;
}
Object[] scriptArgs = bankContainer.getOnInvTransmit();
if (scriptArgs == null)
{
return;
}
// This ensures that any chatbox input (e.g from search) will not remain visible when
// selecting/changing tab
@@ -77,10 +65,28 @@ public class BankSearch
client.setVar(VarClientInt.INPUT_TYPE, inputType.getType());
client.setVar(VarClientStr.INPUT_TEXT, search);
client.runScript(scriptArgs);
layoutBank();
});
}
public void layoutBank()
{
Widget bankContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
if (bankContainer == null || bankContainer.isHidden())
{
return;
}
Object[] scriptArgs = bankContainer.getOnInvTransmit();
if (scriptArgs == null)
{
return;
}
client.runScript(scriptArgs);
}
public void reset(boolean closeChat)
{
search(InputType.NONE, "", closeChat);

View File

@@ -599,9 +599,11 @@ public class TabInterface
chatboxPanelManager.close();
}
if (event.getIdentifier() == WidgetInfo.BANK_ITEM_CONTAINER.getId()
if ((event.getIdentifier() == WidgetInfo.BANK_ITEM_CONTAINER.getId()
|| event.getIdentifier() == WidgetInfo.BANK_INVENTORY_ITEMS_CONTAINER.getId())
&& event.getMenuOpcode() == MenuOpcode.EXAMINE_ITEM_BANK_EQ
&& event.getOption().equalsIgnoreCase("withdraw-x"))
&& (event.getOption().equalsIgnoreCase("withdraw-x")
|| event.getOption().equalsIgnoreCase("deposit-x")))
{
waitSearchTick = true;
rememberedSearch = client.getVar(VarClientStr.INPUT_TEXT);

View File

@@ -39,10 +39,10 @@ import net.runelite.api.Client;
import net.runelite.api.Constants;
import net.runelite.api.Prayer;
import net.runelite.api.Skill;
import net.runelite.api.events.BoostedLevelChanged;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.StatChanged;
import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
@@ -174,7 +174,7 @@ public class BoostsPlugin extends Plugin
{
eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
eventBus.subscribe(BoostedLevelChanged.class, this, this::onBoostedLevelChanged);
eventBus.subscribe(StatChanged.class, this, this::onStatChanged);
eventBus.subscribe(GameTick.class, this, this::onGameTick);
}
@@ -211,9 +211,9 @@ public class BoostsPlugin extends Plugin
}
}
private void onBoostedLevelChanged(BoostedLevelChanged boostedLevelChanged)
private void onStatChanged(StatChanged statChanged)
{
Skill skill = boostedLevelChanged.getSkill();
Skill skill = statChanged.getSkill();
if (!BOOSTABLE_COMBAT_SKILLS.contains(skill) && !BOOSTABLE_NON_COMBAT_SKILLS.contains(skill))
{

View File

@@ -202,7 +202,7 @@ public class ChatTranslationPlugin extends Plugin implements KeyListener
newEntries[i].setOpcode(MenuOpcode.RUNELITE.getId());
event.setMenuEntries(newEntries);
event.setModified(true);
event.setModified();
return;
}

View File

@@ -512,17 +512,6 @@ public class ClueScrollPlugin extends Plugin
return clue;
}
// (This|The) anagram reveals who to speak to next:
if (text.contains("anagram reveals who to speak to next:"))
{
return AnagramClue.forText(text);
}
if (text.startsWith("the cipher reveals who to speak to next:"))
{
return CipherClue.forText(text);
}
if (text.startsWith("i'd like to hear some music."))
{
return MusicClue.forText(clueScrollText.getText());
@@ -533,6 +522,18 @@ public class ClueScrollPlugin extends Plugin
return coordinatesToWorldPoint(text);
}
final AnagramClue anagramClue = AnagramClue.forText(text);
if (anagramClue != null)
{
return anagramClue;
}
final CipherClue cipherClue = CipherClue.forText(text);
if (cipherClue != null)
{
return cipherClue;
}
final CrypticClue crypticClue = CrypticClue.forText(text);
if (crypticClue != null)

View File

@@ -51,109 +51,109 @@ public class AnagramClue extends ClueScroll implements TextClueScroll, NpcClueSc
private static final String ANAGRAM_TEXT_BEGINNER = "The anagram reveals who to speak to next: ";
private static final Set<AnagramClue> CLUES = ImmutableSet.of(
new AnagramClue("A BAKER", "Baraek", new WorldPoint(3217, 3434, 0), "Varrock square", "5"),
new AnagramClue("A BASIC ANTI POT", "Captain Tobias", new WorldPoint(3026, 3216, 0), "Port Sarim", "6"),
new AnagramClue("A BAKER", "Baraek", new WorldPoint(3217, 3434, 0), "Varrock square", "How many stalls are there in Varrock Square?", "5"),
new AnagramClue("A BASIC ANTI POT", "Captain Tobias", new WorldPoint(3026, 3216, 0), "Port Sarim", "How many ships are there docked at Port Sarim currently?", "6"),
new AnagramClue("A ELF KNOWS", "Snowflake", new WorldPoint(2872, 3934, 0), "Weiss"),
new AnagramClue("A HEART", "Aretha", new WorldPoint(1814, 3851, 0), "Soul altar", "2"),
new AnagramClue("A HEART", "Aretha", new WorldPoint(1814, 3851, 0), "Soul altar", "32 - 5x = 22, what is x?", "2"),
new AnagramClue("A ZEN SHE", "Zenesha", new WorldPoint(2652, 3295, 0), "Platebody Southern Ardougne centre square"),
new AnagramClue("ACE MATCH ELM", "Cam The Camel", new WorldPoint(3300, 3231, 0), "North of the glider in Al Kharid"),
new AnagramClue("AHA JAR", "Jaraah", new WorldPoint(3359, 3276, 0), "Duel Arena hospital"),
new AnagramClue("AN PAINT TONIC", "Captain Ninto", new WorldPoint(2865, 9877, 0), "Bar under White Wolf Mountain"),
new AnagramClue("ARC O LINE", "Caroline", new WorldPoint(2715, 3302, 0), "North Witchaven next to the row boat", "11"),
new AnagramClue("ARE COL", "Oracle", new WorldPoint(3013, 3501, 0), "Ice Mountain West of Edgeville", "48"),
new AnagramClue("ARMCHAIR THE PELT", "Charlie the Tramp", new WorldPoint(3209, 3392, 0), "South entrance of Varrock", "0"),
new AnagramClue("ARC O LINE", "Caroline", new WorldPoint(2715, 3302, 0), "North Witchaven next to the row boat", "How many fishermen are there on the fishing platform?", "11"),
new AnagramClue("ARE COL", "Oracle", new WorldPoint(3013, 3501, 0), "Ice Mountain West of Edgeville", "If x is 15 and y is 3 what is 3x + y?", "48"),
new AnagramClue("ARMCHAIR THE PELT", "Charlie the Tramp", new WorldPoint(3209, 3392, 0), "South entrance of Varrock", "How many coins would I have if I had 0 coins and attempted to buy 3 loaves of bread?", "0"),
new AnagramClue("ARR! SO I AM A CRUST, AND?", "Ramara du Croissant", new WorldPoint(2339, 3677, 0), "Piscatoris Fishing Colony"),
new AnagramClue("AT HERG", "Regath", new WorldPoint(1719, 3723, 0), "General Store, Arceuus, Zeah", "25"),
new AnagramClue("AT HERG", "Regath", new WorldPoint(1719, 3723, 0), "General Store, Arceuus, Zeah", "What is -5 to the power of 2?", "25"),
new AnagramClue("A BAS", "Saba", new WorldPoint(2858, 3577, 0), "Death Plateau"),
new AnagramClue("AREA CHEF TREK", "Father Aereck", new WorldPoint(3243, 3208, 0), "Lumbridge Church", "19 or 20"),
new AnagramClue("AREA CHEF TREK", "Father Aereck", new WorldPoint(3243, 3208, 0), "Lumbridge Church", "How many gravestones are in the church graveyard?", "19 or 20"),
new AnagramClue("BAIL TRIMS", "Brimstail", new WorldPoint(2402, 3419, 0), "West of Stronghold Slayer Cave"),
new AnagramClue("BAKER CLIMB", "Brambickle", new WorldPoint(2783, 3861, 0), "Trollweiss mountain"),
new AnagramClue("BLUE GRIM GUIDED", "Lumbridge Guide", new WorldPoint(3232, 3232, 0), "Lumbridge"),
new AnagramClue("BY LOOK", "Bolkoy", new WorldPoint(2529, 3162, 0), "Tree Gnome Village general store", "13"),
new AnagramClue("CALAMARI MADE MUD", "Madame Caldarium", new WorldPoint(2553, 2868, 0), "Corsair Cove", "6"),
new AnagramClue("BY LOOK", "Bolkoy", new WorldPoint(2529, 3162, 0), "Tree Gnome Village general store", "How many flowers are there in the clearing below this platform?", "13"),
new AnagramClue("CALAMARI MADE MUD", "Madame Caldarium", new WorldPoint(2553, 2868, 0), "Corsair Cove", "What is 3(5-3)?", "6"),
new AnagramClue("CAR IF ICES", "Sacrifice", new WorldPoint(2209, 3056, 0), "Zul-Andra"),
new AnagramClue("CAREER IN MOON", "Oneiromancer", new WorldPoint(2150, 3866, 0), "Astral altar", "25"),
new AnagramClue("CLASH ION", "Nicholas", new WorldPoint(1841, 3803, 0), "North of Port Piscarilius fishing shop", "4"),
new AnagramClue("C ON GAME HOC", "Gnome Coach", new WorldPoint(2395, 3486, 0), "Gnome Ball course", "6"),
new AnagramClue("COOL NERD", "Old crone", new WorldPoint(3462, 3557, 0), "East of the Slayer Tower", "619"),
new AnagramClue("COPPER ORE CRYPTS", "Prospector Percy", new WorldPoint(3061, 3377, 0), "Motherlode Mine", "12"),
new AnagramClue("CAREER IN MOON", "Oneiromancer", new WorldPoint(2150, 3866, 0), "Astral altar", "How many Suqah inhabit Lunar isle?", "25"),
new AnagramClue("CLASH ION", "Nicholas", new WorldPoint(1841, 3803, 0), "North of Port Piscarilius fishing shop", "How many windows are in Tynan's shop?", "4"),
new AnagramClue("C ON GAME HOC", "Gnome Coach", new WorldPoint(2395, 3486, 0), "Gnome Ball course", "How many gnomes on the Gnome ball field have red patches on their uniforms?", "6"),
new AnagramClue("COOL NERD", "Old crone", new WorldPoint(3462, 3557, 0), "East of the Slayer Tower", "What is the combined combat level of each species that live in Slayer tower?", "619"),
new AnagramClue("COPPER ORE CRYPTS", "Prospector Percy", new WorldPoint(3061, 3377, 0), "Motherlode Mine", "During a party, everyone shook hands with everyone else. There were 66 handshakes. How many people were at the party?", "12"),
new AnagramClue("DED WAR", "Edward", new WorldPoint(3284, 3943, 0), "Inside Rogue's Castle"),
new AnagramClue("DEKAGRAM", "Dark mage", new WorldPoint(3039, 4835, 0), "Centre of the Abyss", "13"),
new AnagramClue("DO SAY MORE", "Doomsayer", new WorldPoint(3230, 3230, 0), "East of Lumbridge Castle", "95"),
new AnagramClue("DIM THARN", "Mandrith", new WorldPoint(3182, 3946, 0), "Wilderness Resource Area", "28 or Puzzle box"),
new AnagramClue("DR HITMAN", "Mandrith", new WorldPoint(3182, 3946, 0), "Wilderness Resource Area", "28, Light box or Puzzle box"),
new AnagramClue("DR WARDEN FUNK", "Drunken Dwarf", new WorldPoint(2913, 10221, 0), "East Side of Keldagrim", "Puzzle box"),
new AnagramClue("DRAGONS LAMENT", "Strange Old Man", new WorldPoint(3564, 3288, 0), "Barrows", "40"),
new AnagramClue("DT RUN B", "Brundt the Chieftain", new WorldPoint(2658, 3670, 0), "Rellekka, main hall", "4"),
new AnagramClue("DEKAGRAM", "Dark mage", new WorldPoint(3039, 4835, 0), "Centre of the Abyss", "How many rifts are found here in the abyss?", "13"),
new AnagramClue("DO SAY MORE", "Doomsayer", new WorldPoint(3230, 3230, 0), "East of Lumbridge Castle", "What is 40 divided by 1/2 plus 15?", "95"),
new AnagramClue("DIM THARN", "Mandrith", new WorldPoint(3182, 3946, 0), "Wilderness Resource Area"),
new AnagramClue("DR HITMAN", "Mandrith", new WorldPoint(3182, 3946, 0), "Wilderness Resource Area", "How many scorpions live under the pit?", "28"),
new AnagramClue("DR WARDEN FUNK", "Drunken Dwarf", new WorldPoint(2913, 10221, 0), "East Side of Keldagrim"),
new AnagramClue("DRAGONS LAMENT", "Strange Old Man", new WorldPoint(3564, 3288, 0), "Barrows", "One pipe fills a barrel in 1 hour while another pipe can fill the same barrel in 2 hours. How many minutes will it take to fill the take if both pipes are used?", "40"),
new AnagramClue("DT RUN B", "Brundt the Chieftain", new WorldPoint(2658, 3670, 0), "Rellekka, main hall", "How many people are waiting for the next bard to perform?", "4"),
new AnagramClue("DUO PLUG", "Dugopul", new WorldPoint(2803, 2744, 0), "Graveyard on Ape Atoll"),
new AnagramClue("EEK ZERO OP", "Zoo keeper", new WorldPoint(2613, 3269, 0), "Ardougne Zoo", "40"),
new AnagramClue("EEK ZERO OP", "Zoo keeper", new WorldPoint(2613, 3269, 0), "Ardougne Zoo", "How many animals are in the Ardougne Zoo?", "40"),
new AnagramClue("EL OW", "Lowe", new WorldPoint(3233, 3423, 0), "Varrock archery store"),
new AnagramClue("ERR CURE IT", "Recruiter", new WorldPoint(2541, 3305, 0), "West Ardougne centre square", "20"),
new AnagramClue("ERR CURE IT", "Recruiter", new WorldPoint(2541, 3305, 0), "West Ardougne centre square", "How many houses have a cross on the door?", "20"),
new AnagramClue("FORLUN", "Runolf", new WorldPoint(2512, 10256, 0), "Miscellania & Etceteria Dungeon"),
new AnagramClue("GOBLIN KERN", "King Bolren", new WorldPoint(2541, 3170, 0), "Tree Gnome Village"),
new AnagramClue("GOT A BOY", "Gabooty", new WorldPoint(2790, 3066, 0), "Centre of Tai Bwo Wannai", "11"),
new AnagramClue("GOT A BOY", "Gabooty", new WorldPoint(2790, 3066, 0), "Centre of Tai Bwo Wannai", "How many buildings in the village?", "11"),
new AnagramClue("GULAG RUN", "Uglug Nar", new WorldPoint(2442, 3051, 0), "West of Jiggig"),
new AnagramClue("GOBLETS ODD TOES", "Otto Godblessed", new WorldPoint(2501, 3487, 0), "Otto's Grotto", "2"),
new AnagramClue("HALT US", "Luthas", new WorldPoint(2938, 3152, 0), "Banana plantation, Karamja", "33 (or none)"),
new AnagramClue("GOBLETS ODD TOES", "Otto Godblessed", new WorldPoint(2501, 3487, 0), "Otto's Grotto", "How many types of dragon are there beneath the whirlpool's cavern?", "2"),
new AnagramClue("HALT US", "Luthas", new WorldPoint(2938, 3152, 0), "Banana plantation, Karamja"),
new AnagramClue("HE DO POSE. IT IS CULTRRL, MK?", "Riki the sculptor's model", new WorldPoint(2904, 10206, 0), "East Keldagrim, south of kebab seller."),
new AnagramClue("HEORIC", "Eohric", new WorldPoint(2900, 3565, 0), "Top floor of Burthorpe Castle", "36"),
new AnagramClue("HIS PHOR", "Horphis", new WorldPoint(1639, 3812, 0), "Arceuus Library, Zeah", "1"),
new AnagramClue("I AM SIR", "Marisi", new WorldPoint(1737, 3557, 0), "Allotment patch, South of Hosidius chapel", "5"),
new AnagramClue("HEORIC", "Eohric", new WorldPoint(2900, 3565, 0), "Top floor of Burthorpe Castle", "King Arthur and Merlin sit down at the Round Table with 8 knights. How many degrees does each get?", "36"),
new AnagramClue("HIS PHOR", "Horphis", new WorldPoint(1639, 3812, 0), "Arceuus Library, Zeah", "On a scale of 1-10, how helpful is Logosia?", "1"),
new AnagramClue("I AM SIR", "Marisi", new WorldPoint(1737, 3557, 0), "Allotment patch, South of Hosidius chapel", "How many cities form the Kingdom of Great Kourend?", "5"),
new AnagramClue("ICY FE", "Fycie", new WorldPoint(2630, 2997, 0), "East Feldip Hills"),
new AnagramClue("I DOOM ICON INN", "Dominic Onion", new WorldPoint(2609, 3116, 0), "Nightmare Zone", "9,500"),
new AnagramClue("I DOOM ICON INN", "Dominic Onion", new WorldPoint(2609, 3116, 0), "Nightmare Zone", "How many reward points does a herb box cost?", "9,500"),
new AnagramClue("I EAT ITS CHART HINTS DO U", "Shiratti the Custodian", new WorldPoint(3427, 2927, 0), "North of fountain, Nardah"),
new AnagramClue("I EVEN", "Nieve", new WorldPoint(2432, 3422, 0), "The slayer master in Gnome Stronghold", "2"),
new AnagramClue("I EVEN", "Nieve", new WorldPoint(2432, 3422, 0), "The slayer master in Gnome Stronghold", "How many farming patches are there in Gnome stronghold?", "2"),
new AnagramClue("I FAFFY RUN", "Fairy Nuff", new WorldPoint(3201, 3169, 0), "North of the bank in Zanaris"),
new AnagramClue("IM N ZEZIM", "Impling", new WorldPoint(2592, 4324, 0), "The Imp inside Puro-Puro"),
new AnagramClue("KAY SIR", "Sir Kay", new WorldPoint(2760, 3496, 0), "The courtyard in Camelot Castle", "6"),
new AnagramClue("LEAKEY", "Kaylee", new WorldPoint(2957, 3370, 0), "Rising Sun Inn in Falador", "18"),
new AnagramClue("KAY SIR", "Sir Kay", new WorldPoint(2760, 3496, 0), "The courtyard in Camelot Castle", "How many fountains are there within the grounds of Camelot castle.", "6"),
new AnagramClue("LEAKEY", "Kaylee", new WorldPoint(2957, 3370, 0), "Rising Sun Inn in Falador", "How many chairs are there in the Rising Sun?", "18"),
new AnagramClue("LAND DOOMD", "Odd Old Man", new WorldPoint(3359, 3506, 0), "Limestone mine northeast of Varrock"),
new AnagramClue("LARK IN DOG", "King Roald", new WorldPoint(3220, 3476, 0), "Ground floor of Varrock castle", "24"),
new AnagramClue("LOW LAG", "Gallow", new WorldPoint(1805, 3566, 0), "Vinery in the Great Kourend", "12"),
new AnagramClue("LADDER MEMO GUV", "Guard Vemmeldo", new WorldPoint(2447, 3418, 1), "Gnome Stronghold Bank", "3"),
new AnagramClue("LARK IN DOG", "King Roald", new WorldPoint(3220, 3476, 0), "Ground floor of Varrock castle", "How many bookcases are there in the Varrock palace library?", "24"),
new AnagramClue("LOW LAG", "Gallow", new WorldPoint(1805, 3566, 0), "Vinery in the Great Kourend", "How many vine patches can you find in this vinery?", "12"),
new AnagramClue("LADDER MEMO GUV", "Guard Vemmeldo", new WorldPoint(2447, 3418, 1), "Gnome Stronghold Bank", "How many magic trees can you find inside the Gnome Stronghold?", "3"),
new AnagramClue("MAL IN TAU", "Luminata", new WorldPoint(3508, 3237, 0), "Near Burgh de Rott entrance"),
new AnagramClue("ME AM THE CALC", "Cam the Camel", new WorldPoint(3300, 3231, 0), "Outside Duel Arena"),
new AnagramClue("MACHETE CLAM", "Cam the Camel", new WorldPoint(3300, 3231, 0), "Outside Duel Arena", "6"),
new AnagramClue("MACHETE CLAM", "Cam the Camel", new WorldPoint(3300, 3231, 0), "Outside Duel Arena", "How many items can carry water in RuneScape?", "6"),
new AnagramClue("ME IF", "Femi", new WorldPoint(2461, 3382, 0), "Gates of Tree Gnome Stronghold"),
new AnagramClue("MOLD LA RAN", "Old Man Ral", new WorldPoint(3602, 3209, 0), "Meiyerditch"),
new AnagramClue("MOTHERBOARD", "Brother Omad", new WorldPoint(2606, 3211, 0), "Monastery south of Ardougne", "129"),
new AnagramClue("MOTHERBOARD", "Brother Omad", new WorldPoint(2606, 3211, 0), "Monastery south of Ardougne", "What is the next number? 12, 13, 15, 17, 111, 113, 117, 119, 123....?", "129"),
new AnagramClue("MUS KIL READER", "Radimus Erkle", new WorldPoint(2726, 3368, 0), "Legends' Guild"),
new AnagramClue("MY MANGLE LAL", "Lammy Langle", new WorldPoint(1688, 3540, 0), "Hosidius spirit tree patch"),
new AnagramClue("NO OWNER", "Oronwen", new WorldPoint(1162, 3178, 0), "Lletya Seamstress shop in Lletya", "20"),
new AnagramClue("NOD MED", "Edmond", new WorldPoint(2566, 3332, 0), "Behind the most NW house in East Ardougne", "3"),
new AnagramClue("O BIRDZ A ZANY EN PC", "Cap'n Izzy No-Beard", new WorldPoint(2807, 3191, 0), "Brimhaven Agility Arena", "33"),
new AnagramClue("OK CO", "Cook", new WorldPoint(3207, 3214, 0), "Ground floor of Lumbridge Castle", "9"),
new AnagramClue("NO OWNER", "Oronwen", new WorldPoint(1162, 3178, 0), "Lletya Seamstress shop in Lletya", "What is the minimum amount of quest points required to reach Lletya?", "20"),
new AnagramClue("NOD MED", "Edmond", new WorldPoint(2566, 3332, 0), "Behind the most NW house in East Ardougne", "How many pigeon cages are there around the back of Jerico's house?", "3"),
new AnagramClue("O BIRDZ A ZANY EN PC", "Cap'n Izzy No-Beard", new WorldPoint(2807, 3191, 0), "Brimhaven Agility Arena", "How many Banana Trees are there in the plantation?", "33"),
new AnagramClue("OK CO", "Cook", new WorldPoint(3207, 3214, 0), "Ground floor of Lumbridge Castle", "How many cannons does Lumbridge Castle have?", "9"),
new AnagramClue("OR ZINC FUMES WARD", "Wizard Frumscone", new WorldPoint(2594, 3086, 0), "Downstairs in the Wizards' Guild"),
new AnagramClue("OUR OWN NEEDS", "Nurse Wooned", new WorldPoint(1575, 3590, 0), "Shayzien Infirmary", "19"),
new AnagramClue("PACINNG A TAIE", "Captain Ginea", new WorldPoint(1561, 3602, 0), "Building east of Shayzien combat ring", "113"),
new AnagramClue("PEAK REFLEX", "Flax keeper", new WorldPoint(2744, 3444, 0), "Flax field south of Seers Village", "676"),
new AnagramClue("OUR OWN NEEDS", "Nurse Wooned", new WorldPoint(1575, 3590, 0), "Shayzien Infirmary", "How many injured soldiers are in this tent?", "19"),
new AnagramClue("PACINNG A TAIE", "Captain Ginea", new WorldPoint(1561, 3602, 0), "Building east of Shayzien combat ring", "1 soldier can deal with 6 lizardmen. How many soldiers do we need for an army of 678 lizardmen?", "113"),
new AnagramClue("PEAK REFLEX", "Flax keeper", new WorldPoint(2744, 3444, 0), "Flax field south of Seers Village", "If I have 1014 flax, and I spin a third of them into bowstring, how many flax do I have left?", "676"),
new AnagramClue("PEATY PERT", "Party Pete", new WorldPoint(3047, 3376, 0), "Falador Party Room"),
new AnagramClue("PROFS LOSE WRONG PIE", "Professor Onglewip", new WorldPoint(3113, 3162, 0), "Ground floor of Wizards Tower"),
new AnagramClue("QUIT HORRIBLE TYRANT", "Brother Tranquility", new WorldPoint(3681, 2963, 0), "Mos Le'Harmless or Harmony Island", "7"),
new AnagramClue("QUE SIR", "Squire", new WorldPoint(2975, 3343, 0), "Falador Castle Courtyard", "654"),
new AnagramClue("R AK MI", "Karim", new WorldPoint(3273, 3181, 0), "Al Kharid Kebab shop", "5"),
new AnagramClue("RAT MAT WITHIN", "Martin Thwait", new WorldPoint(2906, 3537, 0), "Rogues' Den", "2"),
new AnagramClue("QUIT HORRIBLE TYRANT", "Brother Tranquility", new WorldPoint(3681, 2963, 0), "Mos Le'Harmless or Harmony Island", "If I have 49 bottles of rum to share between 7 pirates, how many would each pirate get?", "7"),
new AnagramClue("QUE SIR", "Squire", new WorldPoint(2975, 3343, 0), "Falador Castle Courtyard", "White knights are superior to black knights. 2 white knights can handle 3 black knights. How many knights do we need for an army of 981 black knights?", "654"),
new AnagramClue("R AK MI", "Karim", new WorldPoint(3273, 3181, 0), "Al Kharid Kebab shop", "I have 16 kebabs, I eat one myself and share the rest equally between 3 friends. How many do they have each?", "5"),
new AnagramClue("RAT MAT WITHIN", "Martin Thwait", new WorldPoint(2906, 3537, 0), "Rogues' Den", "How many natural fires burn in Rogue's Den?", "2"),
new AnagramClue("RED ART TANS", "Trader Stan", new WorldPoint(3041, 3193, 0), "Port Sarim Charter ship"),
new AnagramClue("RATAI", "Taria", new WorldPoint(2940, 3223, 0), "Rimmington bush patch", "7"),
new AnagramClue("R SLICER", "Clerris", new WorldPoint(1761, 3850, 0), "Arceuus mine, Zeah", "738"),
new AnagramClue("RATAI", "Taria", new WorldPoint(2940, 3223, 0), "Rimmington bush patch", "How many buildings are there in Rimmington?", "7"),
new AnagramClue("R SLICER", "Clerris", new WorldPoint(1761, 3850, 0), "Arceuus mine, Zeah", "If I have 1,000 blood runes, and cast 131 ice barrage spells, how many blood runes do I have left?", "738"),
new AnagramClue("RIP MAUL", "Primula", new WorldPoint(2454, 2853, 1), "Myth's Guild, first floor"),
new AnagramClue("SAND NUT", "Dunstan", new WorldPoint(2919, 3574, 0), "Anvil in north east Burthorpe", "8"),
new AnagramClue("SAND NUT", "Dunstan", new WorldPoint(2919, 3574, 0), "Anvil in north east Burthorpe", "How much smithing experience does one receive for smelting a blurite bar?", "8"),
new AnagramClue("SEQUIN DIRGE", "Queen Sigrid", new WorldPoint(2612, 3867, 0), "Throne room of Etceteria Castle."),
new AnagramClue("SLAM DUSTER GRAIL", "Guildmaster Lars", new WorldPoint(1649, 3498, 0), "Woodcutting guild, Zeah"),
new AnagramClue("SLIDE WOMAN", "Wise Old Man", new WorldPoint(3088, 3253, 0), "Draynor Village", "28"),
new AnagramClue("SLIDE WOMAN", "Wise Old Man", new WorldPoint(3088, 3253, 0), "Draynor Village", "How many bookcases are in the Wise Old Man's house?", "28"),
new AnagramClue("SNAH", "Hans", new WorldPoint(3218, 3219, 0), "Lumbridge Castle courtyard"),
new AnagramClue("SNAKES SO I SAIL", "Lisse Isaakson", new WorldPoint(2351, 3801, 0), "Neitiznot", "2"),
new AnagramClue("TAMED ROCKS", "Dockmaster", new WorldPoint(1822, 3739, 0), "Port Piscarilius, NE of General store", "5"),
new AnagramClue("SNAKES SO I SAIL", "Lisse Isaakson", new WorldPoint(2351, 3801, 0), "Neitiznot", "How many arctic logs are required to make a large fremennik round shield?", "2"),
new AnagramClue("TAMED ROCKS", "Dockmaster", new WorldPoint(1822, 3739, 0), "Port Piscarilius, NE of General store", "What is the cube root of 125?", "5"),
new AnagramClue("TEN WIGS ON", "Wingstone", new WorldPoint(3389, 2877, 0), "Between Nardah & Agility Pyramid"),
new AnagramClue("THEM CAL CAME", "Cam the Camel", new WorldPoint(3300, 3231, 0), "Just outside of the Duel Arena"),
new AnagramClue("THICKNO", "Hickton", new WorldPoint(2822, 3442, 0), "Catherby fletching shop", "2"),
new AnagramClue("THICKNO", "Hickton", new WorldPoint(2822, 3442, 0), "Catherby fletching shop", "How many ranges are there in Catherby?", "2"),
new AnagramClue("TWENTY CURE IRON", "New recruit Tony", new WorldPoint(1498, 3544, 0), "Shayzien Graveyard"),
new AnagramClue("UNLEASH NIGHT MIST", "Sigli the Huntsman", new WorldPoint(2660, 3654, 0), "Rellekka", "302"),
new AnagramClue("VESTE", "Steve", new WorldPoint(2432, 3423, 0), "Upstairs Wyvern Area or Stronghold Slayer Cave", "2"),
new AnagramClue("VEIL VEDA", "Evil Dave", new WorldPoint(3079, 9892, 0), "Doris' basement, Edgeville", "666"),
new AnagramClue("WOO AN EGG KIWI", "Awowogei", ObjectID.AWOWOGEI, new WorldPoint(2802, 2765, 0), "Ape Atoll", "24"),
new AnagramClue("UNLEASH NIGHT MIST", "Sigli the Huntsman", new WorldPoint(2660, 3654, 0), "Rellekka", "What is the combined slayer requirement of every monster in the slayer cave?", "302"),
new AnagramClue("VESTE", "Steve", new WorldPoint(2432, 3423, 0), "Upstairs Wyvern Area or Stronghold Slayer Cave", "How many farming patches are there in Gnome stronghold?", "2"),
new AnagramClue("VEIL VEDA", "Evil Dave", new WorldPoint(3079, 9892, 0), "Doris' basement, Edgeville", "What is 333 multiplied by 2?", "666"),
new AnagramClue("WOO AN EGG KIWI", "Awowogei", ObjectID.AWOWOGEI, new WorldPoint(2802, 2765, 0), "Ape Atoll", "If I have 303 bananas, and share them between 31 friends evenly, only handing out full bananas. How many will I have left over?", "24"),
new AnagramClue("YAWNS GY", "Ysgawyn", new WorldPoint(2340, 3167, 0), "Lletya"),
new AnagramClue("MAJORS LAVA BADS AIR", "Ambassador Alvijar", new WorldPoint(2736, 5351, 1), "Dorgesh-Kaan, NE Middle Level", "2505"),
new AnagramClue("MAJORS LAVA BADS AIR", "Ambassador Alvijar", new WorldPoint(2736, 5351, 1), "Dorgesh-Kaan, NE Middle Level", "Double the miles before the initial Dorgeshuun veteran.", "2505"),
new AnagramClue("AN EARL", "Ranael", new WorldPoint(3315, 3163, 0), "Al Kharid skirt shop"),
new AnagramClue("CARPET AHOY", "Apothecary", new WorldPoint(3195, 3404, 0), "Southwest Varrock"),
new AnagramClue("DISORDER", "Sedridor", new WorldPoint(3102, 9570, 0), "Wizards' Tower basement"),
@@ -163,7 +163,7 @@ public class AnagramClue extends ClueScroll implements TextClueScroll, NpcClueSc
new AnagramClue("RUG DETER", "Gertrude", new WorldPoint(3151, 3412, 0), "West of Varrock, south of the Cooks' Guild"),
new AnagramClue("SIR SHARE RED", "Hairdresser", new WorldPoint(2944, 3381, 0), "Western Falador"),
new AnagramClue("TAUNT ROOF", "Fortunato", new WorldPoint(3080, 3250, 0), "Draynor Village Market"),
new AnagramClue("HICK JET", "Jethick", new WorldPoint(2541, 3305, 0), "West Ardougne", "38"),
new AnagramClue("HICK JET", "Jethick", new WorldPoint(2541, 3305, 0), "West Ardougne", "How many graves are there in the city graveyard?", "38"),
new AnagramClue("RUE GO", "Goreu", new WorldPoint(2335, 3162, 0), "Lletya")
);
@@ -171,27 +171,29 @@ public class AnagramClue extends ClueScroll implements TextClueScroll, NpcClueSc
private final String npc;
private final WorldPoint location;
private final String area;
private final String question;
private final String answer;
private int objectId;
private AnagramClue(String text, String npc, WorldPoint location, String area)
{
this(text, npc, location, area, null);
this(text, npc, location, area, "", null);
}
private AnagramClue(String text, String npc, WorldPoint location, String area, String answer)
private AnagramClue(String text, String npc, WorldPoint location, String area, String question, String answer)
{
this.text = text;
this.npc = npc;
this.location = location;
this.area = area;
this.question = question;
this.answer = answer;
this.objectId = -1;
}
private AnagramClue(String text, String npc, int objectId, WorldPoint location, String area, String answer)
private AnagramClue(String text, String npc, int objectId, WorldPoint location, String area, String question, String answer)
{
this(text, npc, location, area, answer);
this(text, npc, location, area, question, answer);
this.objectId = objectId;
}
@@ -257,7 +259,9 @@ public class AnagramClue extends ClueScroll implements TextClueScroll, NpcClueSc
{
for (AnagramClue clue : CLUES)
{
if (text.equalsIgnoreCase(ANAGRAM_TEXT + clue.text) || text.equalsIgnoreCase(ANAGRAM_TEXT_BEGINNER + clue.text))
if (text.equalsIgnoreCase(ANAGRAM_TEXT + clue.text)
|| text.equalsIgnoreCase(ANAGRAM_TEXT_BEGINNER + clue.text)
|| text.equalsIgnoreCase(clue.question))
{
return clue;
}

View File

@@ -43,35 +43,42 @@ import net.runelite.client.ui.overlay.components.TitleComponent;
public class CipherClue extends ClueScroll implements TextClueScroll, NpcClueScroll, LocationClueScroll
{
private static final Set<CipherClue> CLUES = ImmutableSet.of(
new CipherClue("BMJ UIF LFCBC TFMMFS", "Ali the Kebab seller", new WorldPoint(3354, 2974, 0), "Pollnivneach", "399"),
new CipherClue("GUHCHO", "Drezel", new WorldPoint(3440, 9895, 0), "Paterdomus", "7"),
new CipherClue("ZCZL", "Adam", new WorldPoint(3227, 3227, 0), "Outside Lumbridge castle", "666"),
new CipherClue("ZHLUG ROG PDQ", "Weird Old Man", new WorldPoint(3224, 3112, 0), "Kalphite Lair entrance. Fairy ring BIQ", "150"),
new CipherClue("ECRVCKP MJCNGF", "Captain Khaled", new WorldPoint(1845, 3754, 0), "Large eastern building in Port Piscarilius", "5"),
new CipherClue("OVEXON", "Eluned", new WorldPoint(2289, 3144, 0), "Outside Lletya", "53,000"),
new CipherClue("VTYR APCNTGLW", "King Percival", new WorldPoint(2634, 4682, 1), "Fisher Realm, first floor. Fairy ring BJR", "5"),
new CipherClue("UZZU MUJHRKYYKJ", "Otto Godblessed", new WorldPoint(2501, 3487, 0), "Otto's Grotto", "3"),
new CipherClue("USBJCPSO", "Traiborn", new WorldPoint(3112, 3162, 0), "First floor of Wizards Tower", "3150"),
new CipherClue("HCKTA IQFHCVJGT", "Fairy Godfather", new WorldPoint(2446, 4428, 0), "Zanaris throne room", "64"),
new CipherClue("ZSBKDO ZODO", "Pirate Pete", new WorldPoint(3680, 3537, 0), "Dock northeast of the Ectofunctus", "Puzzle"),
new CipherClue("GBJSZ RVFFO", "Fairy Queen", new WorldPoint(2347, 4435, 0), "Fairy Resistance Hideout", "Puzzle"),
new CipherClue("QSPGFTTPS HSBDLMFCPOF", "Professor Gracklebone", new WorldPoint(1625, 3802, 0), "Ground floor of Arceuus Library", "9"),
new CipherClue("IWPPLQTP", "Gunnjorn", new WorldPoint(2541, 3548, 0), "Barbarian Outpost Agility course", "Puzzle"),
new CipherClue("BSOPME MZETQPS", "Arnold Lydspor", new WorldPoint(2329, 3689, 0), "Piscatoris Fishing Colony general store/bank", "Puzzle")
new CipherClue("BMJ UIF LFCBC TFMMFS", "Ali the Kebab seller", new WorldPoint(3354, 2974, 0), "Pollnivneach", "How many coins would you need to purchase 133 kebabs from me?", "399"),
new CipherClue("GUHCHO", "Drezel", new WorldPoint(3440, 9895, 0), "Paterdomus", "Please solve this for x: 7x - 28=21", "7"),
new CipherClue("ZCZL", "Adam", new WorldPoint(3227, 3227, 0), "Outside Lumbridge castle", "How many snakeskins are needed in order to craft 44 boots, 29 vambraces and 34 bandanas?", "666"),
new CipherClue("ZHLUG ROG PDQ", "Weird Old Man", new WorldPoint(3224, 3112, 0), "Kalphite Lair entrance. Fairy ring BIQ", "SIX LEGS! All of them have 6! There are 25 of them! How many legs?", "150"),
new CipherClue("ECRVCKP MJCNGF", "Captain Khaled", new WorldPoint(1845, 3754, 0), "Large eastern building in Port Piscarilius", "How many fishing cranes can you find around here?", "5"),
new CipherClue("OVEXON", "Eluned", new WorldPoint(2289, 3144, 0), "Outside Lletya", "A question on elven crystal math. I have 5 and 3 crystals, large and small respectively. A large crystal is worth 10,000 coins and a small is worth but 1,000. How much are all my crystals worth?", "53,000"),
new CipherClue("VTYR APCNTGLW", "King Percival", new WorldPoint(2634, 4682, 1), "Fisher Realm, first floor. Fairy ring BJR", "How many cannons are on this here castle?", "5"),
new CipherClue("UZZU MUJHRKYYKJ", "Otto Godblessed", new WorldPoint(2501, 3487, 0), "Otto's Grotto", "How many pyre sites are found around this lake?", "3"),
new CipherClue("USBJCPSO", "Traiborn", new WorldPoint(3112, 3162, 0), "First floor of Wizards Tower", "How many air runes would I need to cast 630 wind waves?", "3150"),
new CipherClue("HCKTA IQFHCVJGT", "Fairy Godfather", new WorldPoint(2446, 4428, 0), "Zanaris throne room", "There are 3 inputs and 4 letters on each ring How many total individual fairy ring codes are possible?", "64"),
new CipherClue("ZSBKDO ZODO", "Pirate Pete", new WorldPoint(3680, 3537, 0), "Dock northeast of the Ectofunctus"),
new CipherClue("GBJSZ RVFFO", "Fairy Queen", new WorldPoint(2347, 4435, 0), "Fairy Resistance Hideout"),
new CipherClue("QSPGFTTPS HSBDLMFCPOF", "Professor Gracklebone", new WorldPoint(1625, 3802, 0), "Ground floor of Arceuus Library", "How many round tables can be found on this floor of the library?", "9"),
new CipherClue("IWPPLQTP", "Gunnjorn", new WorldPoint(2541, 3548, 0), "Barbarian Outpost Agility course"),
new CipherClue("BSOPME MZETQPS", "Arnold Lydspor", new WorldPoint(2329, 3689, 0), "Piscatoris Fishing Colony general store/bank")
);
private String text;
private String npc;
private WorldPoint location;
private String area;
private String question;
private String answer;
private CipherClue(String text, String npc, WorldPoint location, String area, String answer)
private CipherClue(String text, String npc, WorldPoint location, String area)
{
this(text, npc, location, area, "", null);
}
private CipherClue(String text, String npc, WorldPoint location, String area, String question, String answer)
{
this.text = "The cipher reveals who to speak to next: " + text;
this.npc = npc;
this.location = location;
this.area = area;
this.question = question;
this.answer = answer;
}
@@ -122,7 +129,7 @@ public class CipherClue extends ClueScroll implements TextClueScroll, NpcClueScr
{
for (CipherClue clue : CLUES)
{
if (clue.text.equalsIgnoreCase(text))
if (clue.text.equalsIgnoreCase(text) || clue.question.equalsIgnoreCase(text))
{
return clue;
}

View File

@@ -63,7 +63,7 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati
.put(new WorldPoint(2383, 3370, 0), "West of the outpost")
.put(new WorldPoint(3312, 3375, 0), "North-west of Exam Centre, on the hill.")
.put(new WorldPoint(3121, 3384, 0), "North-east of Draynor Manor, near River Lum.")
.put(new WorldPoint(3430, 3388, 0), "West of Mort Myre Swamp.")
.put(new WorldPoint(3430, 3388, 0), "West of Mort Myre Swamp (BKR).")
.put(new WorldPoint(2920, 3403, 0), "South-east of Taverley, near Lady of the Lake.")
.put(new WorldPoint(2594, 2899, 0), "South-east of Feldip Hills, by the crimson swifts (AKS).")
.put(new WorldPoint(2387, 3435, 0), "West of Tree Gnome Stronghold, near the pen containing terrorbirds.")
@@ -71,21 +71,21 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati
.put(new WorldPoint(2381, 3468, 0), "West of Tree Gnome Stronghold, north of the pen with terrorbirds.")
.put(new WorldPoint(3005, 3475, 0), "Ice Mountain, west of Edgeville.")
.put(new WorldPoint(2585, 3505, 0), "By the shore line north of the Coal Trucks.")
.put(new WorldPoint(3443, 3515, 0), "South of Slayer Tower.")
.put(new WorldPoint(3443, 3515, 0), "South of Slayer Tower (CKS).")
.put(new WorldPoint(2416, 3516, 0), "Tree Gnome Stronghold, west of Grand Tree, near swamp.")
.put(new WorldPoint(3429, 3523, 0), "South of Slayer Tower.")
.put(new WorldPoint(2363, 3531, 0), "North-east of Eagles' Peak.")
.put(new WorldPoint(3429, 3523, 0), "South of Slayer Tower (CKS).")
.put(new WorldPoint(2363, 3531, 0), "North-east of Eagles' Peak (AKQ).")
.put(new WorldPoint(2919, 3535, 0), "East of Burthorpe pub.")
.put(new WorldPoint(3548, 3560, 0), "Inside Fenkenstrain's Castle.")
.put(new WorldPoint(1456, 3620, 0), "Graveyard west of Shayzien (DJR).")
.put(new WorldPoint(2735, 3638, 0), "East of Rellekka, north-west of Golden Apple Tree (AJR).")
.put(new WorldPoint(2681, 3653, 0), "Rellekka, in the garden of the south-east house.")
.put(new WorldPoint(2537, 3881, 0), "Miscellania.")
.put(new WorldPoint(2537, 3881, 0), "Miscellania (CIP).")
.put(new WorldPoint(2828, 3234, 0), "Southern coast of Crandor.")
.put(new WorldPoint(1247, 3726, 0), "Just inside the Farming Guild")
.put(new WorldPoint(3770, 3898, 0), "On the small island north-east of Fossil Island's mushroom forest.")
// Hard
.put(new WorldPoint(2209, 3161, 0), "North-east of Tyras Camp.")
.put(new WorldPoint(2209, 3161, 0), "North-east of Tyras Camp (BJS).")
.put(new WorldPoint(2181, 3206, 0), "South of Elf Camp.")
.put(new WorldPoint(3081, 3209, 0), "Small Island (CLP).")
.put(new WorldPoint(3374, 3250, 0), "Duel Arena combat area.")
@@ -98,12 +98,12 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati
.put(new WorldPoint(2581, 3030, 0), "Gu'Tanoth island, enter cave north-west of Feldip Hills (AKS).")
.put(new WorldPoint(2961, 3024, 0), "Ship yard (DKP).")
.put(new WorldPoint(2339, 3311, 0), "East of Prifddinas on Arandar mountain pass.")
.put(new WorldPoint(3440, 3341, 0), "Nature Spirit's grotto.")
.put(new WorldPoint(2763, 2974, 0), "Cairn Isle, west of Shilo Village.")
.put(new WorldPoint(3440, 3341, 0), "Nature Spirit's grotto (BIP).")
.put(new WorldPoint(2763, 2974, 0), "Cairn Isle, west of Shilo Village (CKR).")
.put(new WorldPoint(3138, 2969, 0), "West of Bandit Camp in Kharidian Desert.")
.put(new WorldPoint(2924, 2963, 0), "On the southern part of eastern Karamja.")
.put(new WorldPoint(2838, 2914, 0), "Kharazi Jungle, near water pool.")
.put(new WorldPoint(3441, 3419, 0), "Mort Myre Swamp.")
.put(new WorldPoint(2838, 2914, 0), "Kharazi Jungle, near water pool (CKR).")
.put(new WorldPoint(3441, 3419, 0), "Mort Myre Swamp (BKR).")
.put(new WorldPoint(2950, 2902, 0), "South-east of Kharazi Jungle.")
.put(new WorldPoint(2775, 2891, 0), "South-west of Kharazi Jungle.")
.put(new WorldPoint(3113, 3602, 0), "Wilderness. North of Edgeville (level 11).")
@@ -113,8 +113,8 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati
.put(new WorldPoint(3305, 3692, 0), "Wilderness. West of eastern green dragon.")
.put(new WorldPoint(3055, 3696, 0), "Wilderness. Bandit Camp.")
.put(new WorldPoint(3302, 3696, 0), "Wilderness. West of eastern green dragon.")
.put(new WorldPoint(1479, 3696, 0), "Lizardman Canyon.")
.put(new WorldPoint(2712, 3732, 0), "North-east of Rellekka.")
.put(new WorldPoint(1479, 3696, 0), "Lizardman Canyon (DJR).")
.put(new WorldPoint(2712, 3732, 0), "North-east of Rellekka (DKS).")
.put(new WorldPoint(2970, 3749, 0), "Wilderness. Forgotten Cemetery.")
.put(new WorldPoint(3094, 3764, 0), "Wilderness. Mining site north of Bandit Camp.")
.put(new WorldPoint(3311, 3769, 0), "Wilderness. North of Venenatis.")
@@ -168,6 +168,7 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati
.put(new WorldPoint(3051, 3736, 0), "East of the Wilderness Obelisk in 28 Wilderness.")
.put(new WorldPoint(2316, 3814, 0), "West of Neitiznot, near the bridge.")
.put(new WorldPoint(2872, 3937, 0), "Weiss.")
.put(new WorldPoint(2835, 2998, 0), "Northeast corner of the Island of Stone.")
// Master
.put(new WorldPoint(2178, 3209, 0), "South of Elf Camp.")
.put(new WorldPoint(2155, 3100, 0), "South of Port Tyras (BJS).")
@@ -187,7 +188,7 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati
.put(new WorldPoint(1698, 3792, 0), "Arceuus church.")
.put(new WorldPoint(2951, 3820, 0), "Wilderness. Chaos Temple (level 38).")
.put(new WorldPoint(2202, 3825, 0), "Pirates' Cove, between Lunar Isle and Rellekka.")
.put(new WorldPoint(1761, 3853, 0), "Arceuus essence mine.")
.put(new WorldPoint(1761, 3853, 0), "Arceuus essence mine (CIS).")
.put(new WorldPoint(2090, 3863, 0), "South of Lunar Isle, west of Astral altar.")
.put(new WorldPoint(1442, 3878, 0), "Sulphur Mine.")
.put(new WorldPoint(3380, 3929, 0), "Wilderness. Near Volcano.")

View File

@@ -61,7 +61,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc
new CrypticClue("Search the crates in a bank in Varrock.", CRATE_5107, new WorldPoint(3187, 9825, 0), "Search in the basement of the West Varrock bank."),
new CrypticClue("Falo the bard wants to see you.", "Falo the Bard", new WorldPoint(2689, 3550, 0), "Speak to Falo the Bard located between Seer's Village and Rellekka. Southwest of fairy ring CJR."),
new CrypticClue("Search a bookcase in the Wizards tower.", BOOKCASE_12539, new WorldPoint(3113, 3159, 0), "The bookcase located on the ground floor."),
new CrypticClue("Come have a cip with this great soot covered denizen.", "Miner Magnus", new WorldPoint(2527, 3891, 0), "Talk to Miner Magnus east of the fairy ring CIP. Answer: 8"),
new CrypticClue("Come have a cip with this great soot covered denizen.", "Miner Magnus", new WorldPoint(2527, 3891, 0), "Talk to Miner Magnus east of the fairy ring CIP. Answer: 8", "How many coal rocks are around here?"),
new CrypticClue("Citric cellar.", "Heckel Funch", new WorldPoint(2490, 3488, 0), "Speak to Heckel Funch on the first floor in the Grand Tree."),
new CrypticClue("I burn between heroes and legends.", "Candle maker", new WorldPoint(2799, 3438, 0), "Speak to the Candle maker in Catherby."),
new CrypticClue("Speak to Sarah at Falador farm.", "Sarah", new WorldPoint(3038, 3292, 0), "Talk to Sarah at Falador farm, north of Port Sarim."),
@@ -110,7 +110,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc
new CrypticClue("Dig between some ominous stones in Falador.", new WorldPoint(3040, 3399, 0), "Three standing stones inside a walled area. East of the northern Falador gate."),
new CrypticClue("Speak to Rusty north of Falador.", "Rusty", new WorldPoint(2979, 3435, 0), "Rusty can be found northeast of Falador on the way to the Mind altar."),
new CrypticClue("Search a wardrobe in Draynor.", WARDROBE_5622, new WorldPoint(3087, 3261, 0), "Go to Aggie's house and search the wardrobe in northern wall."),
new CrypticClue("I have many arms but legs, I have just one. I have little family but my seed you can grow on, I am not dead, yet I am but a spirit, and my power, on your quests, you will earn the right to free it.", NULL_1293, new WorldPoint(2544, 3170, 0), "Spirit Tree in Tree Gnome Village. Answer: 13112221"),
new CrypticClue("I have many arms but legs, I have just one. I have little family but my seed you can grow on, I am not dead, yet I am but a spirit, and my power, on your quests, you will earn the right to free it.", NULL_1293, new WorldPoint(2544, 3170, 0), "Spirit Tree in Tree Gnome Village. Answer: 13112221", "What is the next number in the sequence? 1, 11, 21, 1211, 111221, 312211"),
new CrypticClue("I am the one who watches the giants. The giants in turn watch me. I watch with two while they watch with one. Come seek where I may be.", "Kamfreena", new WorldPoint(2845, 3539, 0), "Speak to Kamfreena on the top floor of the Warriors' Guild."),
new CrypticClue("In a town where wizards are known to gather, search upstairs in a large house to the north.", "Man", 375, new WorldPoint(2593, 3108, 1), "Search the chest upstairs in the house north of Yanille Wizard's Guild. Kill a man for the key."),
new CrypticClue("Probably filled with wizards socks.", "Wizard", DRAWERS_350, new WorldPoint(3116, 9562, 0), "Search the drawers in the basement of the Wizard's Tower south of Draynor Village. Kill one of the Wizards for the key. Fairy ring DIS"),
@@ -138,7 +138,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc
new CrypticClue("46 is my number. My body is the colour of burnt orange and crawls among those with eight. Three mouths I have, yet I cannot eat. My blinking blue eye hides my grave.", new WorldPoint(3170, 3885, 0), "Sapphire respawn in the Spider's Nest, lvl 46 Wilderness. Dig under the sapphire spawn."),
new CrypticClue("Green is the colour of my death as the winter-guise, I swoop towards the ground.", new WorldPoint(2780, 3783, 0), "Players need to slide down to where Trollweiss grows on Trollweiss Mountain."),
new CrypticClue("Talk to a party-goer in Falador.", "Lucy", new WorldPoint(3046, 3382, 0), "Lucy is the bartender on the first floor of the party room."),
new CrypticClue("He knows just how easy it is to lose track of time.", "Brother Kojo", new WorldPoint(2570, 3250, 0), "Speak to Brother Kojo in the Clock Tower. Answer: 22"),
new CrypticClue("He knows just how easy it is to lose track of time.", "Brother Kojo", new WorldPoint(2570, 3250, 0), "Speak to Brother Kojo in the Clock Tower. Answer: 22", "On a clock, how many times a day do the minute hand and the hour hand overlap?"),
new CrypticClue("A great view - watch the rapidly drying hides get splashed. Check the box you are sitting on.", BOXES, new WorldPoint(2523, 3493, 1), "Almera's House north of Baxtorian Falls, search boxes on the first floor."),
new CrypticClue("Search the Coffin in Edgeville.", COFFIN, new WorldPoint(3091, 3477, 0), "Search the coffin located by the Wilderness teleport lever."),
new CrypticClue("When no weapons are at hand, then is the time to reflect. In Saradomin's name, redemption draws closer...", DRAWERS_350, new WorldPoint(2818, 3351, 0), "On Entrana, search the southern drawer in the house with the cooking range."),
@@ -211,7 +211,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc
new CrypticClue("Great demons, dragons and spiders protect this blue rock, beneath which, you may find what you seek.", new WorldPoint(3045, 10265, 0), "Dig by the runite rock in the Lava Maze Dungeon"),
new CrypticClue("My giant guardians below the market streets would be fans of rock and roll, if only they could grab hold of it. Dig near my green bubbles!", new WorldPoint(3161, 9904, 0), "Dig near the cauldron by Moss Giants under Varrock Sewers"),
new CrypticClue("Varrock is where I reside, not the land of the dead, but I am so old, I should be there instead. Let's hope your reward is as good as it says, just 1 gold one and you can have it read.", "Gypsy Aris", new WorldPoint(3203, 3424, 0), "Talk to Gypsy Aris, West of varrock main square."),
new CrypticClue("Speak to a referee.", "Gnome ball referee", new WorldPoint(2386, 3487, 0), "Talk to a Gnome ball referee found on the Gnome ball field in the Gnome Stronghold. Answer: 5096"),
new CrypticClue("Speak to a referee.", "Gnome ball referee", new WorldPoint(2386, 3487, 0), "Talk to a Gnome ball referee found on the Gnome ball field in the Gnome Stronghold. Answer: 5096", "What is 57 x 89 + 23?"),
new CrypticClue("This crate holds a better reward than a broken arrow.", CRATE_356, new WorldPoint(2671, 3437, 0), "Inside the Ranging Guild. Search the crate behind the northern most building."),
new CrypticClue("Search the drawers in the house next to the Port Sarim mage shop.", DRAWERS, new WorldPoint(3024, 3259, 0), "House east of Betty's. Contains a cooking sink."),
new CrypticClue("With a name like that, you'd expect a little more than just a few scimitars.", "Daga", new WorldPoint(2759, 2775, 0), "Speak to Daga on Ape Atoll."),
@@ -300,7 +300,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc
new CrypticClue("Search the open crate found in the Hosidius kitchens.", CRATE_27533, new WorldPoint(1683, 3616, 0), "The kitchens are north-west of the town in Hosidius."),
new CrypticClue("Dig under Ithoi's cabin.", new WorldPoint(2529, 2838, 0), "Dig under Ithoi's cabin in the Corsair Cove."),
new CrypticClue("Search the drawers, upstairs in the bank to the East of Varrock.", DRAWERS_7194, new WorldPoint(3250, 3420, 1), "Search the drawers upstairs in Varrock east bank."),
new CrypticClue("Speak to Hazelmere.", "Hazelmere", new WorldPoint(2677, 3088, 1), "Located upstairs in the house to the north of fairy ring CLS. Answer: 6859"),
new CrypticClue("Speak to Hazelmere.", "Hazelmere", new WorldPoint(2677, 3088, 1), "Located upstairs in the house to the north of fairy ring CLS. Answer: 6859", "What is 19 to the power of 3?"),
new CrypticClue("The effects of this fire are magnified.", new WorldPoint(1179, 3626, 0), "Dig by the fire beside Ket'sal K'uk in the westernmost part of the Kebos Swamp. "),
new CrypticClue("Always walking around the castle grounds and somehow knows everyone's age.", "Hans", new WorldPoint(3221, 3218, 0), "Talk to Hans walking around Lumbridge Castle."),
new CrypticClue("In the place Duke Horacio calls home, talk to a man with a hat dropped by goblins.", "Cook", new WorldPoint(3208, 3213, 0), "Talk to the Cook in Lumbridge Castle."),
@@ -331,6 +331,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc
private final int objectId;
private final WorldPoint location;
private final String solution;
private final String questionText;
private CrypticClue(String text, WorldPoint location, String solution)
{
@@ -339,21 +340,37 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc
private CrypticClue(String text, int objectId, WorldPoint location, String solution)
{
this(text, null, objectId, location, solution);
this(text, null, objectId, location, solution, "");
}
private CrypticClue(String text, String npc, WorldPoint location, String solution)
{
this(text, npc, -1, location, solution);
this(text, npc, -1, location, solution, "");
}
private CrypticClue(String text, int objectId, WorldPoint location, String solution, String questionText)
{
this(text, null, objectId, location, solution, questionText);
}
private CrypticClue(String text, String npc, WorldPoint location, String solution, String questionText)
{
this(text, npc, -1, location, solution, questionText);
}
private CrypticClue(String text, String npc, int objectId, WorldPoint location, String solution)
{
this(text, npc, objectId, location, solution, "");
}
private CrypticClue(String text, String npc, int objectId, WorldPoint location, String solution, String questionText)
{
this.text = text;
this.npc = npc;
this.objectId = objectId;
this.location = location;
this.solution = solution;
this.questionText = questionText;
setRequiresSpade(getLocation() != null && getNpc() == null && objectId == -1);
}
@@ -442,7 +459,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc
{
for (CrypticClue clue : CLUES)
{
if (clue.text.equalsIgnoreCase(text))
if (clue.text.equalsIgnoreCase(text) || clue.questionText.equalsIgnoreCase(text))
{
return clue;
}

View File

@@ -144,7 +144,7 @@ public class SkillChallengeClue extends ClueScroll implements NpcClueScroll
new SkillChallengeClue("Craft multiple cosmic runes from a single essence.", item(ItemID.PURE_ESSENCE)),
new SkillChallengeClue("Plant a watermelon seed.", item(ItemID.RAKE), item(ItemID.SEED_DIBBER), xOfItem(ItemID.WATERMELON_SEED, 3)),
new SkillChallengeClue("Activate the Chivalry prayer."),
new SkillChallengeClue("Hand in a Tier 2 or higher set of Shayzien supply armour", "hand in a boxed set of shayzien supply armour at tier 2 or above.", any("Shayzien Supply Set (Tier 2 or higher)", item(ItemID.SHAYZIEN_SUPPLY_SET_2), item(ItemID.SHAYZIEN_SUPPLY_SET_3), item(ItemID.SHAYZIEN_SUPPLY_SET_4), item(ItemID.SHAYZIEN_SUPPLY_SET_5))),
new SkillChallengeClue("Hand in a Tier 2 or higher set of Shayzien supply armour", "take the lovakengj armourers a boxed set of shayzien supply armour at tier 2 or above.", any("Shayzien Supply Set (Tier 2 or higher)", item(ItemID.SHAYZIEN_SUPPLY_SET_2), item(ItemID.SHAYZIEN_SUPPLY_SET_3), item(ItemID.SHAYZIEN_SUPPLY_SET_4), item(ItemID.SHAYZIEN_SUPPLY_SET_5))),
// Master Sherlock Tasks
new SkillChallengeClue("Equip an abyssal whip in front of the abyssal demons of the Slayer Tower.", true, any("Abyssal Whip", item(ItemID.ABYSSAL_WHIP), item(ItemID.FROZEN_ABYSSAL_WHIP), item(ItemID.VOLCANIC_ABYSSAL_WHIP))),
new SkillChallengeClue("Smith a runite med helm.", item(ItemID.HAMMER), item(ItemID.RUNITE_BAR)),

View File

@@ -38,7 +38,7 @@ import net.runelite.api.coords.WorldPoint;
* <p>
* These puzzles are established by having some way to test the distance from the solution via "warmth", where being
* colder means one is farther away from the target, and being warmer means one is closer to it, with the goal being to
* reach the most warm value to discover the solution point. Hot-cold puzzles in Old School Runescape are implemented
* reach the most warm value to discover the solution point. Hot-cold puzzles in Old School RuneScape are implemented
* with specific set of solution points, so this solver will filter from a provided set of possible solutions as new
* signals of temperatures and temperature changes are provided.
*/

View File

@@ -48,11 +48,16 @@ public class MultipleOfItemRequirement implements ItemRequirement
@Override
public boolean fulfilledBy(Item[] items)
{
int quantityFound = 0;
for (Item item : items)
{
if (item.getId() == itemId && item.getQuantity() >= quantity)
if (item.getId() == itemId)
{
return true;
quantityFound += item.getQuantity();
if (quantityFound >= quantity)
{
return true;
}
}
}

View File

@@ -26,6 +26,7 @@ package net.runelite.client.plugins.config;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import java.awt.BorderLayout;
import java.awt.Color;
@@ -45,6 +46,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
@@ -105,6 +107,7 @@ import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginInstantiationException;
import net.runelite.client.plugins.PluginManager;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.DynamicGridLayout;
import net.runelite.client.ui.FontManager;
@@ -137,7 +140,7 @@ public class ConfigPanel extends PluginPanel
private static final String RUNELITE_GROUP_NAME = RuneLiteConfig.class.getAnnotation(ConfigGroup.class).value();
private static final String PINNED_PLUGINS_CONFIG_KEY = "pinnedPlugins";
private static final String RUNELITE_PLUGIN = "RuneLite";
private static final String openosrs_PLUGIN = "OpenOSRS";
private static final String OPENOSRS_PLUGIN = "OpenOSRS";
private static final String CHAT_COLOR_PLUGIN = "Chat Color";
private final PluginManager pluginManager;
private final ConfigManager configManager;
@@ -156,6 +159,9 @@ public class ConfigPanel extends PluginPanel
private boolean showingPluginList = true;
private int scrollBarPosition = 0;
private static final ImmutableList<PluginType> definedOrder = ImmutableList.of(PluginType.IMPORTANT, PluginType.EXTERNAL, PluginType.PVM, PluginType.SKILLING, PluginType.PVP, PluginType.UTILITY, PluginType.GENERAL_USE);
private static final Comparator<PluginListItem> categoryComparator = Comparator.comparing(plugin -> definedOrder.indexOf(plugin.getPluginType()));
static
{
final BufferedImage backIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "config_back_icon.png");
@@ -282,15 +288,15 @@ public class ConfigPanel extends PluginPanel
// set OpenOSRS config on top, as it should always have been
final PluginListItem openosrs = new PluginListItem(this, configManager, OpenOSRSConfig,
configManager.getConfigDescriptor(OpenOSRSConfig),
openosrs_PLUGIN, "OpenOSRS client settings", "client");
openosrs.setPinned(pinnedPlugins.contains(openosrs_PLUGIN));
OPENOSRS_PLUGIN, "OpenOSRS client settings", PluginType.IMPORTANT, "client");
openosrs.setPinned(pinnedPlugins.contains(OPENOSRS_PLUGIN));
openosrs.nameLabel.setForeground(Color.WHITE);
pluginList.add(openosrs);
// set RuneLite config on top, as it should always have been
final PluginListItem runeLite = new PluginListItem(this, configManager, runeLiteConfig,
configManager.getConfigDescriptor(runeLiteConfig),
RUNELITE_PLUGIN, "RuneLite client settings", "client");
RUNELITE_PLUGIN, "RuneLite client settings", PluginType.IMPORTANT, "client");
runeLite.setPinned(pinnedPlugins.contains(RUNELITE_PLUGIN));
runeLite.nameLabel.setForeground(Color.WHITE);
pluginList.add(runeLite);
@@ -307,18 +313,21 @@ public class ConfigPanel extends PluginPanel
final PluginListItem listItem = new PluginListItem(this, configManager, plugin, descriptor, config, configDescriptor);
listItem.setPinned(pinnedPlugins.contains(listItem.getName()));
listItem.setColor(getColorByCategory(OpenOSRSConfig, listItem.getPluginType()));
listItem.setHidden(getHiddenByCategory(OpenOSRSConfig, listItem.getPluginType()));
plugins.add(listItem);
}
);
final PluginListItem chatColor = new PluginListItem(this, configManager, chatColorConfig,
configManager.getConfigDescriptor(chatColorConfig),
CHAT_COLOR_PLUGIN, "Recolor chat text", "colour", "messages");
CHAT_COLOR_PLUGIN, "Recolor chat text", PluginType.GENERAL_USE, "colour", "messages");
chatColor.setPinned(pinnedPlugins.contains(CHAT_COLOR_PLUGIN));
chatColor.nameLabel.setForeground(Color.WHITE);
plugins.add(chatColor);
pluginList.addAll(plugins);
ConfigPanel.sortPluginList(OpenOSRSConfig, null);
}
void refreshPluginList()
@@ -466,10 +475,17 @@ public class ConfigPanel extends PluginPanel
String name = listItem.getName();
JLabel title = new JLabel(name);
title.setForeground(Color.WHITE);
title.setForeground(listItem.getColor());
title.setToolTipText("<html>" + name + ":<br>" + listItem.getDescription() + "</html>");
topPanel.add(title);
IconButton toggleButton = new IconButton(PluginListItem.OFF_SWITCHER);
toggleButton.setPreferredSize(new Dimension(25, 0));
listItem.updateToggleButton(toggleButton);
listItem.attachToggleButtonListener(toggleButton);
topPanel.add(toggleButton, BorderLayout.EAST);
final Map<String, JPanel> sectionWidgets = new HashMap<>();
final Map<String, JPanel> titleSectionWidgets = new HashMap<>();
@@ -1379,4 +1395,70 @@ public class ConfigPanel extends PluginPanel
{
openGroupConfigPanel(listItem, config, cd, true);
}
public static Color getColorByCategory(OpenOSRSConfig openOSRSConfig, PluginType pluginType)
{
switch (pluginType)
{
case EXTERNAL:
return openOSRSConfig.externalColor();
case PVM:
return openOSRSConfig.pvmColor();
case PVP:
return openOSRSConfig.pvpColor();
case SKILLING:
return openOSRSConfig.skillingColor();
case UTILITY:
return openOSRSConfig.utilityColor();
}
return null;
}
public static boolean getHiddenByCategory(OpenOSRSConfig openOSRSConfig, PluginType pluginType)
{
if (pluginType == PluginType.IMPORTANT || pluginType == PluginType.GENERAL_USE)
{
return false;
}
if (openOSRSConfig.hidePlugins())
{
return true;
}
switch (pluginType)
{
case EXTERNAL:
return openOSRSConfig.hideExternalPlugins();
case PVM:
return openOSRSConfig.hidePvmPlugins();
case PVP:
return openOSRSConfig.hidePvpPlugins();
case SKILLING:
return openOSRSConfig.hideSkillingPlugins();
case UTILITY:
return openOSRSConfig.hideUtilityPlugins();
}
return false;
}
public static void sortPluginList(OpenOSRSConfig openOSRSConfig, Comparator<PluginListItem> comparator)
{
if (comparator != null)
{
ConfigPanel.pluginList.sort(comparator.thenComparing(PluginListItem::getName));
return;
}
if (openOSRSConfig.pluginSortMode() == net.runelite.client.config.OpenOSRSConfig.SortStyle.CATEGORY)
{
ConfigPanel.pluginList.sort(categoryComparator.thenComparing(PluginListItem::getName));
}
else
{
ConfigPanel.pluginList.sort(Comparator.comparing(PluginListItem::getName));
}
}
}

View File

@@ -25,6 +25,7 @@
package net.runelite.client.plugins.config;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
@@ -43,6 +44,7 @@ import net.runelite.client.config.ConfigDescriptor;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.IconButton;
@@ -58,7 +60,7 @@ public class PluginListItem extends JPanel
private static final ImageIcon CONFIG_ICON;
private static final ImageIcon CONFIG_ICON_HOVER;
private static final ImageIcon ON_SWITCHER;
private static final ImageIcon OFF_SWITCHER;
public static final ImageIcon OFF_SWITCHER;
private static final ImageIcon ON_STAR;
private static final ImageIcon OFF_STAR;
@@ -82,12 +84,16 @@ public class PluginListItem extends JPanel
@Getter(AccessLevel.PUBLIC)
private final String description;
@Getter(AccessLevel.PUBLIC)
private final PluginType pluginType;
private final List<String> keywords = new ArrayList<>();
private final IconButton pinButton = new IconButton(OFF_STAR);
private final IconButton configButton = new IconButton(CONFIG_ICON, CONFIG_ICON_HOVER);
private final IconButton toggleButton = new IconButton(OFF_SWITCHER);
@Getter
private boolean isPluginEnabled = false;
@Getter
@@ -96,6 +102,8 @@ public class PluginListItem extends JPanel
@Getter
private boolean isHidden = false;
private Color color = null;
static
{
BufferedImage configIcon = ImageUtil.getResourceStreamFromClass(ConfigPanel.class, "config_edit_icon.png");
@@ -131,20 +139,20 @@ public class PluginListItem extends JPanel
@Nullable Config config, @Nullable ConfigDescriptor configDescriptor)
{
this(configPanel, configManager, plugin, config, configDescriptor,
descriptor.name(), descriptor.description(), descriptor.tags());
descriptor.name(), descriptor.description(), descriptor.type(), descriptor.tags());
}
/**
* Creates a new {@code PluginListItem} for a core configuration.
*/
PluginListItem(ConfigPanel configPanel, ConfigManager configManager, Config config, ConfigDescriptor configDescriptor,
String name, String description, String... tags)
String name, String description, PluginType pluginType, String... tags)
{
this(configPanel, configManager, null, config, configDescriptor, name, description, tags);
this(configPanel, configManager, null, config, configDescriptor, name, description, pluginType, tags);
}
private PluginListItem(ConfigPanel configPanel, ConfigManager configManager, @Nullable Plugin plugin, @Nullable Config config,
@Nullable ConfigDescriptor configDescriptor, String name, String description, String... tags)
@Nullable ConfigDescriptor configDescriptor, String name, String description, PluginType pluginType, String... tags)
{
this.configPanel = configPanel;
this.plugin = plugin;
@@ -152,6 +160,7 @@ public class PluginListItem extends JPanel
this.configDescriptor = configDescriptor;
this.name = name;
this.description = description;
this.pluginType = pluginType;
Collections.addAll(keywords, name.toLowerCase().split(" "));
Collections.addAll(keywords, description.toLowerCase().split(" "));
Collections.addAll(keywords, tags);
@@ -206,7 +215,7 @@ public class PluginListItem extends JPanel
buttonPanel.add(toggleButton);
}
private void attachToggleButtonListener(IconButton button)
void attachToggleButtonListener(IconButton button)
{
// no need for a listener if there is no plugin to enable / disable
if (plugin == null)
@@ -231,15 +240,6 @@ public class PluginListItem extends JPanel
});
}
IconButton createToggleButton()
{
IconButton button = new IconButton(OFF_SWITCHER);
button.setPreferredSize(new Dimension(25, 0));
updateToggleButton(button);
attachToggleButtonListener(button);
return button;
}
void setPluginEnabled(boolean enabled)
{
isPluginEnabled = enabled;
@@ -253,12 +253,28 @@ public class PluginListItem extends JPanel
pinButton.setToolTipText(pinned ? "Unpin plugin" : "Pin plugin");
}
Color getColor()
{
return this.color == null ? Color.WHITE : this.color;
}
public void setColor(Color color)
{
if (color == null)
{
return;
}
this.color = color;
this.nameLabel.setForeground(color);
}
public void setHidden(boolean hidden)
{
isHidden = hidden;
}
private void updateToggleButton(IconButton button)
void updateToggleButton(IconButton button)
{
button.setIcon(isPluginEnabled ? ON_SWITCHER : OFF_SWITCHER);
button.setToolTipText(isPluginEnabled ? "Disable plugin" : "Enable plugin");

View File

@@ -258,7 +258,7 @@ public class CorpPlugin extends Plugin
}
event.setOpcode(NPC_SECOND_OPTION.getId() + MENU_ACTION_DEPRIORITIZE_OFFSET);
event.setModified(true);
event.setModified();
}
private void onConfigChanged(ConfigChanged configChanged)

View File

@@ -223,7 +223,7 @@ public class CoxPlugin extends Plugin
{
final String fixedPlayerName = Text.sanitize(rawPlayerName);
if (fixedPlayerName.equals(tpMatcher.group(1)))
if (fixedPlayerName.equals(Text.sanitize(tpMatcher.group(1))))
{
victims.add(new Victim(player, Victim.Type.TELEPORT));
}

View File

@@ -0,0 +1,42 @@
package net.runelite.client.plugins.deathindicator;
import java.time.Duration;
import java.time.Instant;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.Scene;
import net.runelite.api.coords.WorldPoint;
import static net.runelite.client.plugins.deathindicator.DeathIndicatorPlugin.HIJACKED_ITEMID;
import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.MiscUtils;
@EqualsAndHashCode
@Getter
@Setter
class Bone
{
private String name;
private WorldPoint loc;
private Instant time;
void addToScene(Scene scene)
{
scene.addItem(HIJACKED_ITEMID, 1, loc);
}
void removeFromScene(Scene scene)
{
scene.removeItem(HIJACKED_ITEMID, 1, loc);
}
String getName()
{
return ColorUtil.colorStartTag(0xff9040) + "Bones (" + name + ")";
}
String getExamine()
{
return name + " died here " + MiscUtils.formatTimeAgo(Duration.between(time, Instant.now()));
}
}

View File

@@ -0,0 +1,190 @@
package net.runelite.client.plugins.deathindicator;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.Scene;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.config.ConfigManager;
import static net.runelite.http.api.RuneLiteAPI.GSON;
@Slf4j
public class Bones
{
private static final String CONFIG_GROUP = "deathIndicator";
private static final String BONES_PREFIX = "bones_";
private ImmutableMap<Integer, Map<WorldPoint, List<Bone>>> map;
void init(Client client, ConfigManager configManager)
{
// Clone is important here as the normal array changes
int[] regions = client.getMapRegions().clone();
Bone[][] bones = getBones(configManager, regions);
if (log.isDebugEnabled())
{
log.trace("Regions are now {}", Arrays.toString(regions));
int n = 0;
for (Bone[] ar : bones)
{
n += ar.length;
}
log.debug("Loaded {} Bones", n);
}
initMap(regions, bones);
showBones(client.getScene());
}
private Bone[][] getBones(ConfigManager configManager, int[] regions)
{
Bone[][] bones = new Bone[regions.length][];
for (int i = 0; i < regions.length; i++)
{
int region = regions[i];
bones[i] = getBones(configManager, region);
}
return bones;
}
private Bone[] getBones(ConfigManager configManager, int regionId)
{
String json = configManager.getConfiguration(CONFIG_GROUP, BONES_PREFIX + regionId);
if (json == null)
{
return new Bone[0];
}
return GSON.fromJson(json, Bone[].class);
}
@SuppressWarnings("unchecked")
private void initMap(int[] regions, Bone[][] bones)
{
ImmutableMap.Builder<Integer, Map<WorldPoint, List<Bone>>> builder = ImmutableMap.builder();
for (int i = 0; i < regions.length; i++)
{
int region = regions[i];
Bone[] boneA = bones[i];
if (boneA.length == 0)
{
builder.put(region, new HashMap<>());
continue;
}
Map<WorldPoint, List<Bone>> map = new HashMap(boneA.length);
for (Bone b : boneA)
{
List<Bone> list = map.computeIfAbsent(b.getLoc(), wp -> new ArrayList<>());
list.add(b);
}
builder.put(region, map);
}
this.map = builder.build();
}
private void showBones(Scene scene)
{
this.forEach(bone -> bone.addToScene(scene));
}
void save(ConfigManager configManager, int region)
{
final Map<WorldPoint, List<Bone>> regionBones = this.map.get(region);
if (regionBones == null)
{
return;
}
final String key = BONES_PREFIX + region;
if (regionBones.size() == 0)
{
configManager.unsetConfiguration(CONFIG_GROUP, key);
}
List<Bone> list = new ArrayList<>(regionBones.values().size());
for (List<Bone> lb : regionBones.values())
{
list.addAll(lb);
}
String val = GSON.toJson(list.toArray(new Bone[0]));
configManager.setConfiguration(CONFIG_GROUP, key, val);
}
public boolean add(Bone bone)
{
if (this.map == null)
{
return false;
}
final int region = bone.getLoc().getRegionID();
final Map<WorldPoint, List<Bone>> map = this.map.get(region);
final List<Bone> list = map.computeIfAbsent(bone.getLoc(), wp -> new ArrayList<>());
list.add(bone);
return true;
}
public void remove(Bone bone)
{
final int region = bone.getLoc().getRegionID();
final Map<WorldPoint, List<Bone>> map = this.map.get(region);
final List<Bone> list = map.get(bone.getLoc());
list.remove(bone);
}
public void clear(Scene scene)
{
if (map == null)
{
return;
}
this.forEach(bone -> bone.removeFromScene(scene));
}
public Bone get(WorldPoint point, int i)
{
return get(point).get(i);
}
public List<Bone> get(WorldPoint point)
{
final int reg = point.getRegionID();
final Map<WorldPoint, List<Bone>> map = this.map.get(reg);
if (map == null)
{
return null;
}
return map.get(point);
}
private void forEach(Consumer<Bone> consumer)
{
for (Map<WorldPoint, List<Bone>> map : this.map.values())
{
for (Map.Entry<WorldPoint, List<Bone>> entry : map.entrySet())
{
for (Bone bone : entry.getValue())
{
consumer.accept(bone);
}
}
}
}
}

View File

@@ -152,4 +152,14 @@ public interface DeathIndicatorConfig extends Config
description = ""
)
void timeOfDeath(Instant timeOfDeath);
@ConfigItem(
keyName = "permaBones",
name = "Permanent bones",
description = "Show right clickable bones with the name of who died permanently, after seeing someone die"
)
default boolean permaBones()
{
return false;
}
}

View File

@@ -30,18 +30,32 @@ import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.ItemDefinition;
import net.runelite.api.ItemID;
import net.runelite.api.MenuEntry;
import net.runelite.api.MenuOpcode;
import net.runelite.api.Player;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.ItemDespawned;
import net.runelite.api.events.LocalPlayerDeath;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOpened;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.PlayerDeath;
import net.runelite.api.events.PostItemDefinition;
import net.runelite.api.util.Text;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.game.ItemManager;
@@ -61,6 +75,10 @@ import net.runelite.client.util.ImageUtil;
@Slf4j
public class DeathIndicatorPlugin extends Plugin
{
private static final Object BONES = new Object();
// A random number, that jagex probably won't actually use in the near future
static final int HIJACKED_ITEMID = 0x69696969;
private static final Set<Integer> RESPAWN_REGIONS = ImmutableSet.of(
12850, // Lumbridge
11828, // Falador
@@ -88,6 +106,14 @@ public class DeathIndicatorPlugin extends Plugin
@Inject
private EventBus eventBus;
@Inject
private ConfigManager configManager;
@Inject
private ClientThread clientThread;
private final Bones bones = new Bones();
private BufferedImage mapArrow;
private Timer deathTimer;
@@ -95,7 +121,7 @@ public class DeathIndicatorPlugin extends Plugin
private WorldPoint lastDeath;
private Instant lastDeathTime;
private int lastDeathWorld;
private int despawnIdx = 0;
@Provides
DeathIndicatorConfig deathIndicatorConfig(ConfigManager configManager)
{
@@ -129,12 +155,18 @@ public class DeathIndicatorPlugin extends Plugin
worldMapPointManager.removeIf(DeathWorldMapPoint.class::isInstance);
worldMapPointManager.add(new DeathWorldMapPoint(new WorldPoint(config.deathLocationX(), config.deathLocationY(), config.deathLocationPlane()), this));
}
if (config.permaBones() && client.getGameState() == GameState.LOGGED_IN)
{
clientThread.invokeLater(this::initBones);
}
}
@Override
protected void shutDown()
{
eventBus.unregister(this);
eventBus.unregister(BONES);
if (client.hasHintArrow())
{
@@ -148,6 +180,18 @@ public class DeathIndicatorPlugin extends Plugin
}
worldMapPointManager.removeIf(DeathWorldMapPoint.class::isInstance);
clientThread.invokeLater(this::clearBones);
}
private void initBones()
{
bones.init(client, configManager);
}
private void clearBones()
{
bones.clear(client.getScene());
}
private void addSubscriptions()
@@ -156,6 +200,105 @@ public class DeathIndicatorPlugin extends Plugin
eventBus.subscribe(LocalPlayerDeath.class, this, this::onLocalPlayerDeath);
eventBus.subscribe(GameTick.class, this, this::onGameTick);
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
if (config.permaBones())
{
addBoneSubs();
}
}
private void addBoneSubs()
{
eventBus.subscribe(ItemDespawned.class, BONES, this::onItemDespawn);
eventBus.subscribe(PlayerDeath.class, BONES, this::onPlayerDeath);
eventBus.subscribe(MenuEntryAdded.class, BONES, this::onMenuEntryAdded);
eventBus.subscribe(MenuOptionClicked.class, BONES, this::onMenuOptionClicked);
eventBus.subscribe(MenuOpened.class, BONES, this::onMenuOpened);
eventBus.subscribe(PostItemDefinition.class, BONES, this::onPostItemDefinition);
}
private void onPostItemDefinition(PostItemDefinition def)
{
ItemDefinition itemDef = def.getItemDefinition();
if (itemDef.getId() == HIJACKED_ITEMID)
{
itemDef.setModelOverride(ItemID.BONES);
itemDef.setName("Bones");
// This is so never hide untradeables doesn't not hide it
itemDef.setTradeable(true);
}
}
private void onPlayerDeath(PlayerDeath death)
{
newBoneFor(death.getPlayer());
}
private void newBoneFor(Player player)
{
Bone b = new Bone();
b.setName(Text.sanitize(player.getName()));
b.setTime(Instant.now());
b.setLoc(player.getWorldLocation());
while (!bones.add(b))
{
initBones();
}
b.addToScene(client.getScene());
bones.save(configManager, b.getLoc().getRegionID());
}
private void onMenuEntryAdded(MenuEntryAdded event)
{
if (event.getIdentifier() == HIJACKED_ITEMID)
{
if (event.getOpcode() == MenuOpcode.GROUND_ITEM_THIRD_OPTION.getId())
{
client.setMenuOptionCount(client.getMenuOptionCount() - 1);
}
}
}
private void onMenuOpened(MenuOpened event)
{
int idx = 0;
for (MenuEntry entry : event)
{
if (entry.getIdentifier() != HIJACKED_ITEMID)
{
continue;
}
// Only entries with appropriate identifier here will be examine so that's easy
// Add idx to id field, so we can find that back from clicked event
entry.setIdentifier(HIJACKED_ITEMID + idx);
Bone bone = bones.get(
WorldPoint.fromScene(client, entry.getParam0(), entry.getParam1(), client.getPlane()),
idx++
);
entry.setTarget(bone.getName());
event.setModified();
}
}
private void onMenuOptionClicked(MenuOptionClicked event)
{
if (event.getIdentifier() >= HIJACKED_ITEMID
&& event.getOpcode() == MenuOpcode.EXAMINE_ITEM_GROUND.getId())
{
Bone b = bones.get(
WorldPoint.fromScene(client, event.getParam0(), event.getParam1(), client.getPlane()),
event.getIdentifier() - HIJACKED_ITEMID
);
client.addChatMessage(ChatMessageType.ITEM_EXAMINE, "", b.getExamine(), "");
event.consume();
}
}
private void onLocalPlayerDeath(LocalPlayerDeath death)
@@ -165,7 +308,13 @@ public class DeathIndicatorPlugin extends Plugin
return;
}
lastDeath = client.getLocalPlayer().getWorldLocation();
Player lp = client.getLocalPlayer();
if (config.permaBones())
{
newBoneFor(lp);
}
lastDeath = lp.getWorldLocation();
lastDeathWorld = client.getWorld();
lastDeathTime = Instant.now();
}
@@ -238,6 +387,28 @@ public class DeathIndicatorPlugin extends Plugin
{
if (event.getGroup().equals("deathIndicator"))
{
if ("permaBones".equals(event.getKey()))
{
if (config.permaBones())
{
addBoneSubs();
if (client.getGameState() == GameState.LOGGED_IN)
{
clientThread.invokeLater(this::initBones);
}
}
else
{
eventBus.unregister(BONES);
if (client.getGameState() == GameState.LOGGED_IN)
{
clientThread.invokeLater(this::clearBones);
}
}
return;
}
if (!config.showDeathHintArrow() && hasDied())
{
client.clearHintArrow();
@@ -267,32 +438,61 @@ public class DeathIndicatorPlugin extends Plugin
private void onGameStateChanged(GameStateChanged event)
{
if (!hasDied())
switch (event.getGameState())
{
return;
}
if (event.getGameState() == GameState.LOGGED_IN)
{
if (client.getWorld() == config.deathWorld())
{
WorldPoint deathPoint = new WorldPoint(config.deathLocationX(), config.deathLocationY(), config.deathLocationPlane());
if (config.showDeathHintArrow())
case LOADING:
clearBones();
break;
case LOGGED_IN:
if (config.permaBones())
{
client.setHintArrow(deathPoint);
initBones();
}
if (config.showDeathOnWorldMap())
if (!hasDied())
{
return;
}
if (client.getWorld() == config.deathWorld())
{
WorldPoint deathPoint = new WorldPoint(config.deathLocationX(), config.deathLocationY(), config.deathLocationPlane());
if (config.showDeathHintArrow())
{
client.setHintArrow(deathPoint);
}
if (config.showDeathOnWorldMap())
{
worldMapPointManager.removeIf(DeathWorldMapPoint.class::isInstance);
worldMapPointManager.add(new DeathWorldMapPoint(deathPoint, this));
}
}
else
{
worldMapPointManager.removeIf(DeathWorldMapPoint.class::isInstance);
worldMapPointManager.add(new DeathWorldMapPoint(deathPoint, this));
}
}
else
break;
}
}
private void onItemDespawn(ItemDespawned event)
{
if (event.getItem().getId() == HIJACKED_ITEMID)
{
List<Bone> list = bones.get(event.getTile().getWorldLocation());
if (list == null)
{
worldMapPointManager.removeIf(DeathWorldMapPoint.class::isInstance);
return;
}
if (list.size() <= despawnIdx)
{
despawnIdx = 0;
}
Bone bone = list.get(despawnIdx++);
bone.addToScene(client.getScene());
}
}

View File

@@ -37,8 +37,6 @@ import java.awt.geom.Rectangle2D;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.Actor;
import net.runelite.api.Client;
import net.runelite.api.Constants;
@@ -76,9 +74,6 @@ import net.runelite.client.ui.overlay.tooltip.TooltipManager;
@Singleton
class DevToolsOverlay extends Overlay
{
private static final int ITEM_EMPTY = 6512;
private static final int ITEM_FILLED = 20594;
private static final Font FONT = FontManager.getRunescapeFont().deriveFont(Font.BOLD, 16);
private static final Color RED = new Color(221, 44, 0);
private static final Color GREEN = new Color(0, 200, 83);
@@ -97,13 +92,6 @@ class DevToolsOverlay extends Overlay
private final DevToolsPlugin plugin;
private final TooltipManager toolTipManager;
@Setter
@Getter
private Widget widget;
@Setter
private int itemIndex = -1;
@Inject
private DevToolsOverlay(Client client, DevToolsPlugin plugin, TooltipManager toolTipManager)
{
@@ -155,8 +143,6 @@ class DevToolsOverlay extends Overlay
renderCursorTooltip(graphics);
}
renderWidgets(graphics);
return null;
}
@@ -490,70 +476,6 @@ class DevToolsOverlay extends Overlay
}
}
private void renderWidgets(Graphics2D graphics)
{
if (widget == null || widget.isHidden())
{
return;
}
Rectangle childBounds = widget.getBounds();
graphics.setColor(CYAN);
graphics.draw(childBounds);
if (itemIndex == -1)
{
return;
}
if (widget.getItemId() != ITEM_EMPTY
&& widget.getItemId() != ITEM_FILLED)
{
Rectangle componentBounds = widget.getBounds();
graphics.setColor(ORANGE);
graphics.draw(componentBounds);
renderWidgetText(graphics, componentBounds, widget.getItemId(), YELLOW);
}
WidgetItem widgetItem = widget.getWidgetItem(itemIndex);
if (widgetItem == null
|| widgetItem.getId() < 0
|| widgetItem.getId() == ITEM_EMPTY
|| widgetItem.getId() == ITEM_FILLED)
{
return;
}
Rectangle itemBounds = widgetItem.getCanvasBounds();
graphics.setColor(ORANGE);
graphics.draw(itemBounds);
renderWidgetText(graphics, itemBounds, widgetItem.getId(), YELLOW);
}
private void renderWidgetText(Graphics2D graphics, Rectangle bounds, int itemId, Color color)
{
if (itemId == -1)
{
return;
}
String text = itemId + "";
FontMetrics fm = graphics.getFontMetrics();
Rectangle2D textBounds = fm.getStringBounds(text, graphics);
int textX = (int) (bounds.getX() + (bounds.getWidth() / 2) - (textBounds.getWidth() / 2));
int textY = (int) (bounds.getY() + (bounds.getHeight() / 2) + (textBounds.getHeight() / 2));
graphics.setColor(Color.BLACK);
graphics.drawString(text, textX + 1, textY + 1);
graphics.setColor(color);
graphics.drawString(text, textX, textY);
}
private void renderPlayerWireframe(Graphics2D graphics, Player player, Color color)
{
Polygon[] polys = player.getPolygons();

View File

@@ -42,11 +42,10 @@ import net.runelite.api.Player;
import net.runelite.api.Skill;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AreaSoundEffectPlayed;
import net.runelite.api.events.BoostedLevelChanged;
import net.runelite.api.events.CommandExecuted;
import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.SoundEffectPlayed;
import net.runelite.api.events.StatChanged;
import net.runelite.api.events.VarbitChanged;
import net.runelite.api.kit.KitType;
import net.runelite.client.config.ConfigManager;
@@ -301,9 +300,13 @@ public class DevToolsPlugin extends Plugin
client.queueChangedSkill(skill);
ExperienceChanged experienceChanged = new ExperienceChanged();
experienceChanged.setSkill(skill);
eventBus.post(ExperienceChanged.class, experienceChanged);
StatChanged statChanged = new StatChanged(
skill,
totalXp,
level,
level
);
eventBus.post(StatChanged.class, statChanged);
break;
}
case "setstat":
@@ -320,13 +323,13 @@ public class DevToolsPlugin extends Plugin
client.queueChangedSkill(skill);
ExperienceChanged experienceChanged = new ExperienceChanged();
experienceChanged.setSkill(skill);
eventBus.post(ExperienceChanged.class, experienceChanged);
BoostedLevelChanged boostedLevelChanged = new BoostedLevelChanged();
boostedLevelChanged.setSkill(skill);
eventBus.post(BoostedLevelChanged.class, boostedLevelChanged);
StatChanged statChanged = new StatChanged(
skill,
xp,
level,
level
);
eventBus.post(StatChanged.class, statChanged);
break;
}
case "anim":
@@ -407,7 +410,7 @@ public class DevToolsPlugin extends Plugin
}
entry.setTarget(entry.getTarget() + " " + ColorUtil.prependColorTag("(" + info + ")", JagexColors.MENU_TARGET));
entry.setModified(true);
entry.setModified();
}
}

View File

@@ -110,6 +110,7 @@ class SoundEffectOverlay extends Overlay
String text =
"Id: " + event.getSoundId() +
" - S: " + (event.getSource() != null ? event.getSource().getName() : "<none>") +
" - L: " + event.getSceneX() + "," + event.getSceneY() +
" - R: " + event.getRange() +
" - D: " + event.getDelay();

View File

@@ -27,13 +27,20 @@
package net.runelite.client.plugins.devtools;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Collection;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.stream.Stream;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
@@ -45,29 +52,67 @@ import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.MenuEntry;
import net.runelite.api.MenuOpcode;
import net.runelite.api.SpriteID;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetConfig;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.api.widgets.WidgetItem;
import net.runelite.api.widgets.WidgetType;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.ui.ClientUI;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.util.ColorUtil;
@Slf4j
@Singleton
class WidgetInspector extends JFrame
{
private static final Map<Integer, WidgetInfo> widgetIdMap = new HashMap<>();
static final Color SELECTED_WIDGET_COLOR = Color.CYAN;
private static final float SELECTED_WIDGET_HUE;
static
{
float[] hsb = new float[3];
Color.RGBtoHSB(SELECTED_WIDGET_COLOR.getRed(), SELECTED_WIDGET_COLOR.getGreen(), SELECTED_WIDGET_COLOR.getBlue(), hsb);
SELECTED_WIDGET_HUE = hsb[0];
}
private final Client client;
private final ClientThread clientThread;
private final DevToolsConfig config;
private final DevToolsOverlay overlay;
private final Provider<WidgetInspectorOverlay> overlay;
private final OverlayManager overlayManager;
private final JTree widgetTree;
private final WidgetInfoTableModel infoTableModel;
private final JCheckBox alwaysOnTop;
private final JCheckBox hideHidden;
private static final Map<Integer, WidgetInfo> widgetIdMap = new HashMap<>();
private DefaultMutableTreeNode root;
@Getter
private Widget selectedWidget;
@Getter
private int selectedItem;
private Widget picker = null;
@Getter
private boolean pickerSelected = false;
@Inject
private WidgetInspector(
@@ -75,17 +120,21 @@ class WidgetInspector extends JFrame
ClientThread clientThread,
WidgetInfoTableModel infoTableModel,
DevToolsConfig config,
DevToolsPlugin plugin,
EventBus eventBus,
DevToolsOverlay overlay,
DevToolsPlugin plugin)
Provider<WidgetInspectorOverlay> overlay,
OverlayManager overlayManager)
{
this.client = client;
this.clientThread = clientThread;
this.infoTableModel = infoTableModel;
this.config = config;
this.overlay = overlay;
this.overlayManager = overlayManager;
eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
eventBus.subscribe(MenuOptionClicked.class, this, this::onMenuOptionClicked);
eventBus.subscribe(MenuEntryAdded.class, this, this::onMenuEntryAdded);
setTitle("RuneLite Widget Inspector");
setIconImage(ClientUI.ICON);
@@ -96,7 +145,6 @@ class WidgetInspector extends JFrame
@Override
public void windowClosing(WindowEvent e)
{
eventBus.unregister(this);
close();
plugin.getWidgetInspector().setActive(false);
}
@@ -114,16 +162,12 @@ class WidgetInspector extends JFrame
{
WidgetTreeNode node = (WidgetTreeNode) selected;
Widget widget = node.getWidget();
overlay.setWidget(widget);
overlay.setItemIndex(widget.getItemId());
refreshInfo();
log.debug("Set widget to {} and item index to {}", widget, widget.getItemId());
setSelectedWidget(widget, -1, false);
}
else if (selected instanceof WidgetItemNode)
{
WidgetItemNode node = (WidgetItemNode) selected;
overlay.setItemIndex(node.getWidgetItem().getIndex());
log.debug("Set item index to {}", node.getWidgetItem().getIndex());
setSelectedWidget(node.getWidgetItem().getWidget(), node.getWidgetItem().getIndex(), false);
}
});
@@ -149,15 +193,20 @@ class WidgetInspector extends JFrame
onConfigChanged(null);
bottomPanel.add(alwaysOnTop);
hideHidden = new JCheckBox("Hide hidden");
hideHidden.setSelected(true);
hideHidden.addItemListener(ev -> refreshWidgets());
bottomPanel.add(hideHidden);
final JButton revalidateWidget = new JButton("Revalidate");
revalidateWidget.addActionListener(ev -> clientThread.invokeLater(() ->
{
if (overlay.getWidget() == null)
if (selectedWidget == null)
{
return;
}
overlay.getWidget().revalidate();
selectedWidget.revalidate();
}));
bottomPanel.add(revalidateWidget);
@@ -165,7 +214,6 @@ class WidgetInspector extends JFrame
add(split, BorderLayout.CENTER);
pack();
}
private void onConfigChanged(ConfigChanged ev)
@@ -180,10 +228,13 @@ class WidgetInspector extends JFrame
clientThread.invokeLater(() ->
{
Widget[] rootWidgets = client.getWidgetRoots();
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
root = new DefaultMutableTreeNode();
overlay.setWidget(null);
overlay.setItemIndex(-1);
Widget wasSelectedWidget = selectedWidget;
int wasSelectedItem = selectedItem;
selectedWidget = null;
selectedItem = -1;
for (Widget widget : rootWidgets)
{
@@ -196,17 +247,15 @@ class WidgetInspector extends JFrame
SwingUtilities.invokeLater(() ->
{
overlay.setWidget(null);
overlay.setItemIndex(-1);
refreshInfo();
widgetTree.setModel(new DefaultTreeModel(root));
setSelectedWidget(wasSelectedWidget, wasSelectedItem, true);
});
});
}
private DefaultMutableTreeNode addWidget(String type, Widget widget)
{
if (widget == null || widget.isHidden())
if (widget == null || (hideHidden.isSelected() && widget.isHidden()))
{
return null;
}
@@ -269,9 +318,70 @@ class WidgetInspector extends JFrame
return node;
}
private void refreshInfo()
private void setSelectedWidget(Widget widget, int item, boolean updateTree)
{
infoTableModel.setWidget(overlay.getWidget());
infoTableModel.setWidget(widget);
if (this.selectedWidget == widget && this.selectedItem == item)
{
return;
}
this.selectedWidget = widget;
this.selectedItem = item;
if (root == null || !updateTree)
{
return;
}
clientThread.invoke(() ->
{
Stack<Widget> treePath = new Stack<>();
for (Widget w = widget; w != null; w = w.getParent())
{
treePath.push(w);
}
DefaultMutableTreeNode node = root;
deeper:
for (; !treePath.empty(); )
{
Widget w = treePath.pop();
for (Enumeration<?> it = node.children(); it.hasMoreElements(); )
{
WidgetTreeNode inner = (WidgetTreeNode) it.nextElement();
if (inner.getWidget().getId() == w.getId() && inner.getWidget().getIndex() == w.getIndex())
{
node = inner;
continue deeper;
}
}
}
if (selectedItem != -1)
{
for (Enumeration<?> it = node.children(); it.hasMoreElements(); )
{
Object wiw = it.nextElement();
if (wiw instanceof WidgetItemNode)
{
WidgetItemNode inner = (WidgetItemNode) wiw;
if (inner.getWidgetItem().getIndex() == selectedItem)
{
node = inner;
break;
}
}
}
}
final DefaultMutableTreeNode fnode = node;
SwingUtilities.invokeLater(() ->
{
widgetTree.getSelectionModel().clearSelection();
widgetTree.getSelectionModel().addSelectionPath(new TreePath(fnode.getPath()));
});
});
}
static WidgetInfo getWidgetInfo(int packedId)
@@ -295,12 +405,176 @@ class WidgetInspector extends JFrame
setVisible(true);
toFront();
repaint();
overlayManager.add(this.overlay.get());
clientThread.invokeLater(this::addPickerWidget);
}
public void close()
{
overlay.setWidget(null);
overlay.setItemIndex(-1);
overlayManager.remove(this.overlay.get());
clientThread.invokeLater(this::removePickerWidget);
setSelectedWidget(null, -1, false);
setVisible(false);
}
}
private void removePickerWidget()
{
if (picker == null)
{
return;
}
Widget parent = picker.getParent();
if (parent == null)
{
return;
}
Widget[] children = parent.getChildren();
if (children == null || children.length <= picker.getIndex() || children[picker.getIndex()] != picker)
{
return;
}
children[picker.getIndex()] = null;
}
private void addPickerWidget()
{
removePickerWidget();
int x = 10, y = 2;
Widget parent = client.getWidget(WidgetInfo.MINIMAP_ORBS);
if (parent == null)
{
Widget[] roots = client.getWidgetRoots();
parent = Stream.of(roots)
.filter(w -> w.getType() == WidgetType.LAYER && w.getContentType() == 0 && !w.isSelfHidden())
.sorted(Comparator.comparing((Widget w) -> w.getRelativeX() + w.getRelativeY())
.reversed()
.thenComparing(Widget::getId)
.reversed())
.findFirst().get();
x = 4;
y = 4;
}
picker = parent.createChild(-1, WidgetType.GRAPHIC);
log.info("Picker is {}.{} [{}]", WidgetInfo.TO_GROUP(picker.getId()), WidgetInfo.TO_CHILD(picker.getId()), picker.getIndex());
picker.setSpriteId(SpriteID.MOBILE_FINGER_ON_INTERFACE);
picker.setOriginalWidth(15);
picker.setOriginalHeight(17);
picker.setOriginalX(x);
picker.setOriginalY(y);
picker.revalidate();
picker.setTargetVerb("Select");
picker.setName("Pick");
picker.setClickMask(WidgetConfig.USE_WIDGET | WidgetConfig.USE_ITEM);
picker.setNoClickThrough(true);
picker.setOnTargetEnterListener((JavaScriptCallback) ev ->
{
pickerSelected = true;
picker.setOpacity(30);
client.setAllWidgetsAreOpTargetable(true);
});
picker.setOnTargetLeaveListener((JavaScriptCallback) ev -> onPickerDeselect());
}
private void onPickerDeselect()
{
client.setAllWidgetsAreOpTargetable(false);
picker.setOpacity(0);
pickerSelected = false;
}
private void onMenuOptionClicked(MenuOptionClicked ev)
{
if (!pickerSelected)
{
return;
}
onPickerDeselect();
client.setSpellSelected(false);
ev.consume();
Object target = getWidgetOrWidgetItemForMenuOption(ev.getMenuOpcode().getId(), ev.getParam0(), ev.getParam1());
if (target == null)
{
return;
}
if (target instanceof WidgetItem)
{
WidgetItem iw = (WidgetItem) target;
setSelectedWidget(iw.getWidget(), iw.getIndex(), true);
}
else
{
setSelectedWidget((Widget) target, -1, true);
}
}
private void onMenuEntryAdded(MenuEntryAdded event)
{
if (!pickerSelected)
{
return;
}
MenuEntry[] menuEntries = client.getMenuEntries();
for (int i = 0; i < menuEntries.length; i++)
{
MenuEntry entry = menuEntries[i];
if (entry.getOpcode() != MenuOpcode.ITEM_USE_ON_WIDGET.getId()
&& entry.getOpcode() != MenuOpcode.SPELL_CAST_ON_WIDGET.getId())
{
continue;
}
String name = WidgetInfo.TO_GROUP(entry.getParam1()) + "." + WidgetInfo.TO_CHILD(entry.getParam1());
if (entry.getParam0() != -1)
{
name += " [" + entry.getParam0() + "]";
}
Color color = colorForWidget(i, menuEntries.length);
entry.setTarget(ColorUtil.wrapWithColorTag(name, color));
}
client.setMenuEntries(menuEntries);
}
Color colorForWidget(int index, int length)
{
float h = SELECTED_WIDGET_HUE + .1f + (.8f / length) * index;
return Color.getHSBColor(h, 1, 1);
}
Object getWidgetOrWidgetItemForMenuOption(int type, int param0, int param1)
{
if (type == MenuOpcode.SPELL_CAST_ON_WIDGET.getId())
{
Widget w = client.getWidget(WidgetInfo.TO_GROUP(param1), WidgetInfo.TO_CHILD(param1));
if (param0 != -1)
{
w = w.getChild(param0);
}
return w;
}
else if (type == MenuOpcode.ITEM_USE_ON_WIDGET.getId())
{
Widget w = client.getWidget(WidgetInfo.TO_GROUP(param1), WidgetInfo.TO_CHILD(param1));
return w.getWidgetItem(param0);
}
return null;
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) 2018 Abex
* Copyright (c) 2017, Kronos <https://github.com/KronosDesign>
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.devtools;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.MenuEntry;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetItem;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
@Singleton
public class WidgetInspectorOverlay extends Overlay
{
private final Client client;
private final WidgetInspector inspector;
@Inject
public WidgetInspectorOverlay(
Client client,
WidgetInspector inspector
)
{
this.client = client;
this.inspector = inspector;
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_WIDGETS);
setPriority(OverlayPriority.HIGHEST);
}
@Override
public Dimension render(Graphics2D g)
{
Widget w = inspector.getSelectedWidget();
if (w != null)
{
Object wiw = w;
if (inspector.getSelectedItem() != -1)
{
wiw = w.getWidgetItem(inspector.getSelectedItem());
}
renderWiw(g, wiw, WidgetInspector.SELECTED_WIDGET_COLOR);
}
if (inspector.isPickerSelected())
{
boolean menuOpen = client.isMenuOpen();
MenuEntry[] entries = client.getMenuEntries();
for (int i = menuOpen ? 0 : entries.length - 1; i < entries.length; i++)
{
MenuEntry e = entries[i];
Object wiw = inspector.getWidgetOrWidgetItemForMenuOption(e.getOpcode(), e.getParam0(), e.getParam1());
if (wiw == null)
{
continue;
}
Color color = inspector.colorForWidget(i, entries.length);
renderWiw(g, wiw, color);
}
}
return null;
}
private void renderWiw(Graphics2D g, Object wiw, Color color)
{
g.setColor(color);
if (wiw instanceof WidgetItem)
{
WidgetItem wi = (WidgetItem) wiw;
Rectangle bounds = wi.getCanvasBounds();
g.draw(bounds);
String text = wi.getId() + "";
FontMetrics fm = g.getFontMetrics();
Rectangle2D textBounds = fm.getStringBounds(text, g);
int textX = (int) (bounds.getX() + (bounds.getWidth() / 2) - (textBounds.getWidth() / 2));
int textY = (int) (bounds.getY() + (bounds.getHeight() / 2) + (textBounds.getHeight() / 2));
g.setColor(Color.BLACK);
g.drawString(text, textX + 1, textY + 1);
g.setColor(Color.ORANGE);
g.drawString(text, textX, textY);
}
else
{
Widget w = (Widget) wiw;
g.draw(w.getBounds());
}
}
}

View File

@@ -47,8 +47,8 @@ import net.runelite.api.Skill;
import net.runelite.api.WorldType;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.StatChanged;
import net.runelite.api.events.VarbitChanged;
import net.runelite.client.RuneLiteProperties;
import net.runelite.client.config.ConfigManager;
@@ -175,7 +175,7 @@ public class DiscordPlugin extends Plugin
{
eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
eventBus.subscribe(ExperienceChanged.class, this, this::onExperienceChanged);
eventBus.subscribe(StatChanged.class, this, this::onStatChanged);
eventBus.subscribe(VarbitChanged.class, this, this::onVarbitChanged);
eventBus.subscribe(DiscordReady.class, this, this::onDiscordReady);
eventBus.subscribe(DiscordJoinRequest.class, this, this::onDiscordJoinRequest);
@@ -222,17 +222,18 @@ public class DiscordPlugin extends Plugin
}
}
private void onExperienceChanged(ExperienceChanged event)
private void onStatChanged(StatChanged statChanged)
{
final int exp = client.getSkillExperience(event.getSkill());
final Integer previous = skillExp.put(event.getSkill(), exp);
final Skill skill = statChanged.getSkill();
final int exp = statChanged.getXp();
final Integer previous = skillExp.put(skill, exp);
if (previous == null || previous >= exp)
{
return;
}
final DiscordGameEventType discordGameEventType = DiscordGameEventType.fromSkill(event.getSkill());
final DiscordGameEventType discordGameEventType = DiscordGameEventType.fromSkill(skill);
if (discordGameEventType != null && this.showSkillingActivity)
{

View File

@@ -312,7 +312,9 @@ public class ExaminePlugin extends Plugin
|| WidgetInfo.CLUE_SCROLL_REWARD_ITEM_CONTAINER.getGroupId() == widgetGroup
|| WidgetInfo.LOOTING_BAG_CONTAINER.getGroupId() == widgetGroup
|| WidgetID.SEED_VAULT_INVENTORY_GROUP_ID == widgetGroup
|| WidgetID.SEED_BOX_GROUP_ID == widgetGroup)
|| WidgetID.SEED_BOX_GROUP_ID == widgetGroup
|| WidgetID.PLAYER_TRADE_SCREEN_GROUP_ID == widgetGroup
|| WidgetID.PLAYER_TRADE_INVENTORY_GROUP_ID == widgetGroup)
{
Widget[] children = widget.getDynamicChildren();
if (actionParam < children.length)

View File

@@ -29,8 +29,6 @@ package net.runelite.client.plugins.experiencedrop;
import com.google.inject.Provides;
import java.awt.Color;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.Map;
import java.util.stream.IntStream;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -46,7 +44,6 @@ import net.runelite.api.SpriteID;
import net.runelite.api.Varbits;
import net.runelite.api.WorldType;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.ExperienceChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.ScriptCallbackEvent;
@@ -57,6 +54,7 @@ import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.game.NPCManager;
import net.runelite.client.game.XpDropEvent;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.OverlayManager;
@@ -65,30 +63,23 @@ import net.runelite.client.util.ColorUtil;
@PluginDescriptor(
name = "XP Drop",
description = "Enable customization of the way XP drops are displayed",
tags = {"experience", "levels", "tick"}
)
tags = {"experience", "levels", "tick"})
@Singleton
public class XpDropPlugin extends Plugin
{
private static final int XPDROP_PADDING = 2; // space between xp drop icons
private static final double HITPOINT_RATIO = 1.33; // Base rate of hp xp per point damage
private static final double DMM_MULTIPLIER_RATIO = 10;
@Inject
private Client client;
@Inject
private XpDropConfig config;
@Inject
private NPCManager npcManager;
@Inject
private OverlayManager overlayManager;
@Inject
private XpDropOverlay overlay;
@Inject
private EventBus eventBus;
@@ -106,16 +97,15 @@ public class XpDropPlugin extends Plugin
private boolean hasDropped = false;
private boolean correctPrayer;
private Skill lastSkill = null;
private final Map<Skill, Integer> previousSkillExpTable = new EnumMap<>(Skill.class);
private PrayerType currentTickPrayer;
private XpDropConfig.DamageMode damageMode;
private boolean hideSkillIcons;
private Color getMeleePrayerColor;
private Color getRangePrayerColor;
private Color getMagePrayerColor;
private int fakeXpDropDelay;
private XpDropConfig.DamageMode showdamagedrops;
@Getter(AccessLevel.PACKAGE)
private Color damageColor;
@@ -153,10 +143,17 @@ public class XpDropPlugin extends Plugin
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
eventBus.subscribe(WidgetHiddenChanged.class, this, this::onWidgetHiddenChanged);
eventBus.subscribe(GameTick.class, this, this::onGameTick);
eventBus.subscribe(ExperienceChanged.class, this, this::onExperienceChanged);
eventBus.subscribe(XpDropEvent.class, this, this::onXpDropEvent);
eventBus.subscribe(ScriptCallbackEvent.class, this, this::onScriptCallbackEvent);
}
private void onXpDropEvent(XpDropEvent event)
{
previousExpGained = event.getExp();
lastSkill = event.getSkill();
hasDropped = true;
}
private void onConfigChanged(ConfigChanged event)
{
if (!event.getGroup().equals("xpdrop"))
@@ -188,8 +185,8 @@ public class XpDropPlugin extends Plugin
private void onGameStateChanged(GameStateChanged event)
{
damage = 0;
tickShow = 0;
damage = 0;
}
private void onWidgetHiddenChanged(WidgetHiddenChanged event)
@@ -269,9 +266,12 @@ public class XpDropPlugin extends Plugin
switch (prayer)
{
case MELEE:
if (spriteIDs.anyMatch(id ->
id == SpriteID.SKILL_ATTACK || id == SpriteID.SKILL_STRENGTH || id == SpriteID.SKILL_DEFENCE
|| correctPrayer))
if (spriteIDs.anyMatch(
id ->
id == SpriteID.SKILL_ATTACK
|| id == SpriteID.SKILL_STRENGTH
|| id == SpriteID.SKILL_DEFENCE
|| correctPrayer))
{
color = this.getMeleePrayerColor.getRGB();
correctPrayer = true;
@@ -351,21 +351,6 @@ public class XpDropPlugin extends Plugin
client.runScript(XPDROP_DISABLED, lastSkill.ordinal(), previousExpGained);
}
private void onExperienceChanged(ExperienceChanged event)
{
final Skill skill = event.getSkill();
final int xp = client.getSkillExperience(skill);
lastSkill = skill;
Integer previous = previousSkillExpTable.put(skill, xp);
if (previous != null)
{
previousExpGained = xp - previous;
hasDropped = true;
}
}
private void onScriptCallbackEvent(ScriptCallbackEvent e)
{
if (this.showdamagedrops == XpDropConfig.DamageMode.NONE)
@@ -404,16 +389,19 @@ public class XpDropPlugin extends Plugin
final int exp = intStack[intStackSize - 1];
calculateDamageDealt(exp);
}
else if (eventName.equals("xpDropAddDamage") &&
damageMode == XpDropConfig.DamageMode.IN_XP_DROP &&
damage > 0)
else if (eventName.equals("xpDropAddDamage")
&& damageMode == XpDropConfig.DamageMode.IN_XP_DROP
&& damage > 0)
{
final String[] stringStack = client.getStringStack();
final int stringStackSize = client.getStringStackSize();
String builder = stringStack[stringStackSize - 1] +
ColorUtil.colorTag(this.damageColor) +
" (" + damage + ")";
String builder =
stringStack[stringStackSize - 1]
+ ColorUtil.colorTag(this.damageColor)
+ " ("
+ damage
+ ")";
stringStack[stringStackSize - 1] = builder;
}
}
@@ -431,7 +419,8 @@ public class XpDropPlugin extends Plugin
Actor a = client.getLocalPlayer().getInteracting();
if (!(a instanceof NPC) && !(a instanceof Player))
{
// If we are interacting with nothing we may have clicked away at the perfect time fall back to last tick
// If we are interacting with nothing we may have clicked away at the perfect time fall back
// to last tick
if (!(lastOpponent instanceof NPC) && !(lastOpponent instanceof Player))
{
damage = (int) Math.rint(damageDealt);

View File

@@ -24,6 +24,7 @@
*/
package net.runelite.client.plugins.fishing;
import java.awt.Color;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@@ -76,7 +77,40 @@ public interface FishingConfig extends Config
}
@ConfigItem(
position = 4,
keyName = "overlayColor",
name = "Overlay Color",
description = "Color of overlays",
position = 4
)
default Color getOverlayColor()
{
return Color.CYAN;
}
@ConfigItem(
keyName = "minnowsOverlayColor",
name = "Minnows Overlay Color",
description = "Color of overlays for Minnows",
position = 5
)
default Color getMinnowsOverlayColor()
{
return Color.RED;
}
@ConfigItem(
keyName = "aerialOverlayColor",
name = "Aerial Overlay Color",
description = "Color of overlays when 1-tick aerial fishing",
position = 6
)
default Color getAerialOverlayColor()
{
return Color.GREEN;
}
@ConfigItem(
position = 7,
keyName = "statTimeout",
name = "Reset stats (minutes)",
description = "The time until fishing session data is reset in minutes."
@@ -87,7 +121,7 @@ public interface FishingConfig extends Config
}
@ConfigItem(
position = 5,
position = 8,
keyName = "showFishingStats",
name = "Show Fishing session stats",
description = "Display the fishing session stats."
@@ -98,7 +132,7 @@ public interface FishingConfig extends Config
}
@ConfigItem(
position = 6,
position = 9,
keyName = "showMinnowOverlay",
name = "Show Minnow Movement overlay",
description = "Display the minnow progress pie overlay."
@@ -109,7 +143,7 @@ public interface FishingConfig extends Config
}
@ConfigItem(
position = 7,
position = 10,
keyName = "trawlerNotification",
name = "Trawler activity notification",
description = "Send a notification when fishing trawler activity drops below 15%."
@@ -120,7 +154,7 @@ public interface FishingConfig extends Config
}
@ConfigItem(
position = 8,
position = 11,
keyName = "trawlerTimer",
name = "Trawler timer in MM:SS",
description = "Trawler Timer will display a more accurate timer in MM:SS format."

View File

@@ -27,6 +27,7 @@ package net.runelite.client.plugins.fishing;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Provides;
import java.awt.Color;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@@ -157,6 +158,12 @@ public class FishingPlugin extends Plugin
private boolean showMinnowOverlay;
private boolean trawlerNotification;
private boolean trawlerTimer;
@Getter(AccessLevel.PACKAGE)
private Color overlayColor;
@Getter(AccessLevel.PACKAGE)
private Color minnowsOverlayColor;
@Getter(AccessLevel.PACKAGE)
private Color aerialOverlayColor;
@Override
protected void startUp() throws Exception
@@ -404,6 +411,7 @@ public class FishingPlugin extends Plugin
if (regionID != TRAWLER_SHIP_REGION_NORMAL && regionID != TRAWLER_SHIP_REGION_SINKING)
{
log.debug("Trawler session ended");
trawlerStartTime = null;
return;
}
@@ -477,5 +485,8 @@ public class FishingPlugin extends Plugin
this.showMinnowOverlay = config.showMinnowOverlay();
this.trawlerNotification = config.trawlerNotification();
this.trawlerTimer = config.trawlerTimer();
this.overlayColor = config.getOverlayColor();
this.minnowsOverlayColor = config.getMinnowsOverlayColor();
this.aerialOverlayColor = config.getAerialOverlayColor();
}
}

View File

@@ -76,7 +76,9 @@ class FishingSpotMinimapOverlay extends Overlay
continue;
}
Color color = npc.getSpotAnimation() == GraphicID.FLYING_FISH ? Color.RED : Color.CYAN;
Color color = npc.getSpotAnimation() == GraphicID.FLYING_FISH
? plugin.getMinnowsOverlayColor()
: plugin.getOverlayColor();
net.runelite.api.Point minimapLocation = npc.getMinimapLocation();
if (minimapLocation != null)

View File

@@ -107,15 +107,15 @@ class FishingSpotOverlay extends Overlay
Color color;
if (npc.getSpotAnimation() == GraphicID.FLYING_FISH)
{
color = Color.RED;
color = plugin.getMinnowsOverlayColor();
}
else if (spot == FishingSpot.COMMON_TENCH && npc.getWorldLocation().distanceTo2D(client.getLocalPlayer().getWorldLocation()) <= ONE_TICK_AERIAL_FISHING)
{
color = Color.GREEN;
color = plugin.getAerialOverlayColor();
}
else
{
color = Color.CYAN;
color = plugin.getOverlayColor();
}
if (spot == FishingSpot.MINNOW && plugin.isShowMinnowOverlay())
@@ -161,7 +161,7 @@ class FishingSpotOverlay extends Overlay
if (spot == FishingSpot.COMMON_TENCH
&& npc.getWorldLocation().distanceTo2D(client.getLocalPlayer().getWorldLocation()) <= ONE_TICK_AERIAL_FISHING)
{
fishImage = ImageUtil.outlineImage(itemManager.getImage(spot.getFishSpriteId()), Color.GREEN);
fishImage = ImageUtil.outlineImage(itemManager.getImage(spot.getFishSpriteId()), color);
}
if (fishImage != null)

View File

@@ -321,4 +321,16 @@ public interface GauntletConfig extends Config
{
return false;
}
@ConfigItem(
position = 21,
keyName = "displayResources",
name = "Show raw resources gathered",
description = "Displays how much of each resource you have gathered.",
titleSection = "resources"
)
default boolean displayGatheredResources()
{
return false;
}
}

View File

@@ -43,6 +43,7 @@ import net.runelite.api.Client;
import net.runelite.api.GameObject;
import net.runelite.api.GameState;
import net.runelite.api.HeadIcon;
import net.runelite.api.ItemID;
import net.runelite.api.NPC;
import net.runelite.api.NPCDefinition;
import net.runelite.api.NpcID;
@@ -50,6 +51,7 @@ import net.runelite.api.ObjectID;
import net.runelite.api.Player;
import net.runelite.api.Projectile;
import net.runelite.api.ProjectileID;
import net.runelite.api.Skill;
import net.runelite.api.SoundEffectID;
import net.runelite.api.Varbits;
import net.runelite.api.events.AnimationChanged;
@@ -58,6 +60,7 @@ import net.runelite.api.events.GameObjectDespawned;
import net.runelite.api.events.GameObjectSpawned;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.NpcSpawned;
import net.runelite.api.events.ProjectileSpawned;
@@ -65,6 +68,8 @@ import net.runelite.api.events.VarbitChanged;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.events.NpcLootReceived;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.SkillIconManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
@@ -73,7 +78,10 @@ import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.LIGHTNING;
import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.MAGIC;
import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.PRAYER;
import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.RANGE;
import net.runelite.client.game.XpDropEvent;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.infobox.Counter;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
@PluginDescriptor(
name = "Gauntlet",
@@ -104,7 +112,8 @@ public class GauntletPlugin extends Plugin
ObjectID.PHREN_ROOTS_36066, ObjectID.FISHING_SPOT_36068, ObjectID.FISHING_SPOT_35971, ObjectID.GRYM_ROOT, ObjectID.GRYM_ROOT_36070,
ObjectID.LINUM_TIRINUM, ObjectID.LINUM_TIRINUM_36072
);
private static final int GATHERING_HERB = 0;
private static final int GATHERING_CLOTH = 1;
@Inject
@Getter(AccessLevel.NONE)
private Client client;
@@ -138,6 +147,10 @@ public class GauntletPlugin extends Plugin
@Setter(AccessLevel.PACKAGE)
@Nullable
private Hunllef hunllef;
@Inject
private InfoBoxManager infoBoxManager;
@Inject
private ItemManager itemManager;
private boolean attackVisualOutline;
private boolean completeStartup = false;
private boolean displayTimerChat;
@@ -160,11 +173,24 @@ public class GauntletPlugin extends Plugin
private final Map<String, Integer> items = new HashMap<>();
private final Set<Missiles> projectiles = new HashSet<>();
private final Set<Resources> resources = new HashSet<>();
private GauntletConfig.CounterDisplay countAttacks;
private int resourceIconSize;
private Set<Tornado> tornadoes = new HashSet<>();
private int projectileIconSize;
private boolean displayResources;
private Counter oreCounter;
private Counter woodCounter;
private Counter clothCounter;
private Counter fishCounter;
private Counter herbCounter;
private int oresGathered;
private int woodGathered;
private int clothGathered;
private int fishGathered;
private int herbGathered;
private int currentFarmingAction = -1;
private boolean countersVisible = false;
@Provides
GauntletConfig getConfig(ConfigManager configManager)
@@ -177,6 +203,7 @@ public class GauntletPlugin extends Plugin
{
addSubscriptions();
updateConfig();
initializeCounters();
overlayManager.add(overlay);
overlayManager.add(infoboxoverlay);
overlayManager.add(GauntletCounter);
@@ -200,6 +227,47 @@ public class GauntletPlugin extends Plugin
}
}
private void addCounters()
{
if (!countersVisible)
{
infoBoxManager.addInfoBox(oreCounter);
infoBoxManager.addInfoBox(woodCounter);
infoBoxManager.addInfoBox(clothCounter);
infoBoxManager.addInfoBox(fishCounter);
infoBoxManager.addInfoBox(herbCounter);
countersVisible = true;
}
}
private void initializeCounters()
{
resetGatheringCounters();
oreCounter = new Counter(itemManager.getImage(ItemID.CORRUPTED_ORE), this, 0);
woodCounter = new Counter(itemManager.getImage(ItemID.PHREN_BARK_23878), this, 0);
clothCounter = new Counter(itemManager.getImage(ItemID.LINUM_TIRINUM_23876), this, 0);
fishCounter = new Counter(itemManager.getImage(ItemID.RAW_PADDLEFISH), this, 0);
herbCounter = new Counter(itemManager.getImage(ItemID.GRYM_LEAF_23875), this, 0);
}
private void resetGatheringCounters()
{
oresGathered = 0;
fishGathered = 0;
woodGathered = 0;
clothGathered = 0;
herbGathered = 0;
}
private void updateCounters()
{
oreCounter.setCount(oresGathered);
woodCounter.setCount(woodGathered);
clothCounter.setCount(clothGathered);
fishCounter.setCount(fishGathered);
herbCounter.setCount(herbGathered);
}
@Override
protected void shutDown()
{
@@ -213,12 +281,24 @@ public class GauntletPlugin extends Plugin
overlayManager.remove(overlay);
overlayManager.remove(infoboxoverlay);
overlayManager.remove(GauntletCounter);
removeCounters();
resetGatheringCounters();
resources.clear();
projectiles.clear();
tornadoes.clear();
setHunllef(null);
}
private void removeCounters()
{
infoBoxManager.removeInfoBox(oreCounter);
infoBoxManager.removeInfoBox(woodCounter);
infoBoxManager.removeInfoBox(clothCounter);
infoBoxManager.removeInfoBox(fishCounter);
infoBoxManager.removeInfoBox(herbCounter);
countersVisible = false;
}
private void addSubscriptions()
{
eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged);
@@ -231,6 +311,56 @@ public class GauntletPlugin extends Plugin
eventBus.subscribe(NpcSpawned.class, this, this::onNpcSpawned);
eventBus.subscribe(ProjectileSpawned.class, this, this::onProjectileSpawned);
eventBus.subscribe(VarbitChanged.class, this, this::onVarbitChanged);
eventBus.subscribe(XpDropEvent.class, this, this::onXpDropEvent);
eventBus.subscribe(NpcLootReceived.class, this, this::onNpcLootReceived);
eventBus.subscribe(MenuOptionClicked.class, this, this::onMenuOptionClicked);
}
private void onMenuOptionClicked(MenuOptionClicked menuOptionClicked)
{
if (menuOptionClicked.getTarget().toUpperCase().contains("LINUM"))
{
currentFarmingAction = GATHERING_CLOTH;
}
if (menuOptionClicked.getTarget().toUpperCase().contains("GRYM"))
{
currentFarmingAction = GATHERING_HERB;
}
}
private void onNpcLootReceived(NpcLootReceived npcLootReceived)
{
fishGathered += (int) npcLootReceived.getItems().stream().filter(item -> item.getId() == ItemID.RAW_PADDLEFISH).count();
herbGathered += (int) npcLootReceived.getItems().stream().filter(item -> item.getId() == ItemID.GRYM_LEAF || item.getId() == ItemID.GRYM_LEAF_23875).count();
updateCounters();
}
private void onXpDropEvent(XpDropEvent experienceChanged)
{
if (experienceChanged.getSkill().compareTo(Skill.MINING) == 0)
{
oresGathered++;
}
if (experienceChanged.getSkill().compareTo(Skill.WOODCUTTING) == 0)
{
woodGathered++;
}
if (experienceChanged.getSkill().compareTo(Skill.FARMING) == 0)
{
if (currentFarmingAction == GATHERING_HERB)
{
herbGathered++;
}
else if (currentFarmingAction == GATHERING_CLOTH)
{
clothGathered++;
}
}
if (experienceChanged.getSkill().compareTo(Skill.FISHING) == 0)
{
fishGathered++;
}
updateCounters();
}
private void onAnimationChanged(AnimationChanged event)
@@ -325,6 +455,18 @@ public class GauntletPlugin extends Plugin
timerVisible = false;
}
}
if (event.getKey().equals("displayResources"))
{
if (this.displayResources && this.startedGauntlet())
{
addCounters();
}
else
{
removeCounters();
}
}
}
private void onGameObjectDespawned(GameObjectDespawned event)
@@ -379,6 +521,7 @@ public class GauntletPlugin extends Plugin
if (HUNLLEF_NPC_IDS.contains(npc.getId()))
{
setHunllef(null);
resetGatheringCounters();
}
else if (TORNADO_NPC_IDS.contains(npc.getId()))
{
@@ -436,6 +579,14 @@ public class GauntletPlugin extends Plugin
{
timer.checkStates(true);
}
if (startedGauntlet() && displayResources)
{
addCounters();
}
else
{
removeCounters();
}
}
boolean fightingBoss()
@@ -468,5 +619,6 @@ public class GauntletPlugin extends Plugin
this.displayTimerChat = config.displayTimerChat();
this.attackVisualOutline = config.attackVisualOutline();
this.highlightPrayerInfobox = config.highlightPrayerInfobox();
this.displayResources = config.displayGatheredResources();
}
}

View File

@@ -27,7 +27,9 @@ package net.runelite.client.plugins.gpu;
import com.jogamp.opengl.GL4;
import java.io.InputStream;
import java.util.Scanner;
import lombok.extern.slf4j.Slf4j;
@Slf4j
class GLUtil
{
private static final int ERR_LEN = 1024;
@@ -197,6 +199,7 @@ class GLUtil
else
{
String err = glGetShaderInfoLog(gl, shader);
log.info(String.valueOf(program));
throw new ShaderException(err);
}
}

View File

@@ -59,11 +59,11 @@ import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.FocusChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.GrandExchangeOfferChanged;
import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.util.Text;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
@@ -84,11 +84,11 @@ import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.QuantityFormatter;
import net.runelite.api.util.Text;
import net.runelite.http.api.RuneLiteAPI;
import net.runelite.http.api.ge.GrandExchangeClient;
import net.runelite.http.api.ge.GrandExchangeTrade;
import net.runelite.http.api.osbuddy.OSBGrandExchangeClient;
import net.runelite.http.api.osbuddy.OSBGrandExchangeResult;
@PluginDescriptor(
name = "Grand Exchange",
@@ -165,6 +165,8 @@ public class GrandExchangePlugin extends Plugin
private Widget grandExchangeOfferQuantityHeading;
private Map<Integer, Integer> itemGELimits;
private int osbItem;
private OSBGrandExchangeResult osbGrandExchangeResult;
private GrandExchangeClient grandExchangeClient;
private boolean quickLookup;
@@ -241,6 +243,9 @@ public class GrandExchangePlugin extends Plugin
{
grandExchangeClient = new GrandExchangeClient(accountSession.getUuid());
}
osbItem = -1;
osbGrandExchangeResult = null;
}
@Override
@@ -261,7 +266,6 @@ public class GrandExchangePlugin extends Plugin
private void addSubscriptions()
{
eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
eventBus.subscribe(GameTick.class, this, this::onGameTick);
eventBus.subscribe(ChatMessage.class, this, this::onChatMessage);
eventBus.subscribe(SessionOpen.class, this, this::onSessionOpen);
eventBus.subscribe(SessionClose.class, this, this::onSessionClose);
@@ -448,7 +452,7 @@ public class GrandExchangePlugin extends Plugin
case WidgetID.SHOP_INVENTORY_GROUP_ID:
menuEntry.setOption(SEARCH_GRAND_EXCHANGE);
menuEntry.setOpcode(MenuOpcode.RUNELITE.getId());
menuEntry.setModified(true);
menuEntry.setModified();
}
}
@@ -480,6 +484,11 @@ public class GrandExchangePlugin extends Plugin
private void onScriptCallbackEvent(ScriptCallbackEvent event)
{
if (event.getEventName().equals("geBuilt"))
{
rebuildGeText();
}
if (!event.getEventName().equals("setGETitle") || !config.showTotal())
{
return;
@@ -519,7 +528,7 @@ public class GrandExchangePlugin extends Plugin
stringStack[stringStackSize - 1] += titleBuilder.toString();
}
private void onGameTick(GameTick event)
private void rebuildGeText()
{
if (grandExchangeText == null || grandExchangeItem == null || grandExchangeItem.isHidden())
{
@@ -527,7 +536,6 @@ public class GrandExchangePlugin extends Plugin
}
final Widget geText = grandExchangeText;
final String geTextString = geText.getText();
final Widget geQuantityHeading = grandExchangeOfferQuantityHeading;
final int offerType = client.getVar(Varbits.GE_OFFER_CREATION_TYPE);
final int itemId = grandExchangeItem.getItemId();
@@ -566,50 +574,58 @@ public class GrandExchangePlugin extends Plugin
}
if (this.enableGELimits && itemGELimits != null && !geTextString.contains(BUY_LIMIT_GE_TEXT))
String[] lines = geText.getText().split("<br>");
String text = lines[0]; // remove any limit or OSB ge values
if (this.enableGELimits && itemGELimits != null)
{
final Integer itemLimit = itemGELimits.get(itemId);
// If we have item buy limit, append it
if (itemLimit != null)
{
final String text = geText.getText() + BUY_LIMIT_GE_TEXT + QuantityFormatter.formatNumber(itemLimit);
geText.setText(text);
text += BUY_LIMIT_GE_TEXT + QuantityFormatter.formatNumber(itemLimit);
}
}
if (!this.enableOsbPrices || geTextString.contains(OSB_GE_TEXT))
geText.setText(text);
if (!this.enableOsbPrices)
{
// OSB prices are disabled or price was already looked up, so no need to set it again
return;
}
// If we already have the result, use it
if (osbGrandExchangeResult != null && osbGrandExchangeResult.getItem_id() == itemId && osbGrandExchangeResult.getOverall_average() > 0)
{
geText.setText(text + OSB_GE_TEXT + QuantityFormatter.formatNumber(osbGrandExchangeResult.getOverall_average()));
}
if (osbItem == itemId)
{
// avoid starting duplicate lookups
return;
}
osbItem = itemId;
log.debug("Looking up OSB item price {}", itemId);
final String start = text;
executorService.submit(() ->
{
if (geText.getText().contains(OSB_GE_TEXT))
{
// If there are multiple tasks queued and one of them have already added the price
return;
}
CLIENT.lookupItem(itemId)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.from(clientThread))
.subscribe(
(osbresult) ->
{
final String text = geText.getText() + OSB_GE_TEXT + QuantityFormatter.formatNumber(osbresult.getOverall_average());
if (geText.getText().contains(OSB_GE_TEXT))
{
// If there are multiple tasks queued and one of them have already added the price
return;
}
geText.setText(text);
osbGrandExchangeResult = osbresult;
// Update the text on the widget too
geText.setText(start + OSB_GE_TEXT + QuantityFormatter.formatNumber(osbresult.getOverall_average()));
},
(e) -> log.debug("Error getting price of item {}", itemId, e)
);
});
}
}
}

View File

@@ -983,7 +983,7 @@ public class GroundItemsPlugin extends Plugin
{
final String optionText = telegrabEntry ? "Cast" : "Take";
lastEntry.setOption(ColorUtil.prependColorTag(optionText, color));
lastEntry.setModified(true);
lastEntry.setModified();
}
if (mode == BOTH || mode == NAME)
@@ -1003,14 +1003,14 @@ public class GroundItemsPlugin extends Plugin
}
lastEntry.setTarget(target);
lastEntry.setModified(true);
lastEntry.setModified();
}
}
if (this.showMenuItemQuantities && itemComposition.isStackable() && quantity > 1)
{
lastEntry.setTarget(lastEntry.getTarget() + " (" + quantity + ")");
lastEntry.setModified(true);
lastEntry.setModified();
}
if (this.removeIgnored && lastEntry.getOption().equals("Take") && hiddenItemList.contains(Text.removeTags(lastEntry.getTarget())))

View File

@@ -99,7 +99,7 @@ import net.runelite.http.api.hiscore.Skill;
@Singleton
public class HiscorePanel extends PluginPanel
{
/* The maximum allowed username length in runescape accounts */
/* The maximum allowed username length in RuneScape accounts */
private static final int MAX_USERNAME_LENGTH = 12;
/**
@@ -344,7 +344,7 @@ public class HiscorePanel extends PluginPanel
return;
}
/* Runescape usernames can't be longer than 12 characters long */
/* RuneScape usernames can't be longer than 12 characters long */
if (lookup.length() > MAX_USERNAME_LENGTH)
{
searchBar.setIcon(IconTextField.Icon.ERROR);

View File

@@ -33,6 +33,7 @@ import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.SpriteID;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.plugins.inferno.displaymodes.InfernoPrayerDisplayMode;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
@@ -41,16 +42,19 @@ import net.runelite.client.ui.overlay.components.ImageComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
@Singleton
public class InfernoJadOverlay extends Overlay
public class InfernoInfoBoxOverlay extends Overlay
{
private static final Color NOT_ACTIVATED_BACKGROUND_COLOR = new Color(150, 0, 0, 150);
private final Client client;
private final InfernoPlugin plugin;
private final SpriteManager spriteManager;
private final PanelComponent imagePanelComponent = new PanelComponent();
private BufferedImage prayMeleeSprite;
private BufferedImage prayRangedSprite;
private BufferedImage prayMagicSprite;
@Inject
private InfernoJadOverlay(final Client client, final InfernoPlugin plugin, final SpriteManager spriteManager)
private InfernoInfoBoxOverlay(final Client client, final InfernoPlugin plugin, final SpriteManager spriteManager)
{
setPosition(OverlayPosition.BOTTOM_RIGHT);
setPriority(OverlayPriority.HIGH);
@@ -62,48 +66,56 @@ public class InfernoJadOverlay extends Overlay
@Override
public Dimension render(Graphics2D graphics)
{
if (!plugin.isShowPrayerHelp() || (plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.BOTTOM_RIGHT
&& plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.BOTH))
if (plugin.getPrayerDisplayMode() != InfernoPrayerDisplayMode.BOTTOM_RIGHT
&& plugin.getPrayerDisplayMode() != InfernoPrayerDisplayMode.BOTH)
{
return null;
}
InfernoJad.Attack attack = null;
int leastTicks = 999;
for (InfernoJad jad : plugin.getJads())
{
if (jad.getNextAttack() == null || jad.getTicksTillNextAttack() < 1)
{
continue;
}
if (jad.getTicksTillNextAttack() < leastTicks)
{
leastTicks = jad.getTicksTillNextAttack();
attack = jad.getNextAttack();
}
}
if (attack == null)
{
return null;
}
final BufferedImage prayerImage = getPrayerImage(attack);
imagePanelComponent.getChildren().clear();
imagePanelComponent.getChildren().add(new ImageComponent(prayerImage));
imagePanelComponent.setBackgroundColor(client.isPrayerActive(attack.getPrayer())
? ComponentConstants.STANDARD_BACKGROUND_COLOR
: NOT_ACTIVATED_BACKGROUND_COLOR);
if (plugin.getClosestAttack() != null)
{
final BufferedImage prayerImage = getPrayerImage(plugin.getClosestAttack());
imagePanelComponent.getChildren().add(new ImageComponent(prayerImage));
imagePanelComponent.setBackgroundColor(client.isPrayerActive(plugin.getClosestAttack().getPrayer())
? ComponentConstants.STANDARD_BACKGROUND_COLOR
: NOT_ACTIVATED_BACKGROUND_COLOR);
}
else
{
imagePanelComponent.setBackgroundColor(ComponentConstants.STANDARD_BACKGROUND_COLOR);
}
return imagePanelComponent.render(graphics);
}
private BufferedImage getPrayerImage(InfernoJad.Attack attack)
private BufferedImage getPrayerImage(InfernoNPC.Attack attack)
{
final int prayerSpriteID = attack == InfernoJad.Attack.MAGIC ? SpriteID.PRAYER_PROTECT_FROM_MAGIC : SpriteID.PRAYER_PROTECT_FROM_MISSILES;
return spriteManager.getSprite(prayerSpriteID, 0);
if (prayMeleeSprite == null)
{
prayMeleeSprite = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MELEE, 0);
}
if (prayRangedSprite == null)
{
prayRangedSprite = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MISSILES, 0);
}
if (prayMagicSprite == null)
{
prayMagicSprite = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MAGIC, 0);
}
switch (attack)
{
case MELEE:
return prayMeleeSprite;
case RANGED:
return prayRangedSprite;
case MAGIC:
return prayMagicSprite;
}
return prayMagicSprite;
}
}

View File

@@ -1,86 +0,0 @@
/*
* Copyright (c) 2017, Devin French <https://github.com/devinfrench>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.inferno;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.AnimationID;
import net.runelite.api.NPC;
import net.runelite.api.Prayer;
@Getter(AccessLevel.PACKAGE)
public class InfernoJad
{
private static final int TICKS_AFTER_ANIMATION = 4;
private NPC npc;
private Attack nextAttack;
private int ticksTillNextAttack;
InfernoJad(NPC npc)
{
this.npc = npc;
nextAttack = null;
ticksTillNextAttack = -1;
}
void updateNextAttack(Attack nextAttack)
{
this.nextAttack = nextAttack;
this.ticksTillNextAttack = TICKS_AFTER_ANIMATION;
}
void gameTick()
{
if (ticksTillNextAttack < 0)
{
return;
}
this.ticksTillNextAttack--;
if (ticksTillNextAttack < 0)
{
nextAttack = null;
}
}
@Getter(AccessLevel.PACKAGE)
enum Attack
{
MAGIC(AnimationID.JALTOK_JAD_MAGE_ATTACK, Prayer.PROTECT_FROM_MAGIC),
RANGE(AnimationID.JALTOK_JAD_RANGE_ATTACK, Prayer.PROTECT_FROM_MISSILES);
private final int animation;
private final Prayer prayer;
Attack(final int animation, final Prayer prayer)
{
this.animation = animation;
this.prayer = prayer;
}
}
}

View File

@@ -1,15 +1,15 @@
/*
* Copyright (c) 2019, Jacky <liangj97@gmail.com>
* Copyright (c) 2017, Devin French <https://github.com/devinfrench>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -25,162 +25,376 @@
package net.runelite.client.plugins.inferno;
import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.AnimationID;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.NpcID;
import net.runelite.api.Prayer;
import net.runelite.api.coords.WorldArea;
import net.runelite.api.coords.WorldPoint;
import org.apache.commons.lang3.ArrayUtils;
public class InfernoNPC
{
public enum Attackstyle
{
MAGE("Mage", Color.CYAN),
RANGE("Range", Color.GREEN),
MELEE("Melee", Color.WHITE),
RANDOM("Random", Color.ORANGE);
@Getter(AccessLevel.PACKAGE)
private String name;
@Getter(AccessLevel.PACKAGE)
private Color color;
Attackstyle(String s, Color c)
{
this.name = s;
this.color = c;
}
}
@Getter(AccessLevel.PACKAGE)
private NPC npc;
@Getter(AccessLevel.PACKAGE)
private String name;
private Type type;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private Attackstyle attackstyle;
private Attack nextAttack;
@Getter(AccessLevel.PACKAGE)
private int attackTicks;
@Getter(AccessLevel.PACKAGE)
private int priority;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private int ticksTillAttack = -1;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private boolean attacking = false;
@Getter(AccessLevel.PACKAGE)
private int attackAnimation;
@Getter(AccessLevel.PACKAGE)
private boolean isMidAttack = false;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private int distanceToPlayer = 0;
@Getter(AccessLevel.PACKAGE)
int textLocHeight;
private int ticksTillNextAttack;
private int lastAnimation;
private boolean lastCanAttack;
//0 = not in LOS, 1 = in LOS after move, 2 = in LOS
private final Map<WorldPoint, Integer> safeSpotCache;
InfernoNPC(NPC npc)
{
this.npc = npc;
textLocHeight = npc.getLogicalHeight() + 40;
switch (npc.getId())
this.type = Type.typeFromId(npc.getId());
this.nextAttack = type.getDefaultAttack();
this.ticksTillNextAttack = 0;
this.lastAnimation = -1;
this.lastCanAttack = false;
this.safeSpotCache = new HashMap();
}
void updateNextAttack(Attack nextAttack, int ticksTillNextAttack)
{
this.nextAttack = nextAttack;
this.ticksTillNextAttack = ticksTillNextAttack;
}
private void updateNextAttack(Attack nextAttack)
{
this.nextAttack = nextAttack;
}
boolean canAttack(Client client, WorldPoint target)
{
if (safeSpotCache.containsKey(target))
{
case NpcID.JALAKREKKET:
attackTicks = 4;
name = "lil mel";
attackAnimation = 7582;
attackstyle = Attackstyle.MELEE;
priority = 7;
break;
return safeSpotCache.get(target) == 2;
}
case NpcID.JALAKREKXIL:
attackTicks = 4;
name = "lil range";
attackAnimation = 7583;
attackstyle = Attackstyle.RANGE;
priority = 6;
break;
boolean hasLos = new WorldArea(target, 1, 1).hasLineOfSightTo(client, this.getNpc().getWorldArea());
boolean hasRange = this.getType().getDefaultAttack() == Attack.MELEE ? this.getNpc().getWorldArea().isInMeleeDistance(target)
: this.getNpc().getWorldArea().distanceTo(target) <= this.getType().getRange();
case NpcID.JALAKREKMEJ:
attackTicks = 4;
name = "lil mage";
attackAnimation = 7581;
attackstyle = Attackstyle.MAGE;
priority = 5;
break;
if (hasLos && hasRange)
{
safeSpotCache.put(target, 2);
}
case NpcID.JALMEJRAH:
attackTicks = 3;
name = "bat";
attackAnimation = 7578;
attackstyle = Attackstyle.RANGE;
priority = 4;
break;
return hasLos && hasRange;
}
case NpcID.JALAK:
attackTicks = 6;
name = "blob";
attackAnimation = 7583; // also 7581
attackstyle = Attackstyle.RANDOM;
priority = 3;
break;
boolean canMoveToAttack(Client client, WorldPoint target, List<WorldPoint> obstacles)
{
if (safeSpotCache.containsKey(target))
{
return safeSpotCache.get(target) == 1 || safeSpotCache.get(target) == 2;
}
case NpcID.JALIMKOT:
attackTicks = 4;
name = "meleer";
attackAnimation = 7597;
attackstyle = Attackstyle.MELEE;
priority = 2;
break;
final List<WorldPoint> realObstacles = new ArrayList<>();
for (WorldPoint obstacle : obstacles)
{
if (this.getNpc().getWorldArea().toWorldPointList().contains(obstacle))
{
continue;
}
case NpcID.JALXIL:
attackTicks = 4;
name = "ranger";
attackAnimation = 7605;
attackstyle = Attackstyle.RANGE;
priority = 1;
break;
realObstacles.add(obstacle);
}
case NpcID.JALZEK:
attackTicks = 4;
name = "mager";
attackAnimation = 7610;
attackstyle = Attackstyle.MAGE;
priority = 0;
break;
final WorldArea targetArea = new WorldArea(target, 1, 1);
WorldArea currentWorldArea = this.getNpc().getWorldArea();
default:
attackTicks = 0;
int steps = 0;
while (true)
{
// Prevent infinite loop in case of pathfinding failure
steps++;
if (steps > 30)
{
return false;
}
final WorldArea predictedWorldArea = currentWorldArea.calculateNextTravellingPoint(client, targetArea, true, x ->
{
for (WorldPoint obstacle : realObstacles)
{
if (new WorldArea(x, 1, 1).intersectsWith(new WorldArea(obstacle, 1, 1)))
{
return false;
}
}
return true;
});
// Will only happen when NPC is underneath player or moving out of scene (but this will never show on overlay)
if (predictedWorldArea == null)
{
safeSpotCache.put(target, 1);
return true;
}
if (predictedWorldArea == currentWorldArea)
{
safeSpotCache.put(target, 0);
return false;
}
boolean hasLos = new WorldArea(target, 1, 1).hasLineOfSightTo(client, predictedWorldArea);
boolean hasRange = this.getType().getDefaultAttack() == Attack.MELEE ? predictedWorldArea.isInMeleeDistance(target)
: predictedWorldArea.distanceTo(target) <= this.getType().getRange();
if (hasLos && hasRange)
{
safeSpotCache.put(target, 1);
return true;
}
currentWorldArea = predictedWorldArea;
}
}
public String info()
private boolean couldAttackPrevTick(Client client, WorldPoint lastPlayerLocation)
{
String info = "";
if (attacking)
{
info += ticksTillAttack;
}
//info += " D: " + distanceToPlayer;
return info;
return new WorldArea(lastPlayerLocation, 1, 1).hasLineOfSightTo(client, this.getNpc().getWorldArea());
}
void attacked()
void gameTick(Client client, WorldPoint lastPlayerLocation, boolean finalPhase)
{
ticksTillAttack = attackTicks;
attacking = true;
safeSpotCache.clear();
if (ticksTillNextAttack > 0)
{
this.ticksTillNextAttack--;
}
//Jad animation detection
if (this.getType() == Type.JAD && this.getNpc().getAnimation() != -1 && this.getNpc().getAnimation() != this.lastAnimation)
{
final InfernoNPC.Attack currentAttack = InfernoNPC.Attack.attackFromId(this.getNpc().getAnimation());
if (currentAttack != null && currentAttack != Attack.UNKNOWN)
{
this.updateNextAttack(currentAttack, this.getType().getTicksAfterAnimation());
}
}
if (ticksTillNextAttack <= 0)
{
switch (this.getType())
{
case ZUK:
if (this.getNpc().getAnimation() == AnimationID.TZKAL_ZUK)
{
if (finalPhase)
{
this.updateNextAttack(this.getType().getDefaultAttack(), 7);
}
else
{
this.updateNextAttack(this.getType().getDefaultAttack(), 10);
}
}
break;
case JAD:
if (this.getNextAttack() != Attack.UNKNOWN)
{
// Jad's cycle continuous after his animation + attack but there's no animation to alert it
this.updateNextAttack(this.getType().getDefaultAttack(), 8);
}
break;
case BLOB:
//RS pathfinding + LOS = hell, so if it can attack you the tick you were on previously, start attack cycle
if (!this.lastCanAttack && this.couldAttackPrevTick(client, lastPlayerLocation))
{
this.updateNextAttack(Attack.UNKNOWN, 3);
}
//If there's no animation when coming out of the safespot, the blob is detecting prayer
else if (!this.lastCanAttack && this.canAttack(client, client.getLocalPlayer().getWorldLocation()))
{
this.updateNextAttack(Attack.UNKNOWN, 4);
}
//This will activate another attack cycle
else if (this.getNpc().getAnimation() != -1)
{
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
}
break;
case BAT:
// Range + LOS check for bat because it suffers from the defense animation bug, also dont activate on "stand" animation
if (this.canAttack(client, client.getLocalPlayer().getWorldLocation())
&& this.getNpc().getAnimation() != AnimationID.JAL_MEJRAH_STAND && this.getNpc().getAnimation() != -1)
{
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
}
break;
case MELEE:
case RANGER:
case MAGE:
// For the meleer, ranger and mage the attack animation is always prioritized so only check for those
// Normal attack animation, doesnt suffer from defense animation bug. Activate usual attack cycle
if (this.getNpc().getAnimation() == AnimationID.JAL_IMKOT
|| this.getNpc().getAnimation() == AnimationID.JAL_XIL_RANGE_ATTACK || this.getNpc().getAnimation() == AnimationID.JAL_XIL_MELEE_ATTACK
|| this.getNpc().getAnimation() == AnimationID.JAL_ZEK_MAGE_ATTACK || this.getNpc().getAnimation() == AnimationID.JAL_ZEK_MELEE_ATTACK)
{
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
}
// Burrow into ground animation for meleer
else if (this.getNpc().getAnimation() == 7600)
{
this.updateNextAttack(this.getType().getDefaultAttack(), 12);
}
// Respawn enemy animation for mage
else if (this.getNpc().getAnimation() == 7611)
{
this.updateNextAttack(this.getType().getDefaultAttack(), 8);
}
break;
default:
if (this.getNpc().getAnimation() != -1)
{
// This will activate another attack cycle
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
}
break;
}
}
//Blob prayer detection
if (this.getType() == Type.BLOB && this.getTicksTillNextAttack() == 3
&& client.getLocalPlayer().getWorldLocation().distanceTo(this.getNpc().getWorldArea()) <= Type.BLOB.getRange())
{
InfernoNPC.Attack nextBlobAttack = InfernoNPC.Attack.UNKNOWN;
if (client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES))
{
nextBlobAttack = InfernoNPC.Attack.MAGIC;
}
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC))
{
nextBlobAttack = InfernoNPC.Attack.RANGED;
}
this.updateNextAttack(nextBlobAttack);
}
// This is for jad (jad's animation lasts till after the attack is launched, which fucks up the attack cycle)
lastAnimation = this.getNpc().getAnimation();
// This is for blob (to check if player just came out of safespot)
lastCanAttack = this.canAttack(client, client.getLocalPlayer().getWorldLocation());
}
@Getter(AccessLevel.PACKAGE)
enum Attack
{
MELEE(Prayer.PROTECT_FROM_MELEE,
Color.ORANGE,
Color.RED,
new int[]{
AnimationID.JAL_NIB,
AnimationID.JAL_AK_MELEE_ATTACK,
AnimationID.JAL_IMKOT,
AnimationID.JAL_XIL_MELEE_ATTACK,
AnimationID.JAL_ZEK_MELEE_ATTACK, //TODO: Yt-HurKot attack animation
}),
RANGED(Prayer.PROTECT_FROM_MISSILES,
Color.GREEN,
new Color(0, 128, 0),
new int[]{
AnimationID.JAL_MEJRAH,
AnimationID.JAL_AK_RANGE_ATTACK,
AnimationID.JAL_XIL_RANGE_ATTACK,
AnimationID.JALTOK_JAD_RANGE_ATTACK,
}),
MAGIC(Prayer.PROTECT_FROM_MAGIC,
Color.CYAN,
Color.BLUE,
new int[]{
AnimationID.JAL_AK_MAGIC_ATTACK,
AnimationID.JAL_ZEK_MAGE_ATTACK,
AnimationID.JALTOK_JAD_MAGE_ATTACK
}),
UNKNOWN(null, Color.WHITE, Color.GRAY, new int[]{});
private final Prayer prayer;
private final Color normalColor;
private final Color criticalColor;
private final int[] animationIds;
Attack(Prayer prayer, Color normalColor, Color criticalColor, int[] animationIds)
{
this.prayer = prayer;
this.normalColor = normalColor;
this.criticalColor = criticalColor;
this.animationIds = animationIds;
}
static Attack attackFromId(int animationId)
{
for (Attack attack : Attack.values())
{
if (ArrayUtils.contains(attack.getAnimationIds(), animationId))
{
return attack;
}
}
return null;
}
}
@Getter(AccessLevel.PACKAGE)
enum Type
{
NIBBLER(new int[]{NpcID.JALNIB}, Attack.MELEE, 4, 99, 100),
BAT(new int[]{NpcID.JALMEJRAH}, Attack.RANGED, 3, 4, 7),
BLOB(new int[]{NpcID.JALAK}, Attack.UNKNOWN, 6, 15, 4),
MELEE(new int[]{NpcID.JALIMKOT}, Attack.MELEE, 4, 1, 3),
RANGER(new int[]{NpcID.JALXIL, NpcID.JALXIL_7702}, Attack.RANGED, 4, 98, 2),
MAGE(new int[]{NpcID.JALZEK, NpcID.JALZEK_7703}, Attack.MAGIC, 4, 98, 1),
JAD(new int[]{NpcID.JALTOKJAD, NpcID.JALTOKJAD_7704}, Attack.UNKNOWN, 3, 99, 0),
HEALER_JAD(new int[]{NpcID.YTHURKOT, NpcID.YTHURKOT_7701, NpcID.YTHURKOT_7705}, Attack.MELEE, 4, 1, 6),
ZUK(new int[]{NpcID.TZKALZUK}, Attack.UNKNOWN, 10, 99, 99),
HEALER_ZUK(new int[]{NpcID.JALMEJJAK}, Attack.UNKNOWN, -1, 99, 100);
private final int[] npcIds;
private final Attack defaultAttack;
private final int ticksAfterAnimation;
private final int range;
private final int priority;
Type(int[] npcIds, Attack defaultAttack, int ticksAfterAnimation, int range, int priority)
{
this.npcIds = npcIds;
this.defaultAttack = defaultAttack;
this.ticksAfterAnimation = ticksAfterAnimation;
this.range = range;
this.priority = priority;
}
static Type typeFromId(int npcId)
{
for (Type type : Type.values())
{
if (ArrayUtils.contains(type.getNpcIds(), npcId))
{
return type;
}
}
return null;
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (c) 2019, Jacky <liangj97@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.inferno;
import java.awt.Dimension;
import java.awt.Graphics2D;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.components.table.TableAlignment;
import net.runelite.client.ui.overlay.components.table.TableComponent;
@Singleton
public class InfernoNibblerOverlay extends Overlay
{
private final Client client;
private final InfernoPlugin plugin;
private final PanelComponent panelComponent = new PanelComponent();
@Inject
public InfernoNibblerOverlay(final Client client, final InfernoPlugin plugin)
{
this.client = client;
this.plugin = plugin;
setPosition(OverlayPosition.TOP_LEFT);
}
@Override
public Dimension render(Graphics2D graphics)
{
if (!plugin.isDisplayNibblerOverlay() || plugin.getNibblers().size() == 0 || client.getMapRegions()[0] != 9043)
{
return null;
}
panelComponent.getChildren().clear();
TableComponent tableComponent = new TableComponent();
tableComponent.setColumnAlignments(TableAlignment.LEFT, TableAlignment.RIGHT);
tableComponent.addRow("Nibblers Left: ", Integer.toString(plugin.getNibblers().size()));
panelComponent.getChildren().add(tableComponent);
return panelComponent.render(graphics);
}
}

View File

@@ -1,56 +1,46 @@
/*
* Copyright (c) 2019, Jacky <liangj97@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.inferno;
import com.google.common.base.Strings;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.Prayer;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.plugins.inferno.displaymodes.InfernoPrayerDisplayMode;
import net.runelite.client.plugins.inferno.displaymodes.InfernoSafespotDisplayMode;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
import net.runelite.client.ui.overlay.OverlayUtil;
@Singleton
public class InfernoOverlay extends Overlay
{
private final Client client;
private static final int TICK_PIXEL_SIZE = 60;
private static final int BOX_WIDTH = 10;
private static final int BOX_HEIGHT = 5;
private final InfernoPlugin plugin;
private final Client client;
@Inject
public InfernoOverlay(final Client client, final InfernoPlugin plugin)
private InfernoOverlay(final Client client, final InfernoPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
setLayer(OverlayLayer.ABOVE_WIDGETS);
setPriority(OverlayPriority.HIGHEST);
this.client = client;
this.plugin = plugin;
}
@@ -58,55 +48,426 @@ public class InfernoOverlay extends Overlay
@Override
public Dimension render(Graphics2D graphics)
{
if (!client.isInInstancedRegion() || client.getMapRegions()[0] != 9043)
final Widget meleePrayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MELEE);
final Widget rangePrayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES);
final Widget magicPrayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC);
if (plugin.isIndicateObstacles())
{
return null;
renderObstacles(graphics);
}
for (InfernoNPC monster : plugin.getMonsters().values())
if (plugin.getSafespotDisplayMode() == InfernoSafespotDisplayMode.AREA)
{
NPC npc = monster.getNpc();
//if (npc == null || !config.showPrayer()) return;
LocalPoint lp = npc.getLocalLocation();
if (lp != null)
renderAreaSafepots(graphics);
}
else if (plugin.getSafespotDisplayMode() == InfernoSafespotDisplayMode.INDIVIDUAL_TILES)
{
renderIndividualTilesSafespots(graphics);
}
for (InfernoNPC infernoNPC : plugin.getInfernoNpcs())
{
if (infernoNPC.getNpc().getConvexHull() != null)
{
Point point = Perspective.localToCanvas(client, lp, client.getPlane(), npc.getLogicalHeight());
if (point != null)
if (plugin.isIndicateNonSafespotted() && plugin.isNormalSafespots(infernoNPC)
&& infernoNPC.canAttack(client, client.getLocalPlayer().getWorldLocation()))
{
if (monster.getTicksTillAttack() == 1 || (monster.getName().equals("blob") && monster.getTicksTillAttack() <= 3))
{
renderTextLocation(graphics, monster, monster.info(), Color.GREEN);
}
else
{
renderTextLocation(graphics, monster, monster.info(), Color.RED);
}
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.RED);
}
if (plugin.isIndicateTemporarySafespotted() && plugin.isNormalSafespots(infernoNPC)
&& infernoNPC.canMoveToAttack(client, client.getLocalPlayer().getWorldLocation(), plugin.getObstacles()))
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.YELLOW);
}
if (plugin.isIndicateSafespotted() && plugin.isNormalSafespots(infernoNPC))
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.GREEN);
}
if (plugin.isIndicateNibblers() && infernoNPC.getType() == InfernoNPC.Type.NIBBLER
&& (!plugin.isIndicateCentralNibbler() || plugin.getCentralNibbler() != infernoNPC))
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.CYAN);
}
if (plugin.isIndicateCentralNibbler() && infernoNPC.getType() == InfernoNPC.Type.NIBBLER
&& plugin.getCentralNibbler() == infernoNPC)
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.BLUE);
}
if (plugin.isIndicateActiveHealersJad() && infernoNPC.getType() == InfernoNPC.Type.HEALER_JAD
&& infernoNPC.getNpc().getInteracting() != client.getLocalPlayer())
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.CYAN);
}
if (plugin.isIndicateActiveHealersZuk() && infernoNPC.getType() == InfernoNPC.Type.HEALER_ZUK
&& infernoNPC.getNpc().getInteracting() != client.getLocalPlayer())
{
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.CYAN);
}
}
if (plugin.isIndicateNpcPosition(infernoNPC))
{
renderNpcLocation(graphics, infernoNPC);
}
if (plugin.isTicksOnNpc(infernoNPC) && infernoNPC.getTicksTillNextAttack() > 0)
{
renderTicksOnNpc(graphics, infernoNPC);
}
}
if ((plugin.getPrayerDisplayMode() == InfernoPrayerDisplayMode.PRAYER_TAB
|| plugin.getPrayerDisplayMode() == InfernoPrayerDisplayMode.BOTH)
&& (meleePrayerWidget != null && !meleePrayerWidget.isHidden()
&& rangePrayerWidget != null && !rangePrayerWidget.isHidden()
&& magicPrayerWidget != null && !magicPrayerWidget.isHidden()))
{
renderPrayerIconOverlay(graphics);
if (plugin.isDescendingBoxes())
{
renderDescendingBoxes(graphics);
}
}
return null;
}
// renders text location
private static void renderTextLocation(Graphics2D graphics, InfernoNPC actor, String text, Color color)
private void renderObstacles(Graphics2D graphics)
{
graphics.setFont(new Font("Arial", Font.BOLD, 15));
Point textLocation = actor.getNpc().getCanvasTextLocation(graphics, text, actor.textLocHeight + 40);
if (Strings.isNullOrEmpty(text))
for (WorldPoint worldPoint : plugin.getObstacles())
{
return;
}
final LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint);
if (textLocation != null)
{
int x = textLocation.getX();
int y = textLocation.getY();
if (localPoint == null)
{
continue;
}
graphics.setColor(Color.BLACK);
graphics.drawString(text, x + 1, y + 1);
final Polygon tilePoly = Perspective.getCanvasTilePoly(client, localPoint);
graphics.setColor(color);
graphics.drawString(text, x, y);
if (tilePoly == null)
{
continue;
}
OverlayUtil.renderPolygon(graphics, tilePoly, Color.BLUE);
}
}
private void renderAreaSafepots(Graphics2D graphics)
{
for (int safeSpotId : plugin.getSafeSpotAreas().keySet())
{
if (safeSpotId > 6)
{
continue;
}
Color colorEdge1 = null;
Color colorEdge2 = null;
Color colorFill = null;
switch (safeSpotId)
{
case 0:
colorEdge1 = Color.WHITE;
colorFill = Color.WHITE;
break;
case 1:
colorEdge1 = Color.RED;
colorFill = Color.RED;
break;
case 2:
colorEdge1 = Color.GREEN;
colorFill = Color.GREEN;
break;
case 3:
colorEdge1 = Color.BLUE;
colorFill = Color.BLUE;
break;
case 4:
colorEdge1 = Color.RED;
colorEdge2 = Color.GREEN;
colorFill = Color.YELLOW;
break;
case 5:
colorEdge1 = Color.RED;
colorEdge2 = Color.BLUE;
colorFill = new Color(255, 0, 255);
break;
case 6:
colorEdge1 = Color.GREEN;
colorEdge2 = Color.BLUE;
colorFill = new Color(0, 255, 255);
break;
default:
continue;
}
//Add all edges, calculate average edgeSize and indicate tiles
final List<int[][]> allEdges = new ArrayList<>();
int edgeSizeSquared = 0;
for (WorldPoint worldPoint : plugin.getSafeSpotAreas().get(safeSpotId))
{
final LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint);
if (localPoint == null)
{
continue;
}
final Polygon tilePoly = Perspective.getCanvasTilePoly(client, localPoint);
if (tilePoly == null)
{
continue;
}
OverlayUtil.renderAreaTilePolygon(graphics, tilePoly, colorFill);
final int[][] edge1 = new int[][]{{tilePoly.xpoints[0], tilePoly.ypoints[0]}, {tilePoly.xpoints[1], tilePoly.ypoints[1]}};
edgeSizeSquared += Math.pow(tilePoly.xpoints[0] - tilePoly.xpoints[1], 2) + Math.pow(tilePoly.ypoints[0] - tilePoly.ypoints[1], 2);
allEdges.add(edge1);
final int[][] edge2 = new int[][]{{tilePoly.xpoints[1], tilePoly.ypoints[1]}, {tilePoly.xpoints[2], tilePoly.ypoints[2]}};
edgeSizeSquared += Math.pow(tilePoly.xpoints[1] - tilePoly.xpoints[2], 2) + Math.pow(tilePoly.ypoints[1] - tilePoly.ypoints[2], 2);
allEdges.add(edge2);
final int[][] edge3 = new int[][]{{tilePoly.xpoints[2], tilePoly.ypoints[2]}, {tilePoly.xpoints[3], tilePoly.ypoints[3]}};
edgeSizeSquared += Math.pow(tilePoly.xpoints[2] - tilePoly.xpoints[3], 2) + Math.pow(tilePoly.ypoints[2] - tilePoly.ypoints[3], 2);
allEdges.add(edge3);
final int[][] edge4 = new int[][]{{tilePoly.xpoints[3], tilePoly.ypoints[3]}, {tilePoly.xpoints[0], tilePoly.ypoints[0]}};
edgeSizeSquared += Math.pow(tilePoly.xpoints[3] - tilePoly.xpoints[0], 2) + Math.pow(tilePoly.ypoints[3] - tilePoly.ypoints[0], 2);
allEdges.add(edge4);
}
if (allEdges.size() <= 0)
{
continue;
}
edgeSizeSquared /= allEdges.size();
//Find and indicate unique edges
final int toleranceSquared = (int) Math.ceil(edgeSizeSquared / 6);
for (int i = 0; i < allEdges.size(); i++)
{
int[][] baseEdge = allEdges.get(i);
boolean duplicate = false;
for (int j = 0; j < allEdges.size(); j++)
{
if (i == j)
{
continue;
}
int[][] checkEdge = allEdges.get(j);
if (edgeEqualsEdge(baseEdge, checkEdge, toleranceSquared))
{
duplicate = true;
break;
}
}
if (!duplicate)
{
OverlayUtil.renderFullLine(graphics, baseEdge, colorEdge1);
if (colorEdge2 != null)
{
OverlayUtil.renderDashedLine(graphics, baseEdge, colorEdge2);
}
}
}
}
}
private void renderIndividualTilesSafespots(Graphics2D graphics)
{
for (WorldPoint worldPoint : plugin.getSafeSpotMap().keySet())
{
final int safeSpotId = plugin.getSafeSpotMap().get(worldPoint);
if (safeSpotId > 6)
{
continue;
}
final LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint);
if (localPoint == null)
{
continue;
}
final Polygon tilePoly = Perspective.getCanvasTilePoly(client, localPoint);
if (tilePoly == null)
{
continue;
}
Color color;
switch (safeSpotId)
{
case 0:
color = Color.WHITE;
break;
case 1:
color = Color.RED;
break;
case 2:
color = Color.GREEN;
break;
case 3:
color = Color.BLUE;
break;
case 4:
color = new Color(255, 255, 0);
break;
case 5:
color = new Color(255, 0, 255);
break;
case 6:
color = new Color(0, 255, 255);
break;
default:
continue;
}
OverlayUtil.renderPolygon(graphics, tilePoly, color);
}
}
private void renderTicksOnNpc(Graphics2D graphics, InfernoNPC infernoNPC)
{
final Color color = (infernoNPC.getTicksTillNextAttack() == 1
|| (infernoNPC.getType() == InfernoNPC.Type.BLOB && infernoNPC.getTicksTillNextAttack() == 4))
? infernoNPC.getNextAttack().getCriticalColor() : infernoNPC.getNextAttack().getNormalColor();
final Point canvasPoint = infernoNPC.getNpc().getCanvasTextLocation(
graphics, String.valueOf(infernoNPC.getTicksTillNextAttack()), 0);
OverlayUtil.renderTextLocation(graphics, String.valueOf(infernoNPC.getTicksTillNextAttack()),
plugin.getTextSize(), plugin.getFontStyle().getFont(), color, canvasPoint, false, 0);
}
private void renderNpcLocation(Graphics2D graphics, InfernoNPC infernoNPC)
{
final LocalPoint localPoint = LocalPoint.fromWorld(client, infernoNPC.getNpc().getWorldLocation());
if (localPoint != null)
{
final Polygon tilePolygon = Perspective.getCanvasTilePoly(client, localPoint);
if (tilePolygon != null)
{
OverlayUtil.renderPolygon(graphics, tilePolygon, Color.BLUE);
}
}
}
private void renderDescendingBoxes(Graphics2D graphics)
{
for (Integer tick : plugin.getUpcomingAttacks().keySet())
{
final Map<InfernoNPC.Attack, Integer> attackPriority = plugin.getUpcomingAttacks().get(tick);
int bestPriority = 999;
InfernoNPC.Attack bestAttack = null;
for (Map.Entry<InfernoNPC.Attack, Integer> attackEntry : attackPriority.entrySet())
{
if (attackEntry.getValue() < bestPriority)
{
bestAttack = attackEntry.getKey();
bestPriority = attackEntry.getValue();
}
}
for (InfernoNPC.Attack currentAttack : attackPriority.keySet())
{
//TODO: Config values for these colors
final Color color = (tick == 1 && currentAttack == bestAttack) ? Color.RED : Color.ORANGE;
final Widget prayerWidget = client.getWidget(currentAttack.getPrayer().getWidgetInfo());
int baseX = (int) prayerWidget.getBounds().getX();
baseX += prayerWidget.getBounds().getWidth() / 2;
baseX -= BOX_WIDTH / 2;
int baseY = (int) prayerWidget.getBounds().getY() - tick * TICK_PIXEL_SIZE - BOX_HEIGHT;
baseY += TICK_PIXEL_SIZE - ((plugin.getLastTick() + 600 - System.currentTimeMillis()) / 600.0 * TICK_PIXEL_SIZE);
final Rectangle boxRectangle = new Rectangle(BOX_WIDTH, BOX_HEIGHT);
boxRectangle.translate(baseX, baseY);
if (currentAttack == bestAttack)
{
OverlayUtil.renderFilledPolygon(graphics, boxRectangle, color);
}
else if (plugin.isIndicateNonPriorityDescendingBoxes())
{
OverlayUtil.renderOutlinePolygon(graphics, boxRectangle, color);
}
}
}
}
private void renderPrayerIconOverlay(Graphics2D graphics)
{
if (plugin.getClosestAttack() != null)
{
// Prayer indicator in prayer tab
InfernoNPC.Attack prayerForAttack = null;
if (client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC))
{
prayerForAttack = InfernoNPC.Attack.MAGIC;
}
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES))
{
prayerForAttack = InfernoNPC.Attack.RANGED;
}
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MELEE))
{
prayerForAttack = InfernoNPC.Attack.MELEE;
}
if (plugin.getClosestAttack() != prayerForAttack || plugin.isIndicateWhenPrayingCorrectly())
{
final Widget prayerWidget = client.getWidget(plugin.getClosestAttack().getPrayer().getWidgetInfo());
final Rectangle prayerRectangle = new Rectangle((int) prayerWidget.getBounds().getWidth(),
(int) prayerWidget.getBounds().getHeight());
prayerRectangle.translate((int) prayerWidget.getBounds().getX(), (int) prayerWidget.getBounds().getY());
//TODO: Config values for these colors
Color prayerColor;
if (plugin.getClosestAttack() == prayerForAttack)
{
prayerColor = Color.GREEN;
}
else
{
prayerColor = Color.RED;
}
OverlayUtil.renderOutlinePolygon(graphics, prayerRectangle, prayerColor);
}
}
}
private boolean edgeEqualsEdge(int[][] edge1, int[][] edge2, int toleranceSquared)
{
return (pointEqualsPoint(edge1[0], edge2[0], toleranceSquared) && pointEqualsPoint(edge1[1], edge2[1], toleranceSquared))
|| (pointEqualsPoint(edge1[0], edge2[1], toleranceSquared) && pointEqualsPoint(edge1[1], edge2[0], toleranceSquared));
}
private boolean pointEqualsPoint(int[] point1, int[] point2, int toleranceSquared)
{
double distanceSquared = Math.pow(point1[0] - point2[0], 2) + Math.pow(point1[1] - point2[1], 2);
return distanceSquared <= toleranceSquared;
}
}

View File

@@ -1,138 +0,0 @@
package net.runelite.client.plugins.inferno;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.Prayer;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
import net.runelite.client.ui.overlay.OverlayUtil;
public class InfernoPrayerOverlay extends Overlay
{
private static final int TICK_PIXEL_SIZE = 60;
private static final int BLOB_WIDTH = 10;
private static final int BLOB_HEIGHT = 5;
private final InfernoPlugin plugin;
private final Client client;
@Inject
private InfernoPrayerOverlay(final Client client, final InfernoPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_WIDGETS);
setPriority(OverlayPriority.HIGHEST);
this.client = client;
this.plugin = plugin;
}
@Override
public Dimension render(Graphics2D graphics)
{
if (client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC).isHidden()
|| client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES).isHidden())
{
return null;
}
InfernoJad.Attack prayerForAttack = null;
if (client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC))
{
prayerForAttack = InfernoJad.Attack.MAGIC;
}
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES))
{
prayerForAttack = InfernoJad.Attack.RANGE;
}
InfernoJad.Attack closestAttack = null;
int leastTicks = 999;
for (InfernoJad jad : plugin.getJads())
{
if (jad.getNextAttack() == null || jad.getTicksTillNextAttack() < 1)
{
continue;
}
if (jad.getTicksTillNextAttack() < leastTicks)
{
leastTicks = jad.getTicksTillNextAttack();
closestAttack = jad.getNextAttack();
}
if (!plugin.isDescendingBoxes() || !plugin.isShowPrayerHelp()
|| (plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.PRAYER_TAB
&& plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.BOTH))
{
continue;
}
final Widget prayerWidget = jad.getNextAttack() == InfernoJad.Attack.MAGIC
? client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC) : client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES);
int baseX = (int) prayerWidget.getBounds().getX();
baseX += prayerWidget.getBounds().getWidth() / 2;
baseX -= BLOB_WIDTH / 2;
int baseY = (int) prayerWidget.getBounds().getY() - jad.getTicksTillNextAttack() * TICK_PIXEL_SIZE - BLOB_HEIGHT;
baseY += TICK_PIXEL_SIZE - ((plugin.getLastTick() + 600 - System.currentTimeMillis()) / 600.0 * TICK_PIXEL_SIZE);
final Polygon blob = new Polygon(new int[]{0, BLOB_WIDTH, BLOB_WIDTH, 0}, new int[]{0, 0, BLOB_HEIGHT, BLOB_HEIGHT}, 4);
blob.translate(baseX, baseY);
OverlayUtil.renderPolygon(graphics, blob, Color.ORANGE);
}
if (plugin.isShowPrayerHelp() && closestAttack != null
&& (closestAttack != prayerForAttack || plugin.isIndicateWhenPrayingCorrectly())
&& (plugin.getPrayerOverlayMode() == InfernoPrayerOverlayMode.PRAYER_TAB
|| plugin.getPrayerOverlayMode() == InfernoPrayerOverlayMode.BOTH))
{
final Widget prayerWidget = closestAttack == InfernoJad.Attack.MAGIC
? client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC) : client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES);
final Polygon prayer = new Polygon(
new int[]{0, (int) prayerWidget.getBounds().getWidth(), (int) prayerWidget.getBounds().getWidth(), 0},
new int[]{0, 0, (int) prayerWidget.getBounds().getHeight(), (int) prayerWidget.getBounds().getHeight()},
4);
prayer.translate((int) prayerWidget.getBounds().getX(), (int) prayerWidget.getBounds().getY());
Color prayerColor;
if (closestAttack == prayerForAttack)
{
prayerColor = Color.GREEN;
}
else
{
prayerColor = Color.RED;
}
OverlayUtil.renderPolygon(graphics, prayer, prayerColor);
}
if (plugin.isIndicateActiveHealers())
{
for (NPC healer : plugin.getActiveHealers())
{
if (healer.getConvexHull() == null)
{
continue;
}
OverlayUtil.renderPolygon(graphics, healer.getConvexHull(), Color.CYAN);
}
}
return null;
}
}

View File

@@ -1,19 +0,0 @@
package net.runelite.client.plugins.inferno;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum InfernoPrayerOverlayMode
{
PRAYER_TAB("Prayer Tab"),
BOTTOM_RIGHT("Bottom Right"),
BOTH("Both");
private final String name;
@Override
public String toString()
{
return name;
}
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2019, Kyleeld <https://github.com/kyleeld>
* Copyright (c) 2019, openosrs <https://openosrs.com>
* Copyright (c) 2019, RuneLitePlus <https://runelitepl.us>
*
* All rights reserved.
*
@@ -28,18 +28,21 @@ package net.runelite.client.plugins.inferno;
import com.google.common.collect.ImmutableMap;
import java.awt.Color;
import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.plugins.inferno.displaymodes.InfernoNamingDisplayMode;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.components.TitleComponent;
class InfernoWaveMappings
{
@Getter(AccessLevel.PACKAGE)
private static final ImmutableMap<Integer, int[]> waveMapping;
private static final Map<Integer, int[]> waveMapping;
@Getter(AccessLevel.PACKAGE)
private static final ImmutableMap<Integer, String> npcNameMapping;
private static final Map<Integer, String> npcNameMappingComplex;
@Getter(AccessLevel.PACKAGE)
private static final Map<Integer, String> npcNameMappingSimple;
static
{
@@ -108,30 +111,43 @@ class InfernoWaveMappings
waveMapBuilder.put(61, new int[]{32, 32, 32, 85, 165, 240, 370, 490});
waveMapBuilder.put(62, new int[]{32, 32, 32, 85, 85, 165, 240, 370, 490});
waveMapBuilder.put(63, new int[]{32, 32, 32, 165, 165, 240, 370, 490});
waveMapBuilder.put(64, new int[]{32, 32, 32, 85, 240, 240, 370, 490});
waveMapBuilder.put(65, new int[]{32, 32, 32, 85, 370, 370, 490});
waveMapBuilder.put(66, new int[]{32, 32, 32, 85, 490, 490});
waveMapBuilder.put(64, new int[]{32, 32, 32, 240, 240, 370, 490});
waveMapBuilder.put(65, new int[]{32, 32, 32, 370, 370, 490});
waveMapBuilder.put(66, new int[]{32, 32, 32, 490, 490});
waveMapBuilder.put(67, new int[]{900});
waveMapBuilder.put(68, new int[]{900, 900, 900});
waveMapBuilder.put(69, new int[]{1400});
waveMapping = waveMapBuilder.build();
ImmutableMap.Builder<Integer, String> nameMapBuilder = new ImmutableMap.Builder<>();
ImmutableMap.Builder<Integer, String> nameMapBuilderSimple = new ImmutableMap.Builder<>();
nameMapBuilder.put(32, "Jal-Nib - Level 32");
nameMapBuilder.put(85, "Jal-MejRah - Level 85");
nameMapBuilder.put(165, "Jal-Ak - Level 165");
nameMapBuilder.put(240, "Jal-ImKot - Level 240");
nameMapBuilder.put(370, "Jal-Xil - Level 370");
nameMapBuilder.put(490, "Jal-Zek - Level 490");
nameMapBuilder.put(900, "JalTok-Jad - Level 900");
nameMapBuilder.put(1400, "TzKal-Zuk - Level 1400");
nameMapBuilderSimple.put(32, "Nibbler");
nameMapBuilderSimple.put(85, "Bat");
nameMapBuilderSimple.put(165, "Blob");
nameMapBuilderSimple.put(240, "Meleer");
nameMapBuilderSimple.put(370, "Ranger");
nameMapBuilderSimple.put(490, "Mage");
nameMapBuilderSimple.put(900, "Jad");
nameMapBuilderSimple.put(1400, "Zuk");
npcNameMapping = nameMapBuilder.build();
npcNameMappingSimple = nameMapBuilderSimple.build();
ImmutableMap.Builder<Integer, String> nameMapBuilderComplex = new ImmutableMap.Builder<>();
nameMapBuilderComplex.put(32, "Jal-Nib");
nameMapBuilderComplex.put(85, "Jal-MejRah");
nameMapBuilderComplex.put(165, "Jal-Ak");
nameMapBuilderComplex.put(240, "Jal-ImKot");
nameMapBuilderComplex.put(370, "Jal-Xil");
nameMapBuilderComplex.put(490, "Jal-Zek");
nameMapBuilderComplex.put(900, "JalTok-Jad");
nameMapBuilderComplex.put(1400, "TzKal-Zuk");
npcNameMappingComplex = nameMapBuilderComplex.build();
}
static void addWaveComponent(PanelComponent panelComponent, String header, int wave, Color titleColor, Color color)
static void addWaveComponent(InfernoPlugin plugin, PanelComponent panelComponent, String header, int wave, Color titleColor, Color color)
{
int[] monsters = waveMapping.get(wave);
@@ -160,7 +176,23 @@ class InfernoWaveMappings
TitleComponent.TitleComponentBuilder builder = TitleComponent.builder();
builder.text(count + "x " + npcNameMapping.get(monsterType));
String npcNameText = "";
if (plugin.getNpcNaming() == InfernoNamingDisplayMode.SIMPLE)
{
npcNameText += npcNameMappingSimple.get(monsterType);
}
else
{
npcNameText += npcNameMappingComplex.get(monsterType);
}
if (plugin.isNpcLevels())
{
npcNameText += " (" + monsterType + ")";
}
builder.text(count + "x " + npcNameText);
builder.color(color);
panelComponent.getChildren().add(builder.build());

View File

@@ -8,6 +8,7 @@ import java.awt.Graphics2D;
import lombok.AccessLevel;
import lombok.Setter;
import static net.runelite.client.plugins.inferno.InfernoWaveMappings.addWaveComponent;
import net.runelite.client.plugins.inferno.displaymodes.InfernoWaveDisplayMode;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
@@ -47,6 +48,7 @@ public class InfernoWaveOverlay extends Overlay
displayMode == InfernoWaveDisplayMode.BOTH)
{
addWaveComponent(
plugin,
panelComponent,
"Current Wave (Wave " + plugin.getCurrentWaveNumber() + ")",
plugin.getCurrentWaveNumber(),
@@ -59,6 +61,7 @@ public class InfernoWaveOverlay extends Overlay
displayMode == InfernoWaveDisplayMode.BOTH)
{
addWaveComponent(
plugin,
panelComponent,
"Next Wave (Wave " + plugin.getNextWaveNumber() + ")",
plugin.getNextWaveNumber(),

View File

@@ -0,0 +1,7 @@
package net.runelite.client.plugins.inferno.displaymodes;
public enum InfernoNamingDisplayMode
{
SIMPLE,
COMPLEX
}

View File

@@ -0,0 +1,8 @@
package net.runelite.client.plugins.inferno.displaymodes;
public enum InfernoPrayerDisplayMode
{
PRAYER_TAB,
BOTTOM_RIGHT,
BOTH
}

View File

@@ -0,0 +1,25 @@
package net.runelite.client.plugins.inferno.displaymodes;
import lombok.AccessLevel;
import lombok.Getter;
@Getter(AccessLevel.PACKAGE)
public enum InfernoSafespotDisplayMode
{
OFF("Off"),
INDIVIDUAL_TILES("Individual tiles"),
AREA("Area (lower fps)");
final private String name;
InfernoSafespotDisplayMode(String name)
{
this.name = name;
}
@Override
public String toString()
{
return this.name;
}
}

View File

@@ -22,7 +22,7 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.inferno;
package net.runelite.client.plugins.inferno.displaymodes;
import lombok.RequiredArgsConstructor;

View File

@@ -0,0 +1,25 @@
package net.runelite.client.plugins.inferno.displaymodes;
import lombok.AccessLevel;
import lombok.Getter;
@Getter(AccessLevel.PACKAGE)
public enum InfernoZukShieldDisplayMode
{
OFF("Off"),
LIVE("Live (follow shield)"),
PREDICT("Predict (NOT WORKING YET)");
final private String name;
InfernoZukShieldDisplayMode(String name)
{
this.name = name;
}
@Override
public String toString()
{
return this.name;
}
}

View File

@@ -34,23 +34,17 @@ import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nullable;
import javax.inject.Singleton;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.HyperlinkEvent;
import net.runelite.api.Client;
import static net.runelite.client.RuneLite.LOGS_DIR;
import net.runelite.client.RuneLiteProperties;
import net.runelite.client.account.SessionManager;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.events.SessionClose;
import net.runelite.client.events.SessionOpen;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
@@ -58,10 +52,8 @@ import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.LinkBrowser;
@Singleton
public class InfoPanel extends PluginPanel
class InfoPanel extends PluginPanel
{
private static final String RUNELITE_LOGIN = "https://runelite_login/";
private static final ImageIcon ARROW_RIGHT_ICON;
private static final ImageIcon GITHUB_ICON;
private static final ImageIcon FOLDER_ICON;
@@ -69,21 +61,12 @@ public class InfoPanel extends PluginPanel
private static final ImageIcon PATREON_ICON;
private static final ImageIcon IMPORT_ICON;
private final JLabel loggedLabel = new JLabel();
private final JRichTextPane emailLabel = new JRichTextPane();
private JPanel syncPanel;
private JPanel actionsContainer;
@Inject
@Nullable
private Client client;
@Inject
private SessionManager sessionManager;
@Inject
private ScheduledExecutorService executor;
@Inject
private ConfigManager configManager;
@@ -127,28 +110,11 @@ public class InfoPanel extends PluginPanel
revision.setText(htmlLabel("Oldschool revision: ", engineVer));
loggedLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
loggedLabel.setFont(smallFont);
emailLabel.setForeground(Color.WHITE);
emailLabel.setFont(smallFont);
emailLabel.enableAutoLinkHandler(false);
emailLabel.addHyperlinkListener(e ->
{
if (HyperlinkEvent.EventType.ACTIVATED.equals(e.getEventType()) && e.getURL() != null && e.getURL().toString().equals(RUNELITE_LOGIN))
{
executor.execute(sessionManager::login);
}
});
versionPanel.add(version);
versionPanel.add(plusVersion);
versionPanel.add(revision);
versionPanel.add(Box.createGlue());
versionPanel.add(loggedLabel);
versionPanel.add(emailLabel);
actionsContainer = new JPanel();
JPanel actionsContainer = new JPanel();
actionsContainer.setBorder(new EmptyBorder(10, 0, 0, 0));
actionsContainer.setLayout(new GridLayout(5, 1, 0, 10));
@@ -173,8 +139,6 @@ public class InfoPanel extends PluginPanel
add(versionPanel, BorderLayout.NORTH);
add(actionsContainer, BorderLayout.CENTER);
updateLoggedIn();
}
/**
@@ -267,40 +231,8 @@ public class InfoPanel extends PluginPanel
return container;
}
private void updateLoggedIn()
{
final String name = sessionManager.getAccountSession() != null
? sessionManager.getAccountSession().getUsername()
: null;
if (name != null)
{
emailLabel.setContentType("text/plain");
emailLabel.setText(name);
loggedLabel.setText("Logged in as");
actionsContainer.add(syncPanel, 0);
}
else
{
//emailLabel.setContentType("text/html");
//emailLabel.setText("<a href=\"" + RUNELITE_LOGIN + "\">Login</a> to sync settings to the cloud.");
//loggedLabel.setText("Not logged in");
actionsContainer.remove(syncPanel);
}
}
private static String htmlLabel(String key, String value)
{
return "<html><body style = 'color:#a5a5a5'>" + key + "<span style = 'color:white'>" + value + "</span></body></html>";
}
public void onSessionOpen(SessionOpen sessionOpen)
{
updateLoggedIn();
}
public void onSessionClose(SessionClose e)
{
updateLoggedIn();
}
}

View File

@@ -28,8 +28,6 @@ import java.awt.image.BufferedImage;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.events.SessionClose;
import net.runelite.client.events.SessionOpen;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.ClientToolbar;
@@ -53,12 +51,10 @@ public class InfoPlugin extends Plugin
private NavigationButton navButton;
private InfoPanel panel;
@Override
protected void startUp() throws Exception
{
panel = injector.getInstance(InfoPanel.class);
InfoPanel panel = injector.getInstance(InfoPanel.class);
panel.init();
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "info_icon.png");
@@ -71,8 +67,6 @@ public class InfoPlugin extends Plugin
.build();
clientToolbar.addNavigation(navButton);
addSubscriptions();
}
@Override
@@ -82,10 +76,4 @@ public class InfoPlugin extends Plugin
clientToolbar.removeNavigation(navButton);
}
private void addSubscriptions()
{
eventbus.subscribe(SessionOpen.class, this, event -> panel.onSessionOpen(event));
eventbus.subscribe(SessionClose.class, this, event -> panel.onSessionClose(event));
}
}

View File

@@ -250,7 +250,7 @@ public class InventoryTagsPlugin extends Plugin
// Need to set the event entries to prevent conflicts
event.setMenuEntries(menuList);
event.setModified(true);
event.setModified();
}
}

View File

@@ -472,14 +472,25 @@ public interface ItemChargeConfig extends Config
return true;
}
@ConfigItem(
keyName = "showBasketCharges",
name = "Show Basket Quantity",
description = "Configures if the number of fruit in a basket is shown",
position = 26
)
default boolean showBasketCharges()
{
return true;
}
@ConfigItem(
keyName = "showInfoboxes",
name = "Show Infoboxes",
description = "Configures whether to show an infobox equipped charge items",
position = 26
position = 27
)
default boolean showInfoboxes()
{
return false;
}
}
}

View File

@@ -36,9 +36,11 @@ import static net.runelite.client.plugins.itemcharges.ItemChargeType.ABYSSAL_BRA
import static net.runelite.client.plugins.itemcharges.ItemChargeType.BELLOWS;
import static net.runelite.client.plugins.itemcharges.ItemChargeType.FUNGICIDE_SPRAY;
import static net.runelite.client.plugins.itemcharges.ItemChargeType.IMPBOX;
import static net.runelite.client.plugins.itemcharges.ItemChargeType.SACK;
import static net.runelite.client.plugins.itemcharges.ItemChargeType.TELEPORT;
import static net.runelite.client.plugins.itemcharges.ItemChargeType.WATERCAN;
import static net.runelite.client.plugins.itemcharges.ItemChargeType.WATERSKIN;
import static net.runelite.client.plugins.itemcharges.ItemChargeType.FRUIT_BASKET;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.overlay.WidgetItemOverlay;
import net.runelite.client.ui.overlay.components.TextComponent;
@@ -160,6 +162,8 @@ class ItemChargeOverlay extends WidgetItemOverlay
|| (type == WATERCAN && !plugin.isShowWateringCanCharges())
|| (type == WATERSKIN && !plugin.isShowWaterskinCharges())
|| (type == BELLOWS && !plugin.isShowBellowCharges())
|| (type == FRUIT_BASKET && !plugin.isShowBasketCharges())
|| (type == SACK && !plugin.isShowSackCharges())
|| (type == ABYSSAL_BRACELET && !plugin.isShowAbyssalBraceletCharges()))
{
return;
@@ -180,7 +184,7 @@ class ItemChargeOverlay extends WidgetItemOverlay
{
return plugin.isShowTeleportCharges() || plugin.isShowDodgyCount() || plugin.isShowFungicideCharges()
|| plugin.isShowImpCharges() || plugin.isShowWateringCanCharges() || plugin.isShowWaterskinCharges()
|| plugin.isShowBellowCharges() || plugin.isShowAbyssalBraceletCharges() || plugin.isShowExplorerRingCharges()
|| plugin.isShowBellowCharges() || plugin.isShowBasketCharges() || plugin.isShowSackCharges() || plugin.isShowAbyssalBraceletCharges() || plugin.isShowExplorerRingCharges()
|| plugin.isShowRingOfForgingCount();
}
}
}

View File

@@ -956,6 +956,7 @@ public class ItemChargePlugin extends Plugin
this.showXericTalismanCharges = config.showXericTalismanCharges();
this.showrecoil = config.showrecoil();
this.chronicle = config.chronicle();
this.showBasketCharges = config.showBasketCharges();
this.showSackCharges = config.showSackCharges();
}
}

View File

@@ -1,113 +0,0 @@
/*
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.itemskeptondeath;
import com.google.common.collect.ImmutableMap;
import javax.annotation.Nullable;
import lombok.AllArgsConstructor;
import net.runelite.api.ItemID;
/**
* Some non tradeable items are kept on death inside low level wilderness (1-20) but are turned into a broken variant.
*
* The non-broken variant will be shown inside the interface.
*/
@AllArgsConstructor
enum BrokenOnDeathItem
{
// Capes
FIRE_CAPE(ItemID.FIRE_CAPE, 50000),
FIRE_MAX_CAPE(ItemID.FIRE_MAX_CAPE, 50000),
INFERNAL_CAPE(ItemID.INFERNAL_CAPE, 50000),
INFERNAL_MAX_CAPE(ItemID.INFERNAL_MAX_CAPE, 50000),
AVAS_ASSEMBLER(ItemID.AVAS_ASSEMBLER, 75000),
ASSEMBLER_MAX_CAPE(ItemID.ASSEMBLER_MAX_CAPE, 75000),
// Defenders
BRONZE_DEFENDER(ItemID.BRONZE_DEFENDER, 1000),
IRON_DEFENDER(ItemID.IRON_DEFENDER, 2000),
STEEL_DEFENDER(ItemID.STEEL_DEFENDER, 2500),
BLACK_DEFENDER(ItemID.BLACK_DEFENDER, 5000),
MITHRIL_DEFENDER(ItemID.MITHRIL_DEFENDER, 15000),
ADAMANT_DEFENDER(ItemID.ADAMANT_DEFENDER, 25000),
RUNE_DEFENDER(ItemID.RUNE_DEFENDER, 35000),
DRAGON_DEFENDER(ItemID.DRAGON_DEFENDER, 40000),
AVERNIC_DEFENDER(ItemID.AVERNIC_DEFENDER, 1000000),
// Void
VOID_MAGE_HELM(ItemID.VOID_MAGE_HELM, 40000),
VOID_RANGER_HELM(ItemID.VOID_RANGER_HELM, 40000),
VOID_MELEE_HELM(ItemID.VOID_MELEE_HELM, 40000),
VOID_KNIGHT_TOP(ItemID.VOID_KNIGHT_TOP, 45000),
VOID_KNIGHT_ROBE(ItemID.VOID_KNIGHT_ROBE, 45000),
VOID_KNIGHT_GLOVES(ItemID.VOID_KNIGHT_GLOVES, 30000),
ELITE_VOID_TOP(ItemID.ELITE_VOID_TOP, 50000),
ELITE_VOID_ROBE(ItemID.ELITE_VOID_ROBE, 50000),
// Barb Assault
FIGHTER_HAT(ItemID.FIGHTER_HAT, 45000),
RANGER_HAT(ItemID.RANGER_HAT, 45000),
HEALER_HAT(ItemID.HEALER_HAT, 45000),
FIGHTER_TORSO(ItemID.FIGHTER_TORSO, 50000),
PENANCE_SKIRT(ItemID.PENANCE_SKIRT, 20000),
// Castle Wars
SARADOMIN_HALO(ItemID.SARADOMIN_HALO, 25000),
ZAMORAK_HALO(ItemID.ZAMORAK_HALO, 25000),
GUTHIX_HALO(ItemID.GUTHIX_HALO, 25000),
DECORATIVE_MAGIC_HAT(ItemID.DECORATIVE_ARMOUR_11898, 5000),
DECORATIVE_MAGIC_ROBE_TOP(ItemID.DECORATIVE_ARMOUR_11896, 5000),
DECORATIVE_MAGIC_ROBE_LEGS(ItemID.DECORATIVE_ARMOUR_11897, 5000),
DECORATIVE_RANGE_TOP(ItemID.DECORATIVE_ARMOUR_11899, 5000),
DECORATIVE_RANGE_BOTTOM(ItemID.DECORATIVE_ARMOUR_11900, 5000),
DECORATIVE_RANGE_QUIVER(ItemID.DECORATIVE_ARMOUR_11901, 5000),
GOLD_DECORATIVE_HELM(ItemID.DECORATIVE_HELM_4511, 5000),
GOLD_DECORATIVE_BODY(ItemID.DECORATIVE_ARMOUR_4509, 5000),
GOLD_DECORATIVE_LEGS(ItemID.DECORATIVE_ARMOUR_4510, 5000),
GOLD_DECORATIVE_SKIRT(ItemID.DECORATIVE_ARMOUR_11895, 5000),
GOLD_DECORATIVE_SHIELD(ItemID.DECORATIVE_SHIELD_4512, 5000),
GOLD_DECORATIVE_SWORD(ItemID.DECORATIVE_SWORD_4508, 5000);
private final int itemID;
private final int repairPrice;
private static final ImmutableMap<Integer, Integer> REPAIR_MAP;
static
{
final ImmutableMap.Builder<Integer, Integer> map = new ImmutableMap.Builder<>();
for (final BrokenOnDeathItem p : values())
{
map.put(p.itemID, p.repairPrice);
}
REPAIR_MAP = map.build();
}
@Nullable
static Integer getRepairPrice(int itemId)
{
return REPAIR_MAP.get(itemId);
}
}

View File

@@ -26,7 +26,9 @@
package net.runelite.client.plugins.itemskeptondeath;
import com.google.common.annotations.VisibleForTesting;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
@@ -44,8 +46,8 @@ import net.runelite.api.Constants;
import net.runelite.api.FontID;
import net.runelite.api.InventoryID;
import net.runelite.api.Item;
import net.runelite.api.ItemDefinition;
import net.runelite.api.ItemContainer;
import net.runelite.api.ItemDefinition;
import net.runelite.api.ItemID;
import net.runelite.api.ScriptID;
import net.runelite.api.SkullIcon;
@@ -60,8 +62,10 @@ import net.runelite.api.widgets.WidgetType;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.ItemMapping;
import net.runelite.client.game.ItemReclaimCost;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.util.ColorUtil;
import net.runelite.client.util.QuantityFormatter;
@PluginDescriptor(
@@ -354,7 +358,7 @@ public class ItemsKeptOnDeathPlugin extends Plugin
if (!Pets.isPet(id)
&& !LostIfNotProtected.isLostIfNotProtected(id)
&& !isTradeable(itemManager.getItemDefinition(id)) && wildyLevel <= DEEP_WILDY
&& (wildyLevel <= 0 || BrokenOnDeathItem.getRepairPrice(i.getId()) != null))
&& (wildyLevel <= 0 || ItemReclaimCost.of(id) != null))
{
keptItems.add(new ItemStack(id, qty));
}
@@ -460,10 +464,10 @@ public class ItemsKeptOnDeathPlugin extends Plugin
}
// Jagex uses the repair price when determining which items are kept on death.
final Integer repairPrice = BrokenOnDeathItem.getRepairPrice(canonicalizedItemId);
final ItemReclaimCost repairPrice = ItemReclaimCost.of(canonicalizedItemId);
if (repairPrice != null)
{
exchangePrice = repairPrice;
exchangePrice = repairPrice.getValue();
}
if (exchangePrice == 0)
@@ -573,20 +577,51 @@ public class ItemsKeptOnDeathPlugin extends Plugin
textWidget.revalidate();
// Update Items lost total value
long total = 0;
long theyGet = 0;
long youLose = 0;
for (final Widget w : lostItems)
{
int cid = itemManager.canonicalize(w.getItemId());
final int cid = itemManager.canonicalize(w.getItemId());
final TrueItemValue trueItemValue = TrueItemValue.map(cid);
final Collection<Integer> mapping = ItemMapping.map(cid);
final int breakValue = itemManager.getRepairValue(cid);
if (breakValue != 0)
{
youLose -= breakValue;
theyGet += breakValue;
}
if (trueItemValue != null)
{
int truePrice = 0;
for (int id : trueItemValue.getDeconstructedItem())
{
if (mapping.contains(id))
{
continue;
}
truePrice += itemManager.getItemPrice(id);
}
youLose += truePrice;
}
int price = itemManager.getItemPrice(cid);
if (price == 0)
if (price == 0 && breakValue == 0)
{
// Default to alch price
price = (int) (itemManager.getItemDefinition(cid).getPrice() * Constants.HIGH_ALCHEMY_MULTIPLIER);
}
total += (long) price * w.getItemQuantity();
theyGet += (long) price * w.getItemQuantity();
}
final Widget lostValue = client.getWidget(WidgetInfo.ITEMS_LOST_VALUE);
lostValue.setText(QuantityFormatter.quantityToStackSize(total) + " gp");
lostValue.setText("They get: " + QuantityFormatter.quantityToStackSize(theyGet) +
"<br>You lose: " + ColorUtil.prependColorTag("(" + QuantityFormatter.quantityToStackSize(theyGet + youLose) + ")", Color.red));
// Update Max items kept
final Widget max = client.getWidget(WidgetInfo.ITEMS_KEPT_MAX);

View File

@@ -0,0 +1,45 @@
package net.runelite.client.plugins.itemskeptondeath;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import static net.runelite.api.ItemID.ABYSSAL_TENTACLE;
import static net.runelite.api.ItemID.ABYSSAL_WHIP;
import static net.runelite.api.ItemID.GRANITE_CLAMP;
import static net.runelite.api.ItemID.GRANITE_MAUL;
import static net.runelite.api.ItemID.GRANITE_MAUL_24225;
import static net.runelite.api.ItemID.GRANITE_MAUL_24227;
import static net.runelite.api.ItemID.KRAKEN_TENTACLE;
import static net.runelite.api.ItemID.ORNATE_MAUL_HANDLE;
@Getter
@RequiredArgsConstructor
public enum TrueItemValue
{
GRANITE_MAUL_HANDLE(GRANITE_MAUL_24225, ImmutableSet.of(ORNATE_MAUL_HANDLE, GRANITE_MAUL)),
GRANITE_MAUL_HANDLE_OR(GRANITE_MAUL_24227, ImmutableSet.of(ORNATE_MAUL_HANDLE, GRANITE_MAUL, GRANITE_CLAMP)),
TENTACLE_WHIP(ABYSSAL_TENTACLE, ImmutableSet.of(ABYSSAL_WHIP, KRAKEN_TENTACLE));
private static final ImmutableMap<Integer, TrueItemValue> TRUE_ITEM_VALUE_MAP;
private final int itemID;
private final Set<Integer> deconstructedItem;
static
{
ImmutableMap.Builder<Integer, TrueItemValue> map = ImmutableMap.builder();
for (TrueItemValue p : values())
{
map.put(p.getItemID(), p);
}
TRUE_ITEM_VALUE_MAP = map.build();
}
public static TrueItemValue map(int itemId)
{
return TRUE_ITEM_VALUE_MAP.getOrDefault(itemId, null);
}
}

View File

@@ -56,7 +56,6 @@ import net.runelite.client.util.OSType;
public class LoginScreenPlugin extends Plugin implements KeyListener
{
private static final int MAX_USERNAME_LENGTH = 254;
private static final int MAX_PASSWORD_LENGTH = 20;
private static final int MAX_PIN_LENGTH = 6;
@Inject
@@ -219,11 +218,6 @@ public class LoginScreenPlugin extends Plugin implements KeyListener
// Truncate data to maximum username length if necessary
client.setUsername(data.substring(0, Math.min(data.length(), MAX_USERNAME_LENGTH)));
}
else
{
// Truncate data to maximum password length if necessary
client.setPassword(data.substring(0, Math.min(data.length(), MAX_PASSWORD_LENGTH)));
}
break;
// Authenticator form

View File

@@ -30,17 +30,40 @@ public enum LootRecordSortType implements Comparator<LootTrackerRecord>
if (o1.getSubTitle().equals(LootRecordType.DEATH.name()))
{
return Arrays.stream(o1.getItems()).flatMapToInt(lootTrackerItem ->
IntStream.of((int) lootTrackerItem.getPrice() * lootTrackerItem.getQuantity())).sum();
IntStream.of((int) lootTrackerItem.getGePrice() * lootTrackerItem.getQuantity())).sum();
}
if (o2.getSubTitle().equals(LootRecordType.DEATH.name()))
{
return Arrays.stream(o1.getItems()).flatMapToInt(lootTrackerItem ->
IntStream.of((int) lootTrackerItem.getPrice() * lootTrackerItem.getQuantity())).sum();
IntStream.of((int) lootTrackerItem.getGePrice() * lootTrackerItem.getQuantity())).sum();
}
int sum = Arrays.stream(o1.getItems()).flatMapToInt(lootTrackerItem ->
IntStream.of((int) lootTrackerItem.getPrice() * lootTrackerItem.getQuantity())).sum();
IntStream.of((int) lootTrackerItem.getGePrice() * lootTrackerItem.getQuantity())).sum();
return sum + Arrays.stream(o2.getItems()).flatMapToInt(lootTrackerItem ->
IntStream.of((int) lootTrackerItem.getPrice() * lootTrackerItem.getQuantity())).sum();
IntStream.of((int) lootTrackerItem.getGePrice() * lootTrackerItem.getQuantity())).sum();
}
},
TOTAL_HA_VALUE
{
@Override
public int compare(LootTrackerRecord o1, LootTrackerRecord o2)
{
// We want deaths at the bottom of the list
if (o1.getSubTitle().equals(LootRecordType.DEATH.name()))
{
return Arrays.stream(o1.getItems()).flatMapToInt(lootTrackerItem ->
IntStream.of((int) lootTrackerItem.getHaPrice() * lootTrackerItem.getQuantity())).sum();
}
if (o2.getSubTitle().equals(LootRecordType.DEATH.name()))
{
return Arrays.stream(o1.getItems()).flatMapToInt(lootTrackerItem ->
IntStream.of((int) lootTrackerItem.getHaPrice() * lootTrackerItem.getQuantity())).sum();
}
int sum = Arrays.stream(o1.getItems()).flatMapToInt(lootTrackerItem ->
IntStream.of((int) lootTrackerItem.getHaPrice() * lootTrackerItem.getQuantity())).sum();
return sum + Arrays.stream(o2.getItems()).flatMapToInt(lootTrackerItem ->
IntStream.of((int) lootTrackerItem.getHaPrice() * lootTrackerItem.getQuantity())).sum();
}
},
ITEM_COUNT

View File

@@ -72,6 +72,8 @@ class LootTrackerBox extends JPanel
private final ItemManager itemManager;
@Getter(AccessLevel.PACKAGE)
private final String id;
private final LootTrackerPriceType priceType;
private final boolean showPriceType;
@Getter(AccessLevel.PACKAGE)
private final List<LootTrackerRecord> records = new ArrayList<>();
@@ -87,6 +89,8 @@ class LootTrackerBox extends JPanel
final String id,
@Nullable final String subtitle,
final boolean hideIgnoredItems,
final LootTrackerPriceType priceType,
final boolean showPriceType,
@Nullable final Boolean showDate,
final BiConsumer<String, Boolean> onItemToggle)
{
@@ -95,6 +99,8 @@ class LootTrackerBox extends JPanel
this.itemManager = itemManager;
this.onItemToggle = onItemToggle;
this.hideIgnoredItems = hideIgnoredItems;
this.priceType = priceType;
this.showPriceType = showPriceType;
setLayout(new BorderLayout(0, 1));
setBorder(new EmptyBorder(5, 0, 0, 0));
@@ -200,7 +206,13 @@ class LootTrackerBox extends JPanel
{
buildItems();
priceLabel.setText(QuantityFormatter.quantityToStackSize(totalPrice) + " gp");
String priceTypeString = " ";
if (showPriceType)
{
priceTypeString = priceType == LootTrackerPriceType.HIGH_ALCHEMY ? "HA: " : "GE: ";
}
priceLabel.setText(priceTypeString + QuantityFormatter.quantityToStackSize(totalPrice) + " gp");
priceLabel.setToolTipText(QuantityFormatter.formatNumber(totalPrice) + " gp");
final long kills = getTotalKills();
@@ -280,7 +292,7 @@ class LootTrackerBox extends JPanel
continue;
}
totalPrice += entry.getPrice();
totalPrice += priceType == LootTrackerPriceType.HIGH_ALCHEMY ? entry.getHaPrice() : entry.getGePrice();
int quantity = 0;
for (final LootTrackerItem i : items)
@@ -296,9 +308,10 @@ class LootTrackerBox extends JPanel
if (quantity != 0)
{
int newQuantity = entry.getQuantity() + quantity;
long pricePerItem = entry.getPrice() == 0 ? 0 : (entry.getPrice() / entry.getQuantity());
long gePricePerItem = entry.getGePrice() == 0 ? 0 : (entry.getGePrice() / entry.getQuantity());
long haPricePerItem = entry.getHaPrice() == 0 ? 0 : (entry.getHaPrice() / entry.getQuantity());
items.add(new LootTrackerItem(entry.getId(), entry.getName(), newQuantity, pricePerItem * newQuantity, entry.isIgnored()));
items.add(new LootTrackerItem(entry.getId(), entry.getName(), newQuantity, gePricePerItem * newQuantity, haPricePerItem * newQuantity, entry.isIgnored()));
}
else
{
@@ -306,7 +319,14 @@ class LootTrackerBox extends JPanel
}
}
items.sort((i1, i2) -> Long.compare(i2.getPrice(), i1.getPrice()));
if (priceType == LootTrackerPriceType.HIGH_ALCHEMY)
{
items.sort((i1, i2) -> Long.compare(i2.getHaPrice(), i1.getHaPrice()));
}
else
{
items.sort((i1, i2) -> Long.compare(i2.getGePrice(), i1.getGePrice()));
}
// Calculates how many rows need to be display to fit all items
final int rowSize = ((items.size() % ITEMS_PER_ROW == 0) ? 0 : 1) + items.size() / ITEMS_PER_ROW;
@@ -371,8 +391,11 @@ class LootTrackerBox extends JPanel
{
final String name = item.getName();
final int quantity = item.getQuantity();
final long price = item.getPrice();
final long gePrice = item.getGePrice();
final long haPrice = item.getHaPrice();
final String ignoredLabel = item.isIgnored() ? " - Ignored" : "";
return name + " x " + quantity + " (" + QuantityFormatter.quantityToStackSize(price) + ") " + ignoredLabel;
return "<html>" + name + " x " + quantity + ignoredLabel
+ "<br>GE: " + QuantityFormatter.quantityToStackSize(gePrice)
+ "<br>HA: " + QuantityFormatter.quantityToStackSize(haPrice) + "</html>";
}
}

View File

@@ -220,4 +220,23 @@ public interface LootTrackerConfig extends Config
return true;
}
@ConfigItem(
keyName = "priceType",
name = "Price Type",
description = "What type of price to use for calculating value."
)
default LootTrackerPriceType priceType()
{
return LootTrackerPriceType.GRAND_EXCHANGE;
}
@ConfigItem(
keyName = "showPriceType",
name = "Show Price Type",
description = "Whether to show a GE: or HA: next to the total values in the tracker"
)
default boolean showPriceType()
{
return false;
}
}

View File

@@ -39,7 +39,9 @@ class LootTrackerItem
@Getter(AccessLevel.PACKAGE)
private final int quantity;
@Getter(AccessLevel.PACKAGE)
private final long price;
private final long gePrice;
@Getter
private final long haPrice;
@Getter(AccessLevel.PACKAGE)
@Setter(AccessLevel.PACKAGE)
private boolean ignored;

View File

@@ -46,6 +46,7 @@ import javax.swing.ImageIcon;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.border.EmptyBorder;
@@ -90,6 +91,10 @@ class LootTrackerPanel extends PluginPanel
private static final String HTML_LABEL_TEMPLATE =
"<html><body style='color:%s'>%s<span style='color:white'>%s</span></body></html>";
private static final String SYNC_RESET_ALL_WARNING_TEXT =
"This will permanently delete the current loot from both the client and the RuneLite website.";
private static final String NO_SYNC_RESET_ALL_WARNING_TEXT =
"This will permanently delete the current loot from the client.";
// When there is no loot, display this
private final PluginErrorPanel errorPanel = new PluginErrorPanel();
@@ -385,6 +390,18 @@ class LootTrackerPanel extends PluginPanel
final JMenuItem reset = new JMenuItem("Reset All");
reset.addActionListener(e ->
{
final LootTrackerClient client = plugin.getLootTrackerClient();
final boolean syncLoot = client != null && config.syncPanel();
final int result = JOptionPane.showOptionDialog(overallPanel,
syncLoot ? SYNC_RESET_ALL_WARNING_TEXT : NO_SYNC_RESET_ALL_WARNING_TEXT,
"Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE,
null, new String[]{"Yes", "No"}, "No");
if (result != JOptionPane.YES_OPTION)
{
return;
}
// If not in detailed view, remove all, otherwise only remove for the currently detailed title
records.removeIf(r -> r.matches(currentView));
boxes.removeIf(b -> b.matches(currentView));
@@ -393,8 +410,7 @@ class LootTrackerPanel extends PluginPanel
logsContainer.repaint();
// Delete all loot, or loot matching the current view
LootTrackerClient client = plugin.getLootTrackerClient();
if (client != null && config.syncPanel())
if (syncLoot)
{
client.delete(currentView);
}
@@ -668,7 +684,7 @@ class LootTrackerPanel extends PluginPanel
// Create box
final LootTrackerBox box = new LootTrackerBox(record.getTimestamp().toEpochMilli(), itemManager, record.getTitle(), record.getSubTitle(),
hideIgnoredItems, config.displayDate(), plugin::toggleItem);
hideIgnoredItems, config.priceType(), config.showPriceType(), config.displayDate(), plugin::toggleItem);
box.combine(record);
// Create popup menu
@@ -764,7 +780,8 @@ class LootTrackerPanel extends PluginPanel
private void updateOverall()
{
long overallKills = 0;
long overallGp = 0;
long overallGe = 0;
long overallHa = 0;
for (LootTrackerRecord record : records)
{
@@ -808,7 +825,8 @@ class LootTrackerPanel extends PluginPanel
continue;
}
overallGp += item.getPrice();
overallGe += item.getGePrice();
overallHa += item.getHaPrice();
}
if (present > 0)
@@ -817,8 +835,16 @@ class LootTrackerPanel extends PluginPanel
}
}
String priceType = "";
if (config.showPriceType())
{
priceType = config.priceType() == LootTrackerPriceType.HIGH_ALCHEMY ? "HA " : "GE ";
}
overallKillsLabel.setText(htmlLabel("Total count: ", overallKills));
overallGpLabel.setText(htmlLabel("Total value: ", overallGp));
overallGpLabel.setText(htmlLabel("Total " + priceType + "value: ", config.priceType() == LootTrackerPriceType.HIGH_ALCHEMY ? overallHa : overallGe));
overallGpLabel.setToolTipText("<html>Total GE price: " + QuantityFormatter.formatNumber(overallGe)
+ "<br>Total HA price: " + QuantityFormatter.formatNumber(overallHa) + "</html>");
updateCollapseText();
}

View File

@@ -66,6 +66,7 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client;
import net.runelite.api.Constants;
import net.runelite.api.GameState;
import net.runelite.api.InventoryID;
import net.runelite.api.Item;
@@ -703,6 +704,10 @@ public class LootTrackerPlugin extends Plugin
eventType = "Kingdom of Miscellania";
container = client.getItemContainer(InventoryID.KINGDOM_OF_MISCELLANIA);
break;
case (WidgetID.FISHING_TRAWLER_REWARD_GROUP_ID):
eventType = "Fishing Trawler";
container = client.getItemContainer(InventoryID.FISHING_TRAWLER_REWARD);
break;
default:
return;
}
@@ -1209,15 +1214,18 @@ public class LootTrackerPlugin extends Plugin
{
final ItemDefinition itemDefinition = itemManager.getItemDefinition(itemId);
final int realItemId = itemDefinition.getNote() != -1 ? itemDefinition.getLinkedNoteId() : itemId;
final long price;
final long gePrice ;
final long haPrice ;
// If it's a death we want to get a coin value for untradeables lost
if (!itemDefinition.isTradeable() && quantity < 0)
{
price = (long) itemDefinition.getPrice() * (long) quantity;
gePrice = (long) itemDefinition.getPrice() * (long) quantity;
haPrice = (long) Math.round(itemDefinition.getPrice() * Constants.HIGH_ALCHEMY_MULTIPLIER) * (long) quantity;
}
else
{
price = (long) itemManager.getItemPrice(realItemId) * (long) quantity;
gePrice = (long) itemManager.getItemPrice(realItemId) * (long) quantity;
haPrice = (long) Math.round(itemManager.getItemPrice(realItemId) * Constants.HIGH_ALCHEMY_MULTIPLIER) * (long) quantity;
}
final boolean ignored = ignoredItems.contains(itemDefinition.getName());
@@ -1225,7 +1233,8 @@ public class LootTrackerPlugin extends Plugin
itemId,
itemDefinition.getName(),
quantity,
price,
gePrice,
haPrice,
ignored);
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2019 Hydrox6 <ikada@protonmail.ch>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.loottracker;
public enum LootTrackerPriceType
{
GRAND_EXCHANGE,
HIGH_ALCHEMY
}

View File

@@ -118,7 +118,7 @@ public class LootRecordWriter
{
totalBrackets++;
}
else if (line.contains("}"))
if (line.contains("}"))
{
totalBrackets--;
}

View File

@@ -578,7 +578,7 @@ public class MenuEntrySwapperPlugin extends Plugin
}
event.setMenuEntries(menu_entries.toArray(new MenuEntry[0]));
event.setModified(true);
event.setModified();
}
public void onMenuEntryAdded(MenuEntryAdded event)
@@ -601,8 +601,7 @@ public class MenuEntrySwapperPlugin extends Plugin
for (final String object : removedObjects)
{
if (target.equals(object)
|| hasArrow && target.endsWith(object)
|| targetLength > object.length() && target.startsWith(object))
|| hasArrow && target.endsWith(object))
{
client.setMenuOptionCount(client.getMenuOptionCount() - 1);
return;

View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.music;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup("music")
public interface MusicConfig extends Config
{
@ConfigItem(
keyName = "musicVolume",
name = "",
description = "",
hidden = true
)
default int getMusicVolume()
{
return 0;
}
@ConfigItem(
keyName = "musicVolume",
name = "",
description = "",
hidden = true
)
void setMusicVolume(int vol);
@ConfigItem(
keyName = "soundEffectVolume",
name = "",
description = "",
hidden = true
)
default int getSoundEffectVolume()
{
return 0;
}
@ConfigItem(
keyName = "soundEffectVolume",
name = "",
description = "",
hidden = true
)
void setSoundEffectVolume(int val);
@ConfigItem(
keyName = "areaSoundEffectVolume",
name = "",
description = "",
hidden = true
)
default int getAreaSoundEffectVolume()
{
return 0;
}
@ConfigItem(
keyName = "areaSoundEffectVolume",
name = "",
description = "",
hidden = true
)
void setAreaSoundEffectVolume(int vol);
@ConfigItem(
keyName = "muteOtherAreaSounds",
name = "Mute others' area sounds",
description = "Mute area sounds caused from other players"
)
default boolean muteOtherAreaSounds()
{
return false;
}
}

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2019, Anthony Chen <https://github.com/achencoms>
* Copyright (c) 2019, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -22,33 +23,45 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.musiclist;
package net.runelite.client.plugins.music;
import com.google.inject.Provides;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.function.BiConsumer;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.runelite.api.Actor;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Player;
import net.runelite.api.ScriptID;
import net.runelite.api.SoundEffectID;
import net.runelite.api.SpriteID;
import net.runelite.api.VarClientInt;
import net.runelite.api.VarPlayer;
import net.runelite.api.events.AreaSoundEffectPlayed;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.ScriptCallbackEvent;
import net.runelite.api.events.VarClientIntChanged;
import net.runelite.api.events.VolumeChanged;
import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.vars.InterfaceTab;
import net.runelite.api.widgets.JavaScriptCallback;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetConfig;
import net.runelite.api.widgets.WidgetID;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.api.widgets.WidgetPositionMode;
import net.runelite.api.widgets.WidgetType;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.game.chatbox.ChatboxPanelManager;
import net.runelite.client.game.chatbox.ChatboxTextInput;
@@ -56,11 +69,11 @@ import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
@PluginDescriptor(
name = "Music List",
description = "Adds search and filter for the music list"
name = "Music",
description = "Adds search and filter for the music list, and additional volume control",
tags = {"sound", "volume"}
)
@Singleton
public class MusicListPlugin extends Plugin
public class MusicPlugin extends Plugin
{
@Inject
private Client client;
@@ -68,6 +81,9 @@ public class MusicListPlugin extends Plugin
@Inject
private ClientThread clientThread;
@Inject
private MusicConfig musicConfig;
@Inject
private ChatboxPanelManager chatboxPanelManager;
@@ -88,14 +104,17 @@ public class MusicListPlugin extends Plugin
{
addSubscriptions();
clientThread.invoke(this::addMusicButtons);
clientThread.invoke(() ->
{
addMusicButtons();
applyMusicVolumeConfig();
updateMusicOptions();
});
}
@Override
protected void shutDown()
{
eventBus.unregister(this);
Widget header = client.getWidget(WidgetInfo.MUSIC_WINDOW);
if (header != null)
{
@@ -103,13 +122,25 @@ public class MusicListPlugin extends Plugin
}
tracks = null;
clientThread.invoke(this::teardownMusicOptions);
}
@Provides
MusicConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(MusicConfig.class);
}
private void addSubscriptions()
{
eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
eventBus.subscribe(WidgetLoaded.class, this, this::onWidgetLoaded);
eventBus.subscribe(VarClientIntChanged.class, this, this::onVarClientIntChanged);
eventBus.subscribe(VolumeChanged.class, this, this::onVolumeChanged);
eventBus.subscribe(ScriptCallbackEvent.class, this, this::onScriptCallbackEvent);
eventBus.subscribe(AreaSoundEffectPlayed.class, this, this::onAreaSoundEffectPlayed);
}
private void onGameStateChanged(GameStateChanged gameStateChanged)
@@ -132,6 +163,10 @@ public class MusicListPlugin extends Plugin
currentMusicFilter = MusicState.ALL;
addMusicButtons();
}
if (widgetLoaded.getGroupId() == WidgetID.OPTIONS_GROUP_ID)
{
updateMusicOptions();
}
}
private void addMusicButtons()
@@ -181,9 +216,46 @@ public class MusicListPlugin extends Plugin
}
}
private void onVolumeChanged(VolumeChanged volumeChanged)
{
applyMusicVolumeConfig();
}
private void onConfigChanged(ConfigChanged configChanged)
{
if (configChanged.getGroup().equals("music"))
{
clientThread.invokeLater(this::applyMusicVolumeConfig);
}
}
private void applyMusicVolumeConfig()
{
int musicVolume = musicConfig.getMusicVolume();
if (musicVolume > 0)
{
client.setMusicVolume(musicVolume - 1);
}
int soundEffectVolume = musicConfig.getSoundEffectVolume();
if (soundEffectVolume > 0)
{
client.setSoundEffectVolume(soundEffectVolume - 1);
}
int areaSoundEffectVolume = musicConfig.getAreaSoundEffectVolume();
if (areaSoundEffectVolume > 0)
{
client.setAreaSoundEffectVolume(areaSoundEffectVolume - 1);
}
updateMusicOptions();
}
private boolean isOnMusicTab()
{
return client.getVar(VarClientInt.INTERFACE_TAB) == InterfaceTab.MUSIC.getId();
return client.getVar(VarClientInt.INTERFACE_TAB) == 13;
}
private boolean isChatboxOpen()
@@ -304,4 +376,202 @@ public class MusicListPlugin extends Plugin
private final String name;
private final int spriteID;
}
}
@RequiredArgsConstructor
@Getter
private enum MusicSlider
{
MUSIC(WidgetInfo.OPTIONS_MUSIC_SLIDER, VarPlayer.MUSIC_VOLUME, MusicConfig::getMusicVolume, MusicConfig::setMusicVolume, 255),
AREA(WidgetInfo.OPTIONS_AREA_SOUND_SLIDER, VarPlayer.AREA_EFFECT_VOLUME, MusicConfig::getAreaSoundEffectVolume, MusicConfig::setAreaSoundEffectVolume, 127),
EFFECT(WidgetInfo.OPTIONS_SOUND_EFFECT_SLIDER, VarPlayer.SOUND_EFFECT_VOLUME, MusicConfig::getSoundEffectVolume, MusicConfig::setSoundEffectVolume, 127);
private final WidgetInfo widgetID;
private final VarPlayer var;
private final ToIntFunction<MusicConfig> getter;
private final BiConsumer<MusicConfig, Integer> setter;
private final int max;
@Setter
private Widget handle;
@Setter
private Widget track;
private static int PADDING = 8;
private int getX()
{
return getTrack().getRelativeX() + PADDING;
}
private int getWidth()
{
return getTrack().getWidth() - (PADDING * 2) - handle.getWidth();
}
}
private void teardownMusicOptions()
{
for (MusicSlider slider : MusicSlider.values())
{
Widget icon = client.getWidget(slider.getWidgetID());
if (icon == null)
{
return;
}
if (slider.getHandle() != null)
{
{
Widget handle = slider.getHandle();
Widget parent = handle.getParent();
if (parent == null)
{
continue;
}
else
{
Widget[] siblings = parent.getChildren();
if (siblings == null || handle.getIndex() >= siblings.length || siblings[handle.getIndex()] != handle)
{
continue;
}
siblings[slider.getTrack().getIndex()] = null;
siblings[handle.getIndex()] = null;
}
}
Object[] init = icon.getOnLoadListener();
init[1] = slider.getWidgetID().getId();
// Readd the var transmit triggers and rerun options_allsounds
client.runScript(init);
slider.setHandle(null);
slider.setTrack(null);
}
}
}
private void updateMusicOptions()
{
for (MusicSlider slider : MusicSlider.values())
{
Widget icon = client.getWidget(slider.getWidgetID());
if (icon == null)
{
return;
}
Widget handle = slider.getHandle();
if (handle != null)
{
Widget parent = handle.getParent();
if (parent == null)
{
handle = null;
}
else
{
Widget[] siblings = parent.getChildren();
if (siblings == null || handle.getIndex() >= siblings.length || siblings[handle.getIndex()] != handle)
{
handle = null;
}
}
}
if (handle == null)
{
Object[] init = icon.getOnLoadListener();
icon.setVarTransmitTrigger((int[]) null);
Widget track = icon.getParent().createChild(-1, WidgetType.TEXT);
slider.setTrack(track);
handle = icon.getParent().createChild(-1, WidgetType.GRAPHIC);
slider.setHandle(handle);
{
// First widget of the track
int wid = (Integer) init[2];
Widget w = client.getWidget(WidgetInfo.TO_GROUP(wid), WidgetInfo.TO_CHILD(wid));
track.setOriginalX(w.getRelativeX());
track.setOriginalY(w.getRelativeY());
}
{
// Last widget of the track
int wid = (Integer) init[6];
Widget w = client.getWidget(WidgetInfo.TO_GROUP(wid), WidgetInfo.TO_CHILD(wid));
track.setOriginalWidth((w.getRelativeX() + w.getWidth()) - track.getOriginalX());
}
track.setOriginalHeight(16);
track.setNoClickThrough(true);
track.revalidate();
handle.setSpriteId(SpriteID.OPTIONS_ZOOM_SLIDER_THUMB);
handle.setOriginalWidth(16);
handle.setOriginalHeight(16);
handle.setClickMask(WidgetConfig.DRAG);
JavaScriptCallback move = ev ->
{
int newVal = ((ev.getMouseX() - MusicSlider.PADDING - (slider.getHandle().getWidth() / 2)) * slider.getMax())
/ slider.getWidth();
if (newVal < 0)
{
newVal = 0;
}
if (newVal > slider.getMax())
{
newVal = slider.getMax();
}
// We store +1 so we can tell the difference between 0 and muted
slider.getSetter().accept(musicConfig, newVal + 1);
applyMusicVolumeConfig();
};
track.setOnClickListener(move);
track.setOnHoldListener(move);
track.setOnReleaseListener(move);
track.setHasListener(true);
client.runScript(ScriptID.OPTIONS_ALLSOUNDS, -1, init[2], init[3], init[4], init[5], init[6]);
}
int value = slider.getGetter().applyAsInt(musicConfig) - 1;
if (value <= -1)
{
// Use the vanilla value
value = ((4 - client.getVar(slider.getVar())) * slider.getMax()) / 4;
}
int newX = ((value * slider.getWidth()) / slider.getMax()) + slider.getX();
slider.getHandle().setOriginalX(newX);
slider.getHandle().setOriginalY(slider.getTrack().getOriginalY());
slider.getHandle().revalidate();
}
}
private void onScriptCallbackEvent(ScriptCallbackEvent ev)
{
switch (ev.getEventName())
{
case "optionsAllSounds":
// We have to override this script because it gets invoked periodically from the server
client.getIntStack()[client.getIntStackSize() - 1] = -1;
}
}
private void onAreaSoundEffectPlayed(AreaSoundEffectPlayed areaSoundEffectPlayed)
{
Actor source = areaSoundEffectPlayed.getSource();
if (source != client.getLocalPlayer()
&& source instanceof Player
&& musicConfig.muteOtherAreaSounds())
{
areaSoundEffectPlayed.consume();
}
}
}

View File

@@ -311,7 +311,7 @@ public class NpcIndicatorsPlugin extends Plugin
{
final String target = ColorUtil.prependColorTag(Text.removeTags(event.getTarget()), this.getHighlightColor);
event.setTarget(target);
event.setModified(true);
event.setModified();
}
else if (hotKeyPressed && type == MenuOpcode.EXAMINE_NPC.getId())
{

View File

@@ -161,15 +161,24 @@ public class NpcSceneOverlay extends Overlay
switch (plugin.getRenderStyle())
{
case SOUTH_WEST_TILE:
final LocalPoint lp1 = LocalPoint.fromWorld(client, actor.getWorldLocation());
Polygon tilePoly1 = null;
if (lp1 != null)
{
int size = 1;
NPCDefinition composition = actor.getTransformedDefinition();
if (composition != null)
{
tilePoly1 = Perspective.getCanvasTilePoly(client, lp1);
size = composition.getSize();
}
renderPoly(graphics, color, tilePoly1);
LocalPoint localPoint = actor.getLocalLocation();
int x = localPoint.getX() - ((size - 1) * Perspective.LOCAL_TILE_SIZE / 2);
int y = localPoint.getY() - ((size - 1) * Perspective.LOCAL_TILE_SIZE / 2);
Polygon tilePoly = Perspective.getCanvasTilePoly(client, new LocalPoint(x, y));
renderPoly(graphics, color, tilePoly);
break;
}
case TILE:
int size = 1;
NPCDefinition composition = actor.getTransformedDefinition();

Some files were not shown because too many files have changed in this diff Show More