develop
This commit is contained in:
@@ -42,7 +42,7 @@ import javax.inject.Singleton;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.BufferProvider;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Entity;
|
||||
import net.runelite.api.Renderable;
|
||||
import net.runelite.api.MainBufferProvider;
|
||||
import net.runelite.api.NullItemID;
|
||||
import net.runelite.api.RenderOverview;
|
||||
@@ -561,16 +561,16 @@ public class Hooks implements Callbacks
|
||||
}
|
||||
}
|
||||
|
||||
public static void renderDraw(Entity entity, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash)
|
||||
public static void renderDraw(Renderable renderable, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash)
|
||||
{
|
||||
DrawCallbacks drawCallbacks = client.getDrawCallbacks();
|
||||
if (drawCallbacks != null)
|
||||
{
|
||||
drawCallbacks.draw(entity, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash);
|
||||
drawCallbacks.draw(renderable, orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.draw(orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash);
|
||||
renderable.draw(orientation, pitchSin, pitchCos, yawSin, yawCos, x, y, z, hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.plugins.animsmoothing;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup(AnimationSmoothingPlugin.CONFIG_GROUP)
|
||||
public interface AnimationSmoothingConfig extends Config
|
||||
{
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "smoothPlayerAnimations",
|
||||
name = "Smooth Player Animations",
|
||||
description = "Configures whether the player animations are smooth or not",
|
||||
position = 1
|
||||
)
|
||||
default boolean smoothPlayerAnimations()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "smoothNpcAnimations",
|
||||
name = "Smooth NPC Animations",
|
||||
description = "Configures whether the npc animations are smooth or not",
|
||||
position = 2
|
||||
)
|
||||
default boolean smoothNpcAnimations()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "smoothObjectAnimations",
|
||||
name = "Smooth Object Animations",
|
||||
description = "Configures whether the object animations are smooth or not",
|
||||
position = 3
|
||||
)
|
||||
default boolean smoothObjectAnimations()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.plugins.animsmoothing;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Animation Smoothing",
|
||||
description = "Show smoother player, NPC, and object animations",
|
||||
tags = {"npcs", "objects", "players"},
|
||||
enabledByDefault = false
|
||||
)
|
||||
public class AnimationSmoothingPlugin extends Plugin
|
||||
{
|
||||
static final String CONFIG_GROUP = "animationSmoothing";
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private AnimationSmoothingConfig config;
|
||||
|
||||
@Provides
|
||||
AnimationSmoothingConfig getConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(AnimationSmoothingConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
client.setInterpolatePlayerAnimations(false);
|
||||
client.setInterpolateNpcAnimations(false);
|
||||
client.setInterpolateObjectAnimations(false);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals(CONFIG_GROUP))
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
private void update()
|
||||
{
|
||||
client.setInterpolatePlayerAnimations(config.smoothPlayerAnimations());
|
||||
client.setInterpolateNpcAnimations(config.smoothNpcAnimations());
|
||||
client.setInterpolateObjectAnimations(config.smoothObjectAnimations());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2018, DennisDeV <https://github.com/DevDennis>
|
||||
* 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.antidrag;
|
||||
|
||||
import net.runelite.api.Constants;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup(AntiDragPlugin.CONFIG_GROUP)
|
||||
public interface AntiDragConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "dragDelay",
|
||||
name = "Drag Delay",
|
||||
description = "Configures the inventory drag delay in client ticks (20ms)",
|
||||
position = 1
|
||||
)
|
||||
default int dragDelay()
|
||||
{
|
||||
return Constants.GAME_TICK_LENGTH / Constants.CLIENT_TICK_LENGTH; // one game tick
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "onShiftOnly",
|
||||
name = "On Shift Only",
|
||||
description = "Configures whether to only adjust the delay while holding shift in non-PvP scenarios. Shift is required in PvP regardless of this config setting",
|
||||
position = 2
|
||||
)
|
||||
default boolean onShiftOnly()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "disableOnCtrl",
|
||||
name = "Disable On Control Pressed",
|
||||
description = "Configures whether to ignore the delay while holding control.",
|
||||
position = 3
|
||||
)
|
||||
default boolean disableOnCtrl()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (c) 2018, DennisDeV <https://github.com/DevDennis>
|
||||
* 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.antidrag;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.event.KeyEvent;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.events.FocusChanged;
|
||||
import net.runelite.api.events.VarbitChanged;
|
||||
import net.runelite.api.events.WidgetLoaded;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.input.KeyListener;
|
||||
import net.runelite.client.input.KeyManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Anti Drag",
|
||||
description = "Prevent dragging an item for a specified delay",
|
||||
tags = {"antidrag", "delay", "inventory", "items"},
|
||||
enabledByDefault = false
|
||||
)
|
||||
public class AntiDragPlugin extends Plugin implements KeyListener
|
||||
{
|
||||
static final String CONFIG_GROUP = "antiDrag";
|
||||
|
||||
private static final int DEFAULT_DELAY = 5;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private AntiDragConfig config;
|
||||
|
||||
@Inject
|
||||
private KeyManager keyManager;
|
||||
|
||||
private boolean inPvp;
|
||||
private boolean shiftHeld;
|
||||
private boolean ctrlHeld;
|
||||
|
||||
@Provides
|
||||
AntiDragConfig getConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(AntiDragConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
if (client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
clientThread.invokeLater(() ->
|
||||
{
|
||||
inPvp = client.getVar(Varbits.PVP_SPEC_ORB) == 1;
|
||||
if (!config.onShiftOnly() && !inPvp)
|
||||
{
|
||||
setDragDelay();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
keyManager.registerKeyListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
clientThread.invoke(this::resetDragDelay);
|
||||
keyManager.unregisterKeyListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e)
|
||||
{
|
||||
if (e.getKeyCode() == KeyEvent.VK_CONTROL && config.disableOnCtrl() && !(inPvp || config.onShiftOnly()))
|
||||
{
|
||||
resetDragDelay();
|
||||
ctrlHeld = true;
|
||||
}
|
||||
else if (e.getKeyCode() == KeyEvent.VK_SHIFT && (inPvp || config.onShiftOnly()))
|
||||
{
|
||||
setDragDelay();
|
||||
shiftHeld = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e)
|
||||
{
|
||||
if (e.getKeyCode() == KeyEvent.VK_CONTROL && config.disableOnCtrl() && !(inPvp || config.onShiftOnly()))
|
||||
{
|
||||
setDragDelay();
|
||||
ctrlHeld = false;
|
||||
}
|
||||
else if (e.getKeyCode() == KeyEvent.VK_SHIFT && (inPvp || config.onShiftOnly()))
|
||||
{
|
||||
resetDragDelay();
|
||||
shiftHeld = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals(CONFIG_GROUP))
|
||||
{
|
||||
if (!config.disableOnCtrl())
|
||||
{
|
||||
ctrlHeld = false;
|
||||
}
|
||||
|
||||
if (config.onShiftOnly() || inPvp)
|
||||
{
|
||||
shiftHeld = false;
|
||||
clientThread.invoke(this::resetDragDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
clientThread.invoke(this::setDragDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onVarbitChanged(VarbitChanged varbitChanged)
|
||||
{
|
||||
boolean currentStatus = client.getVar(Varbits.PVP_SPEC_ORB) == 1;
|
||||
|
||||
if (currentStatus != inPvp)
|
||||
{
|
||||
inPvp = currentStatus;
|
||||
|
||||
if (!inPvp && !config.onShiftOnly())
|
||||
{
|
||||
setDragDelay();
|
||||
}
|
||||
else
|
||||
{
|
||||
resetDragDelay();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onFocusChanged(FocusChanged focusChanged)
|
||||
{
|
||||
if (!focusChanged.isFocused())
|
||||
{
|
||||
shiftHeld = false;
|
||||
ctrlHeld = false;
|
||||
clientThread.invoke(this::resetDragDelay);
|
||||
}
|
||||
else if (!inPvp && !config.onShiftOnly())
|
||||
{
|
||||
clientThread.invoke(this::setDragDelay);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onWidgetLoaded(WidgetLoaded widgetLoaded)
|
||||
{
|
||||
if ((widgetLoaded.getGroupId() == WidgetID.BANK_GROUP_ID ||
|
||||
widgetLoaded.getGroupId() == WidgetID.BANK_INVENTORY_GROUP_ID ||
|
||||
widgetLoaded.getGroupId() == WidgetID.DEPOSIT_BOX_GROUP_ID) && (!config.onShiftOnly() || shiftHeld) && !ctrlHeld)
|
||||
{
|
||||
setBankDragDelay(config.dragDelay());
|
||||
}
|
||||
}
|
||||
|
||||
private void setBankDragDelay(int delay)
|
||||
{
|
||||
final Widget bankItemContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
|
||||
final Widget bankInventoryItemsContainer = client.getWidget(WidgetInfo.BANK_INVENTORY_ITEMS_CONTAINER);
|
||||
final Widget bankDepositContainer = client.getWidget(WidgetInfo.DEPOSIT_BOX_INVENTORY_ITEMS_CONTAINER);
|
||||
if (bankItemContainer != null)
|
||||
{
|
||||
Widget[] items = bankItemContainer.getDynamicChildren();
|
||||
for (Widget item : items)
|
||||
{
|
||||
item.setDragDeadTime(delay);
|
||||
}
|
||||
}
|
||||
if (bankInventoryItemsContainer != null)
|
||||
{
|
||||
Widget[] items = bankInventoryItemsContainer.getDynamicChildren();
|
||||
for (Widget item : items)
|
||||
{
|
||||
item.setDragDeadTime(delay);
|
||||
}
|
||||
}
|
||||
if (bankDepositContainer != null)
|
||||
{
|
||||
Widget[] items = bankDepositContainer.getDynamicChildren();
|
||||
for (Widget item : items)
|
||||
{
|
||||
item.setDragDeadTime(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setDragDelay()
|
||||
{
|
||||
client.setInventoryDragDelay(config.dragDelay());
|
||||
setBankDragDelay(config.dragDelay());
|
||||
}
|
||||
|
||||
private void resetDragDelay()
|
||||
{
|
||||
client.setInventoryDragDelay(DEFAULT_DELAY);
|
||||
setBankDragDelay(DEFAULT_DELAY);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2017, honeyhoney <https://github.com/honeyhoney>
|
||||
* 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.attackstyles;
|
||||
|
||||
import net.runelite.api.Skill;
|
||||
|
||||
enum AttackStyle
|
||||
{
|
||||
ACCURATE("Accurate", Skill.ATTACK),
|
||||
AGGRESSIVE("Aggressive", Skill.STRENGTH),
|
||||
DEFENSIVE("Defensive", Skill.DEFENCE),
|
||||
CONTROLLED("Controlled", Skill.ATTACK, Skill.STRENGTH, Skill.DEFENCE),
|
||||
RANGING("Ranging", Skill.RANGED),
|
||||
LONGRANGE("Longrange", Skill.RANGED, Skill.DEFENCE),
|
||||
CASTING("Casting", Skill.MAGIC),
|
||||
DEFENSIVE_CASTING("Defensive Casting", Skill.MAGIC, Skill.DEFENCE),
|
||||
OTHER("Other");
|
||||
|
||||
private final String name;
|
||||
private final Skill[] skills;
|
||||
|
||||
AttackStyle(String name, Skill... skills)
|
||||
{
|
||||
this.name = name;
|
||||
this.skills = skills;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public Skill[] getSkills()
|
||||
{
|
||||
return skills;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2017, honeyhoney <https://github.com/honeyhoney>
|
||||
* 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.attackstyles;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("attackIndicator")
|
||||
public interface AttackStylesConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "alwaysShowStyle",
|
||||
name = "Always show style",
|
||||
description = "Show attack style indicator at all times",
|
||||
position = 1
|
||||
)
|
||||
default boolean alwaysShowStyle()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "warnForDefensive",
|
||||
name = "Warn for defence",
|
||||
description = "Show warning when a Defence skill combat option is selected",
|
||||
position = 2
|
||||
)
|
||||
default boolean warnForDefence()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "warnForAttack",
|
||||
name = "Warn for attack",
|
||||
description = "Show warning when an Attack skill combat option is selected",
|
||||
position = 3
|
||||
)
|
||||
default boolean warnForAttack()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "warnForStrength",
|
||||
name = "Warn for strength",
|
||||
description = "Show warning when a Strength skill combat option is selected",
|
||||
position = 4
|
||||
)
|
||||
default boolean warnForStrength()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "warnForRanged",
|
||||
name = "Warn for ranged",
|
||||
description = "Show warning when a Ranged skill combat option is selected",
|
||||
position = 5
|
||||
)
|
||||
default boolean warnForRanged()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "warnForMagic",
|
||||
name = "Warn for magic",
|
||||
description = "Show warning when a Magic skill combat option is selected",
|
||||
position = 6
|
||||
)
|
||||
default boolean warnForMagic()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "hideAutoRetaliate",
|
||||
name = "Hide auto retaliate",
|
||||
description = "Hide auto retaliate from the combat options tab",
|
||||
position = 7
|
||||
)
|
||||
default boolean hideAutoRetaliate()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "removeWarnedStyles",
|
||||
name = "Remove warned styles",
|
||||
description = "Remove warned styles from the combat options tab",
|
||||
position = 8
|
||||
)
|
||||
default boolean removeWarnedStyles()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2017, honeyhoney <https://github.com/honeyhoney>
|
||||
* 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.attackstyles;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.inject.Inject;
|
||||
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||
import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
|
||||
import net.runelite.client.ui.overlay.OverlayMenuEntry;
|
||||
import net.runelite.client.ui.overlay.OverlayPanel;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
class AttackStylesOverlay extends OverlayPanel
|
||||
{
|
||||
private final AttackStylesPlugin plugin;
|
||||
private final AttackStylesConfig config;
|
||||
|
||||
@Inject
|
||||
private AttackStylesOverlay(AttackStylesPlugin plugin, AttackStylesConfig config)
|
||||
{
|
||||
super(plugin);
|
||||
setPosition(OverlayPosition.ABOVE_CHATBOX_RIGHT);
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Attack style overlay"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
boolean warnedSkillSelected = plugin.isWarnedSkillSelected();
|
||||
|
||||
if (warnedSkillSelected || config.alwaysShowStyle())
|
||||
{
|
||||
final AttackStyle attackStyle = plugin.getAttackStyle();
|
||||
|
||||
if (attackStyle == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final String attackStyleString = attackStyle.getName();
|
||||
|
||||
panelComponent.getChildren().add(TitleComponent.builder()
|
||||
.text(attackStyleString)
|
||||
.color(warnedSkillSelected ? Color.RED : Color.WHITE)
|
||||
.build());
|
||||
|
||||
panelComponent.setPreferredSize(new Dimension(
|
||||
graphics.getFontMetrics().stringWidth(attackStyleString) + 10,
|
||||
0));
|
||||
|
||||
return super.render(graphics);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (c) 2017, honeyhoney <https://github.com/honeyhoney>
|
||||
* 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.attackstyles;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
import com.google.inject.Provides;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.ScriptID;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.VarPlayer;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.ScriptPostFired;
|
||||
import net.runelite.api.events.VarbitChanged;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.CASTING;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.DEFENSIVE_CASTING;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.OTHER;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Attack Styles",
|
||||
description = "Show your current attack style as an overlay",
|
||||
tags = {"combat", "defence", "magic", "overlay", "ranged", "strength", "warn", "pure"}
|
||||
)
|
||||
public class AttackStylesPlugin extends Plugin
|
||||
{
|
||||
private int attackStyleVarbit = -1;
|
||||
private int equippedWeaponTypeVarbit = -1;
|
||||
private int castingModeVarbit = -1;
|
||||
private AttackStyle attackStyle;
|
||||
private final Set<Skill> warnedSkills = new HashSet<>();
|
||||
private boolean warnedSkillSelected = false;
|
||||
private final Table<WeaponType, WidgetInfo, Boolean> widgetsToHide = HashBasedTable.create();
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private AttackStylesConfig config;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private AttackStylesOverlay overlay;
|
||||
|
||||
@Provides
|
||||
AttackStylesConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(AttackStylesConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(overlay);
|
||||
|
||||
if (client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
clientThread.invoke(this::start);
|
||||
}
|
||||
}
|
||||
|
||||
private void start()
|
||||
{
|
||||
resetWarnings();
|
||||
attackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE);
|
||||
equippedWeaponTypeVarbit = client.getVar(Varbits.EQUIPPED_WEAPON_TYPE);
|
||||
castingModeVarbit = client.getVar(Varbits.DEFENSIVE_CASTING_MODE);
|
||||
updateAttackStyle(
|
||||
equippedWeaponTypeVarbit,
|
||||
attackStyleVarbit,
|
||||
castingModeVarbit);
|
||||
updateWarning(false);
|
||||
processWidgets();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
overlayManager.remove(overlay);
|
||||
hideWarnedStyles(false);
|
||||
processWidgets();
|
||||
hideWidget(client.getWidget(WidgetInfo.COMBAT_AUTO_RETALIATE), false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AttackStyle getAttackStyle()
|
||||
{
|
||||
return attackStyle;
|
||||
}
|
||||
|
||||
public boolean isWarnedSkillSelected()
|
||||
{
|
||||
return warnedSkillSelected;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onScriptPostFired(ScriptPostFired scriptPostFired)
|
||||
{
|
||||
if (scriptPostFired.getScriptId() == ScriptID.COMBAT_INTERFACE_SETUP)
|
||||
{
|
||||
processWidgets();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide or unhide widgets depending on widgetsToHide
|
||||
*/
|
||||
private void processWidgets()
|
||||
{
|
||||
WeaponType equippedWeaponType = WeaponType.getWeaponType(equippedWeaponTypeVarbit);
|
||||
|
||||
if (widgetsToHide.containsRow(equippedWeaponType))
|
||||
{
|
||||
for (WidgetInfo widgetKey : widgetsToHide.row(equippedWeaponType).keySet())
|
||||
{
|
||||
hideWidget(client.getWidget(widgetKey), widgetsToHide.get(equippedWeaponType, widgetKey));
|
||||
}
|
||||
}
|
||||
hideWidget(client.getWidget(WidgetInfo.COMBAT_AUTO_RETALIATE), config.hideAutoRetaliate());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
if (event.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
resetWarnings();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onVarbitChanged(VarbitChanged event)
|
||||
{
|
||||
int currentAttackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE);
|
||||
int currentEquippedWeaponTypeVarbit = client.getVar(Varbits.EQUIPPED_WEAPON_TYPE);
|
||||
int currentCastingModeVarbit = client.getVar(Varbits.DEFENSIVE_CASTING_MODE);
|
||||
|
||||
if (attackStyleVarbit != currentAttackStyleVarbit || equippedWeaponTypeVarbit != currentEquippedWeaponTypeVarbit || castingModeVarbit != currentCastingModeVarbit)
|
||||
{
|
||||
boolean weaponSwitch = currentEquippedWeaponTypeVarbit != equippedWeaponTypeVarbit;
|
||||
|
||||
attackStyleVarbit = currentAttackStyleVarbit;
|
||||
equippedWeaponTypeVarbit = currentEquippedWeaponTypeVarbit;
|
||||
castingModeVarbit = currentCastingModeVarbit;
|
||||
|
||||
updateAttackStyle(equippedWeaponTypeVarbit, attackStyleVarbit,
|
||||
castingModeVarbit);
|
||||
updateWarning(weaponSwitch);
|
||||
|
||||
// this isn't required, but will hide styles 1 tick earlier than the script event, which fires
|
||||
// 1 tick after the combat options is unhidden
|
||||
if (weaponSwitch)
|
||||
{
|
||||
processWidgets();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals("attackIndicator"))
|
||||
{
|
||||
boolean enabled = Boolean.TRUE.toString().equals(event.getNewValue());
|
||||
switch (event.getKey())
|
||||
{
|
||||
case "warnForDefensive":
|
||||
updateWarnedSkills(enabled, Skill.DEFENCE);
|
||||
break;
|
||||
case "warnForAttack":
|
||||
updateWarnedSkills(enabled, Skill.ATTACK);
|
||||
break;
|
||||
case "warnForStrength":
|
||||
updateWarnedSkills(enabled, Skill.STRENGTH);
|
||||
break;
|
||||
case "warnForRanged":
|
||||
updateWarnedSkills(enabled, Skill.RANGED);
|
||||
break;
|
||||
case "warnForMagic":
|
||||
updateWarnedSkills(enabled, Skill.MAGIC);
|
||||
break;
|
||||
case "removeWarnedStyles":
|
||||
hideWarnedStyles(enabled);
|
||||
break;
|
||||
}
|
||||
processWidgets();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetWarnings()
|
||||
{
|
||||
updateWarnedSkills(config.warnForAttack(), Skill.ATTACK);
|
||||
updateWarnedSkills(config.warnForStrength(), Skill.STRENGTH);
|
||||
updateWarnedSkills(config.warnForDefence(), Skill.DEFENCE);
|
||||
updateWarnedSkills(config.warnForRanged(), Skill.RANGED);
|
||||
updateWarnedSkills(config.warnForMagic(), Skill.MAGIC);
|
||||
}
|
||||
|
||||
private void updateAttackStyle(int equippedWeaponType, int attackStyleIndex, int castingMode)
|
||||
{
|
||||
AttackStyle[] attackStyles = WeaponType.getWeaponType(equippedWeaponType).getAttackStyles();
|
||||
if (attackStyleIndex < attackStyles.length)
|
||||
{
|
||||
attackStyle = attackStyles[attackStyleIndex];
|
||||
if (attackStyle == null)
|
||||
{
|
||||
attackStyle = OTHER;
|
||||
}
|
||||
else if ((attackStyle == CASTING) && (castingMode == 1))
|
||||
{
|
||||
attackStyle = DEFENSIVE_CASTING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateWarnedSkills(boolean enabled, Skill skill)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
warnedSkills.add(skill);
|
||||
}
|
||||
else
|
||||
{
|
||||
warnedSkills.remove(skill);
|
||||
}
|
||||
updateWarning(false);
|
||||
}
|
||||
|
||||
private void updateWarning(boolean weaponSwitch)
|
||||
{
|
||||
warnedSkillSelected = false;
|
||||
if (attackStyle != null)
|
||||
{
|
||||
for (Skill skill : attackStyle.getSkills())
|
||||
{
|
||||
if (warnedSkills.contains(skill))
|
||||
{
|
||||
if (weaponSwitch)
|
||||
{ // NOPMD EmptyIfStmt
|
||||
// TODO : chat message to warn players that their weapon switch also caused an unwanted attack style change
|
||||
}
|
||||
warnedSkillSelected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
hideWarnedStyles(config.removeWarnedStyles());
|
||||
}
|
||||
|
||||
private void hideWarnedStyles(boolean enabled)
|
||||
{
|
||||
WeaponType equippedWeaponType = WeaponType.getWeaponType(equippedWeaponTypeVarbit);
|
||||
if (equippedWeaponType == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AttackStyle[] attackStyles = equippedWeaponType.getAttackStyles();
|
||||
|
||||
// Iterate over attack styles
|
||||
for (int i = 0; i < attackStyles.length; i++)
|
||||
{
|
||||
AttackStyle attackStyle = attackStyles[i];
|
||||
if (attackStyle == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean warnedSkill = false;
|
||||
for (Skill skill : attackStyle.getSkills())
|
||||
{
|
||||
if (warnedSkills.contains(skill))
|
||||
{
|
||||
warnedSkill = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Magic staves defensive casting mode
|
||||
if (attackStyle == DEFENSIVE_CASTING || !enabled)
|
||||
{
|
||||
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_BOX, enabled && warnedSkill);
|
||||
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_ICON, enabled && warnedSkill);
|
||||
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_SHIELD, enabled && warnedSkill);
|
||||
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_DEFENSIVE_SPELL_TEXT, enabled && warnedSkill);
|
||||
}
|
||||
|
||||
// Remove appropriate combat option
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_ONE, enabled && warnedSkill);
|
||||
break;
|
||||
case 1:
|
||||
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_TWO, enabled && warnedSkill);
|
||||
break;
|
||||
case 2:
|
||||
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_THREE, enabled && warnedSkill);
|
||||
break;
|
||||
case 3:
|
||||
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_STYLE_FOUR, enabled && warnedSkill);
|
||||
break;
|
||||
case 4:
|
||||
widgetsToHide.put(equippedWeaponType, WidgetInfo.COMBAT_SPELLS, enabled && warnedSkill);
|
||||
break;
|
||||
default:
|
||||
// 5 can be defensive casting
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void hideWidget(Widget widget, boolean hidden)
|
||||
{
|
||||
if (widget != null)
|
||||
{
|
||||
widget.setHidden(hidden);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Set<Skill> getWarnedSkills()
|
||||
{
|
||||
return warnedSkills;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Table<WeaponType, WidgetInfo, Boolean> getHiddenWidgets()
|
||||
{
|
||||
return widgetsToHide;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2017, honeyhoney <https://github.com/honeyhoney>
|
||||
* 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.attackstyles;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.Map;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.ACCURATE;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.AGGRESSIVE;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.CASTING;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.CONTROLLED;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.DEFENSIVE;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.DEFENSIVE_CASTING;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.LONGRANGE;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.OTHER;
|
||||
import static net.runelite.client.plugins.attackstyles.AttackStyle.RANGING;
|
||||
|
||||
enum WeaponType
|
||||
{
|
||||
TYPE_0(ACCURATE, AGGRESSIVE, null, DEFENSIVE),
|
||||
TYPE_1(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
|
||||
TYPE_2(ACCURATE, AGGRESSIVE, null, DEFENSIVE),
|
||||
TYPE_3(RANGING, RANGING, null, LONGRANGE),
|
||||
TYPE_4(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
|
||||
TYPE_5(RANGING, RANGING, null, LONGRANGE),
|
||||
TYPE_6(AGGRESSIVE, RANGING, CASTING, null),
|
||||
TYPE_7(RANGING, RANGING, null, LONGRANGE),
|
||||
TYPE_8(OTHER, AGGRESSIVE, null, null),
|
||||
TYPE_9(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
|
||||
TYPE_10(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
|
||||
TYPE_11(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
|
||||
TYPE_12(CONTROLLED, AGGRESSIVE, null, DEFENSIVE),
|
||||
TYPE_13(ACCURATE, AGGRESSIVE, null, DEFENSIVE),
|
||||
TYPE_14(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
|
||||
TYPE_15(CONTROLLED, CONTROLLED, CONTROLLED, DEFENSIVE),
|
||||
TYPE_16(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
|
||||
TYPE_17(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
|
||||
TYPE_18(ACCURATE, AGGRESSIVE, null, DEFENSIVE, CASTING, DEFENSIVE_CASTING),
|
||||
TYPE_19(RANGING, RANGING, null, LONGRANGE),
|
||||
TYPE_20(ACCURATE, CONTROLLED, null, DEFENSIVE),
|
||||
TYPE_21(ACCURATE, AGGRESSIVE, null, DEFENSIVE, CASTING, DEFENSIVE_CASTING),
|
||||
TYPE_22(ACCURATE, AGGRESSIVE, AGGRESSIVE, DEFENSIVE),
|
||||
TYPE_23(CASTING, CASTING, null, DEFENSIVE_CASTING),
|
||||
TYPE_24(ACCURATE, AGGRESSIVE, CONTROLLED, DEFENSIVE),
|
||||
TYPE_25(CONTROLLED, AGGRESSIVE, null, DEFENSIVE),
|
||||
TYPE_26(AGGRESSIVE, AGGRESSIVE, null, AGGRESSIVE),
|
||||
TYPE_27(ACCURATE, null, null, OTHER);
|
||||
|
||||
private final AttackStyle[] attackStyles;
|
||||
|
||||
private static final Map<Integer, WeaponType> weaponTypes;
|
||||
|
||||
static
|
||||
{
|
||||
ImmutableMap.Builder<Integer, WeaponType> builder = new ImmutableMap.Builder<>();
|
||||
|
||||
for (WeaponType weaponType : values())
|
||||
{
|
||||
builder.put(weaponType.ordinal(), weaponType);
|
||||
}
|
||||
|
||||
weaponTypes = builder.build();
|
||||
}
|
||||
|
||||
WeaponType(AttackStyle... attackStyles)
|
||||
{
|
||||
this.attackStyles = attackStyles;
|
||||
}
|
||||
|
||||
public AttackStyle[] getAttackStyles()
|
||||
{
|
||||
return attackStyles;
|
||||
}
|
||||
|
||||
public static WeaponType getWeaponType(int id)
|
||||
{
|
||||
return weaponTypes.get(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2018, TheLonelyDev <https://github.com/TheLonelyDev>
|
||||
* Copyright (c) 2018, Jeremy Plsek <https://github.com/jplsek>
|
||||
* 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.bank;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.Keybind;
|
||||
|
||||
@ConfigGroup("bank")
|
||||
public interface BankConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "showGE",
|
||||
name = "Show Grand Exchange price",
|
||||
description = "Show grand exchange price total (GE)",
|
||||
position = 1
|
||||
)
|
||||
default boolean showGE()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showHA",
|
||||
name = "Show high alchemy price",
|
||||
description = "Show high alchemy price total (HA)",
|
||||
position = 2
|
||||
)
|
||||
default boolean showHA()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showExact",
|
||||
name = "Show exact bank value",
|
||||
description = "Show exact bank value",
|
||||
position = 3
|
||||
)
|
||||
default boolean showExact()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "rightClickBankInventory",
|
||||
name = "Disable left click bank inventory",
|
||||
description = "Configures whether the bank inventory button will bank your inventory on left click",
|
||||
position = 4
|
||||
)
|
||||
default boolean rightClickBankInventory()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "rightClickBankEquip",
|
||||
name = "Disable left click bank equipment",
|
||||
description = "Configures whether the bank equipment button will bank your equipment on left click",
|
||||
position = 5
|
||||
)
|
||||
default boolean rightClickBankEquip()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "rightClickBankLoot",
|
||||
name = "Disable left click bank looting bag",
|
||||
description = "Configures whether the bank looting bag button will bank your looting bag contents on left click",
|
||||
position = 6
|
||||
)
|
||||
default boolean rightClickBankLoot()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "seedVaultValue",
|
||||
name = "Show seed vault value",
|
||||
description = "Adds the total value of all seeds inside the seed vault to the title",
|
||||
position = 7
|
||||
)
|
||||
default boolean seedVaultValue()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "bankPinKeyboard",
|
||||
name = "Keyboard Bankpin",
|
||||
description = "Allows using the keyboard keys for bank pin input",
|
||||
position = 8
|
||||
)
|
||||
default boolean bankPinKeyboard()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "searchKeybind",
|
||||
name = "Search Shortcut",
|
||||
description = "Keyboard shortcut for initiating a bank search",
|
||||
position = 9
|
||||
)
|
||||
default Keybind searchKeybind()
|
||||
{
|
||||
return new Keybind(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,546 @@
|
||||
/*
|
||||
* Copyright (c) 2018, TheLonelyDev <https://github.com/TheLonelyDev>
|
||||
* Copyright (c) 2018, Jeremy Plsek <https://github.com/jplsek>
|
||||
* 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.bank;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.text.ParseException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemComposition;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.ScriptID;
|
||||
import net.runelite.api.VarClientStr;
|
||||
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.ScriptPostFired;
|
||||
import net.runelite.api.events.WidgetLoaded;
|
||||
import net.runelite.api.widgets.JavaScriptCallback;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.config.Keybind;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.input.KeyListener;
|
||||
import net.runelite.client.input.KeyManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.util.QuantityFormatter;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Bank",
|
||||
description = "Modifications to the banking interface",
|
||||
tags = {"grand", "exchange", "high", "alchemy", "prices", "deposit"}
|
||||
)
|
||||
@Slf4j
|
||||
public class BankPlugin extends Plugin
|
||||
{
|
||||
private static final String DEPOSIT_WORN = "Deposit worn items";
|
||||
private static final String DEPOSIT_INVENTORY = "Deposit inventory";
|
||||
private static final String DEPOSIT_LOOT = "Deposit loot";
|
||||
private static final String SEED_VAULT_TITLE = "Seed Vault";
|
||||
|
||||
private static final String NUMBER_REGEX = "[0-9]+(\\.[0-9]+)?[kmb]?";
|
||||
private static final Pattern VALUE_SEARCH_PATTERN = Pattern.compile("^(?<mode>ge|ha|alch)?" +
|
||||
" *(((?<op>[<>=]|>=|<=) *(?<num>" + NUMBER_REGEX + "))|" +
|
||||
"((?<num1>" + NUMBER_REGEX + ") *- *(?<num2>" + NUMBER_REGEX + ")))$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
|
||||
@Inject
|
||||
private BankConfig config;
|
||||
|
||||
@Inject
|
||||
private BankSearch bankSearch;
|
||||
|
||||
@Inject
|
||||
private KeyManager keyManager;
|
||||
|
||||
private boolean forceRightClickFlag;
|
||||
private Multiset<Integer> itemQuantities; // bank item quantities for bank value search
|
||||
private String searchString;
|
||||
|
||||
private final KeyListener searchHotkeyListener = new KeyListener()
|
||||
{
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e)
|
||||
{
|
||||
Keybind keybind = config.searchKeybind();
|
||||
if (keybind.matches(e))
|
||||
{
|
||||
Widget bankContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
|
||||
if (bankContainer == null || bankContainer.isSelfHidden())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Search hotkey pressed");
|
||||
|
||||
bankSearch.initSearch();
|
||||
e.consume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@Provides
|
||||
BankConfig getConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(BankConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp()
|
||||
{
|
||||
keyManager.registerKeyListener(searchHotkeyListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
keyManager.unregisterKeyListener(searchHotkeyListener);
|
||||
clientThread.invokeLater(() -> bankSearch.reset(false));
|
||||
forceRightClickFlag = false;
|
||||
itemQuantities = null;
|
||||
searchString = null;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMenuShouldLeftClick(MenuShouldLeftClick event)
|
||||
{
|
||||
if (!forceRightClickFlag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
forceRightClickFlag = false;
|
||||
MenuEntry[] menuEntries = client.getMenuEntries();
|
||||
for (MenuEntry entry : menuEntries)
|
||||
{
|
||||
if ((entry.getOption().equals(DEPOSIT_WORN) && config.rightClickBankEquip())
|
||||
|| (entry.getOption().equals(DEPOSIT_INVENTORY) && config.rightClickBankInventory())
|
||||
|| (entry.getOption().equals(DEPOSIT_LOOT) && config.rightClickBankLoot()))
|
||||
{
|
||||
event.setForceRightClick(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMenuEntryAdded(MenuEntryAdded event)
|
||||
{
|
||||
if ((event.getOption().equals(DEPOSIT_WORN) && config.rightClickBankEquip())
|
||||
|| (event.getOption().equals(DEPOSIT_INVENTORY) && config.rightClickBankInventory())
|
||||
|| (event.getOption().equals(DEPOSIT_LOOT) && config.rightClickBankLoot()))
|
||||
{
|
||||
forceRightClickFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onScriptCallbackEvent(ScriptCallbackEvent event)
|
||||
{
|
||||
int[] intStack = client.getIntStack();
|
||||
String[] stringStack = client.getStringStack();
|
||||
int intStackSize = client.getIntStackSize();
|
||||
int stringStackSize = client.getStringStackSize();
|
||||
|
||||
switch (event.getEventName())
|
||||
{
|
||||
case "bankSearchFilter":
|
||||
int itemId = intStack[intStackSize - 1];
|
||||
String search = stringStack[stringStackSize - 1];
|
||||
|
||||
if (valueSearch(itemId, search))
|
||||
{
|
||||
// return true
|
||||
intStack[intStackSize - 2] = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
case "bankpinButtonSetup":
|
||||
{
|
||||
if (!config.bankPinKeyboard())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final int compId = intStack[intStackSize - 2];
|
||||
final int buttonId = intStack[intStackSize - 1];
|
||||
//TODO Implement client.getWidget(compId)
|
||||
Widget button = null;
|
||||
Widget buttonRect = button.getChild(0);
|
||||
|
||||
final Object[] onOpListener = buttonRect.getOnOpListener();
|
||||
buttonRect.setOnKeyListener((JavaScriptCallback) e ->
|
||||
{
|
||||
int typedChar = e.getTypedKeyChar() - '0';
|
||||
if (typedChar != buttonId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("Bank pin keypress");
|
||||
|
||||
final String input = client.getVar(VarClientStr.CHATBOX_TYPED_TEXT);
|
||||
clientThread.invokeLater(() ->
|
||||
{
|
||||
// reset chatbox input to avoid pin going to chatbox..
|
||||
client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, input);
|
||||
client.runScript(ScriptID.CHAT_PROMPT_INIT);
|
||||
|
||||
client.runScript(onOpListener);
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onWidgetLoaded(WidgetLoaded event)
|
||||
{
|
||||
if (event.getGroupId() != WidgetID.SEED_VAULT_GROUP_ID || !config.seedVaultValue())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
updateSeedVaultTotal();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onScriptPostFired(ScriptPostFired event)
|
||||
{
|
||||
if (event.getScriptId() == ScriptID.BANKMAIN_BUILD)
|
||||
{
|
||||
// Compute bank prices using only the shown items so that we can show bank value during searches
|
||||
final Widget bankItemContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
|
||||
final ItemContainer bankContainer = client.getItemContainer(InventoryID.BANK);
|
||||
final Widget[] children = bankItemContainer.getChildren();
|
||||
long geTotal = 0, haTotal = 0;
|
||||
|
||||
if (children != null)
|
||||
{
|
||||
log.debug("Computing bank price of {} items", bankContainer.size());
|
||||
|
||||
// The first components are the bank items, followed by tabs etc. There are always 816 components regardless
|
||||
// of bank size, but we only need to check up to the bank size.
|
||||
for (int i = 0; i < bankContainer.size(); ++i)
|
||||
{
|
||||
Widget child = children[i];
|
||||
if (child != null && !child.isSelfHidden() && child.getItemId() > -1)
|
||||
{
|
||||
final int alchPrice = getHaPrice(child.getItemId());
|
||||
geTotal += (long) itemManager.getItemPrice(child.getItemId()) * child.getItemQuantity();
|
||||
haTotal += (long) alchPrice * child.getItemQuantity();
|
||||
}
|
||||
}
|
||||
|
||||
Widget bankTitle = client.getWidget(WidgetInfo.BANK_TITLE_BAR);
|
||||
bankTitle.setText(bankTitle.getText() + createValueText(geTotal, haTotal));
|
||||
}
|
||||
}
|
||||
else if (event.getScriptId() == ScriptID.BANKMAIN_SEARCH_REFRESH)
|
||||
{
|
||||
// vanilla only lays out the bank every 40 client ticks, so if the search input has changed,
|
||||
// and the bank wasn't laid out this tick, lay it out early
|
||||
final String inputText = client.getVar(VarClientStr.INPUT_TEXT);
|
||||
if (searchString != inputText && client.getGameCycle() % 40 != 0)
|
||||
{
|
||||
clientThread.invokeLater(bankSearch::layoutBank);
|
||||
searchString = inputText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onItemContainerChanged(ItemContainerChanged event)
|
||||
{
|
||||
int containerId = event.getContainerId();
|
||||
|
||||
if (containerId == InventoryID.BANK.getId())
|
||||
{
|
||||
itemQuantities = null;
|
||||
}
|
||||
else if (containerId == InventoryID.SEED_VAULT.getId() && config.seedVaultValue())
|
||||
{
|
||||
updateSeedVaultTotal();
|
||||
}
|
||||
}
|
||||
|
||||
private String createValueText(long gePrice, long haPrice)
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (config.showGE() && gePrice != 0)
|
||||
{
|
||||
stringBuilder.append(" (");
|
||||
|
||||
if (config.showHA())
|
||||
{
|
||||
stringBuilder.append("GE: ");
|
||||
}
|
||||
|
||||
if (config.showExact())
|
||||
{
|
||||
stringBuilder.append(QuantityFormatter.formatNumber(gePrice));
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.append(QuantityFormatter.quantityToStackSize(gePrice));
|
||||
}
|
||||
stringBuilder.append(')');
|
||||
}
|
||||
|
||||
if (config.showHA() && haPrice != 0)
|
||||
{
|
||||
stringBuilder.append(" (");
|
||||
|
||||
if (config.showGE())
|
||||
{
|
||||
stringBuilder.append("HA: ");
|
||||
}
|
||||
|
||||
if (config.showExact())
|
||||
{
|
||||
stringBuilder.append(QuantityFormatter.formatNumber(haPrice));
|
||||
}
|
||||
else
|
||||
{
|
||||
stringBuilder.append(QuantityFormatter.quantityToStackSize(haPrice));
|
||||
}
|
||||
stringBuilder.append(')');
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private void updateSeedVaultTotal()
|
||||
{
|
||||
final Widget titleContainer = client.getWidget(WidgetInfo.SEED_VAULT_TITLE_CONTAINER);
|
||||
if (titleContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final Widget title = titleContainer.getChild(1);
|
||||
if (title == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final ContainerPrices prices = calculate(getSeedVaultItems());
|
||||
if (prices == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final String titleText = createValueText(prices.getGePrice(), prices.getHighAlchPrice());
|
||||
title.setText(SEED_VAULT_TITLE + titleText);
|
||||
}
|
||||
|
||||
private Item[] getSeedVaultItems()
|
||||
{
|
||||
final ItemContainer itemContainer = client.getItemContainer(InventoryID.SEED_VAULT);
|
||||
if (itemContainer == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return itemContainer.getItems();
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
boolean valueSearch(final int itemId, final String str)
|
||||
{
|
||||
final Matcher matcher = VALUE_SEARCH_PATTERN.matcher(str);
|
||||
if (!matcher.matches())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Count bank items and remember it for determining item quantity
|
||||
if (itemQuantities == null)
|
||||
{
|
||||
itemQuantities = getBankItemSet();
|
||||
}
|
||||
|
||||
final ItemComposition itemComposition = itemManager.getItemComposition(itemId);
|
||||
final int qty = itemQuantities.count(itemId);
|
||||
final long gePrice = (long) itemManager.getItemPrice(itemId) * qty;
|
||||
final long haPrice = (long) itemComposition.getHaPrice() * qty;
|
||||
|
||||
long value = Math.max(gePrice, haPrice);
|
||||
|
||||
final String mode = matcher.group("mode");
|
||||
if (mode != null)
|
||||
{
|
||||
value = mode.toLowerCase().equals("ge") ? gePrice : haPrice;
|
||||
}
|
||||
|
||||
final String op = matcher.group("op");
|
||||
if (op != null)
|
||||
{
|
||||
long compare;
|
||||
try
|
||||
{
|
||||
compare = QuantityFormatter.parseQuantity(matcher.group("num"));
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case ">":
|
||||
return value > compare;
|
||||
case "<":
|
||||
return value < compare;
|
||||
case "=":
|
||||
return value == compare;
|
||||
case ">=":
|
||||
return value >= compare;
|
||||
case "<=":
|
||||
return value <= compare;
|
||||
}
|
||||
}
|
||||
|
||||
final String num1 = matcher.group("num1");
|
||||
final String num2 = matcher.group("num2");
|
||||
if (num1 != null && num2 != null)
|
||||
{
|
||||
long compare1, compare2;
|
||||
try
|
||||
{
|
||||
compare1 = QuantityFormatter.parseQuantity(num1);
|
||||
compare2 = QuantityFormatter.parseQuantity(num2);
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return compare1 <= value && compare2 >= value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Multiset<Integer> getBankItemSet()
|
||||
{
|
||||
ItemContainer itemContainer = client.getItemContainer(InventoryID.BANK);
|
||||
if (itemContainer == null)
|
||||
{
|
||||
return HashMultiset.create();
|
||||
}
|
||||
|
||||
Multiset<Integer> set = HashMultiset.create();
|
||||
for (Item item : itemContainer.getItems())
|
||||
{
|
||||
if (item.getId() != ItemID.BANK_FILLER)
|
||||
{
|
||||
set.add(item.getId(), item.getQuantity());
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
ContainerPrices calculate(@Nullable Item[] items)
|
||||
{
|
||||
if (items == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
long ge = 0;
|
||||
long alch = 0;
|
||||
|
||||
for (final Item item : items)
|
||||
{
|
||||
final int qty = item.getQuantity();
|
||||
final int id = item.getId();
|
||||
|
||||
if (id <= 0 || qty == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
alch += (long) getHaPrice(id) * qty;
|
||||
ge += (long) itemManager.getItemPrice(id) * qty;
|
||||
}
|
||||
|
||||
return new ContainerPrices(ge, alch);
|
||||
}
|
||||
|
||||
private int getHaPrice(int itemId)
|
||||
{
|
||||
switch (itemId)
|
||||
{
|
||||
case ItemID.COINS_995:
|
||||
return 1;
|
||||
case ItemID.PLATINUM_TOKEN:
|
||||
return 1000;
|
||||
default:
|
||||
return itemManager.getItemComposition(itemId).getHaPrice();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* 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.bank;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.ScriptID;
|
||||
import net.runelite.api.VarClientInt;
|
||||
import net.runelite.api.VarClientStr;
|
||||
import net.runelite.api.vars.InputType;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
@Singleton
|
||||
public class BankSearch
|
||||
{
|
||||
private final Client client;
|
||||
private final ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private BankSearch(
|
||||
final Client client,
|
||||
final ClientThread clientThread
|
||||
)
|
||||
{
|
||||
this.client = client;
|
||||
this.clientThread = clientThread;
|
||||
}
|
||||
|
||||
public void layoutBank()
|
||||
{
|
||||
Widget bankContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
|
||||
if (bankContainer == null || bankContainer.isHidden())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Object[] scriptArgs = bankContainer.getOnInvTransmitListener();
|
||||
if (scriptArgs == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
client.runScript(scriptArgs);
|
||||
}
|
||||
|
||||
public void initSearch()
|
||||
{
|
||||
clientThread.invoke(() ->
|
||||
{
|
||||
Widget bankContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
|
||||
if (bankContainer == null || bankContainer.isHidden())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Object[] bankBuildArgs = bankContainer.getOnInvTransmitListener();
|
||||
if (bankBuildArgs == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// the search toggle script requires 1 as its first argument
|
||||
Object[] searchToggleArgs = ArrayUtils.insert(1, bankBuildArgs, 1);
|
||||
searchToggleArgs[0] = ScriptID.BANKMAIN_SEARCH_TOGGLE;
|
||||
|
||||
// reset search to clear tab tags and also allow us to initiate a new search while searching
|
||||
reset(true);
|
||||
client.runScript(searchToggleArgs);
|
||||
});
|
||||
}
|
||||
|
||||
public void reset(boolean closeChat)
|
||||
{
|
||||
clientThread.invoke(() ->
|
||||
{
|
||||
// This ensures that any chatbox input (e.g from search) will not remain visible when
|
||||
// selecting/changing tab
|
||||
if (closeChat)
|
||||
{
|
||||
// this clears the input text and type, and resets the chatbox to allow input
|
||||
client.runScript(ScriptID.MESSAGE_LAYER_CLOSE, 1, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
client.setVar(VarClientInt.INPUT_TYPE, InputType.NONE.getType());
|
||||
client.setVar(VarClientStr.INPUT_TEXT, "");
|
||||
}
|
||||
|
||||
layoutBank();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 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.bank;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
class ContainerPrices
|
||||
{
|
||||
private long gePrice;
|
||||
private long highAlchPrice;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* 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.banktags;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("banktags")
|
||||
public interface BankTagsConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "useTabs",
|
||||
name = "Use Tag Tabs",
|
||||
description = "Enable the ability to add tabs to your bank which allow fast access to tags.",
|
||||
position = 1
|
||||
)
|
||||
default boolean tabs()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "rememberTab",
|
||||
name = "Remember last Tag Tab",
|
||||
description = "Enable the ability to remember last Tag Tab when closing/opening the bank.",
|
||||
position = 2
|
||||
)
|
||||
default boolean rememberTab()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "removeTabSeparators",
|
||||
name = "Remove tab separators",
|
||||
description = "Remove the tab separators normally present in tag tabs",
|
||||
position = 3
|
||||
)
|
||||
default boolean removeSeparators()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "preventTagTabDrags",
|
||||
name = "Prevent tag tab item dragging",
|
||||
description = "Ignore dragged items to prevent unwanted bank item reordering",
|
||||
position = 4
|
||||
)
|
||||
default boolean preventTagTabDrags()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "position",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default int position()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "position",
|
||||
name = "",
|
||||
description = ""
|
||||
)
|
||||
void position(int idx);
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "tab",
|
||||
name = "",
|
||||
description = "",
|
||||
hidden = true
|
||||
)
|
||||
default String tab()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "tab",
|
||||
name = "",
|
||||
description = ""
|
||||
)
|
||||
void tab(String tab);
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* Copyright (c) 2018, Tomas Slusny <slusnucky@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.banktags;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.primitives.Shorts;
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.event.MouseWheelEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemComposition;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import net.runelite.api.KeyCode;
|
||||
import net.runelite.api.MenuAction;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.ScriptID;
|
||||
import net.runelite.api.SpriteID;
|
||||
import net.runelite.api.VarClientStr;
|
||||
import net.runelite.api.events.DraggingWidgetChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.GrandExchangeSearched;
|
||||
import net.runelite.api.events.MenuEntryAdded;
|
||||
import net.runelite.api.events.MenuOptionClicked;
|
||||
import net.runelite.api.events.ScriptCallbackEvent;
|
||||
import net.runelite.api.events.ScriptPostFired;
|
||||
import net.runelite.api.events.ScriptPreFired;
|
||||
import net.runelite.api.events.WidgetLoaded;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.game.ItemVariationMapping;
|
||||
import net.runelite.client.game.SpriteManager;
|
||||
import net.runelite.client.game.chatbox.ChatboxPanelManager;
|
||||
import net.runelite.client.input.MouseManager;
|
||||
import net.runelite.client.input.MouseWheelListener;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDependency;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.plugins.banktags.tabs.TabInterface;
|
||||
import static net.runelite.client.plugins.banktags.tabs.TabInterface.FILTERED_CHARS;
|
||||
import net.runelite.client.plugins.banktags.tabs.TabSprites;
|
||||
import net.runelite.client.plugins.banktags.tabs.TagTab;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import net.runelite.client.util.Text;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Bank Tags",
|
||||
description = "Enable tagging of bank items and searching of bank tags",
|
||||
tags = {"searching", "tagging"}
|
||||
)
|
||||
@PluginDependency(ClueScrollPlugin.class)
|
||||
public class BankTagsPlugin extends Plugin implements MouseWheelListener
|
||||
{
|
||||
public static final String CONFIG_GROUP = "banktags";
|
||||
public static final String TAG_SEARCH = "tag:";
|
||||
private static final String EDIT_TAGS_MENU_OPTION = "Edit-tags";
|
||||
public static final String ICON_SEARCH = "icon_";
|
||||
public static final String TAG_TABS_CONFIG = "tagtabs";
|
||||
public static final String VAR_TAG_SUFFIX = "*";
|
||||
private static final int ITEMS_PER_ROW = 8;
|
||||
private static final int ITEM_VERTICAL_SPACING = 36;
|
||||
private static final int ITEM_HORIZONTAL_SPACING = 48;
|
||||
private static final int ITEM_ROW_START = 51;
|
||||
|
||||
private static final int MAX_RESULT_COUNT = 250;
|
||||
|
||||
private static final String SEARCH_BANK_INPUT_TEXT =
|
||||
"Show items whose names or tags contain the following text:<br>" +
|
||||
"(To show only tagged items, start your search with 'tag:')";
|
||||
private static final String SEARCH_BANK_INPUT_TEXT_FOUND =
|
||||
"Show items whose names or tags contain the following text: (%d found)<br>" +
|
||||
"(To show only tagged items, start your search with 'tag:')";
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private ChatboxPanelManager chatboxPanelManager;
|
||||
|
||||
@Inject
|
||||
private MouseManager mouseManager;
|
||||
|
||||
@Inject
|
||||
private BankTagsConfig config;
|
||||
|
||||
@Inject
|
||||
private TagManager tagManager;
|
||||
|
||||
@Inject
|
||||
private TabInterface tabInterface;
|
||||
|
||||
@Inject
|
||||
private SpriteManager spriteManager;
|
||||
|
||||
@Inject
|
||||
private ConfigManager configManager;
|
||||
|
||||
@Provides
|
||||
BankTagsConfig getConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(BankTagsConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetConfiguration()
|
||||
{
|
||||
List<String> extraKeys = Lists.newArrayList(
|
||||
CONFIG_GROUP + "." + TagManager.ITEM_KEY_PREFIX,
|
||||
CONFIG_GROUP + "." + ICON_SEARCH,
|
||||
CONFIG_GROUP + "." + TAG_TABS_CONFIG
|
||||
);
|
||||
|
||||
for (String prefix : extraKeys)
|
||||
{
|
||||
List<String> keys = configManager.getConfigurationKeys(prefix);
|
||||
for (String key : keys)
|
||||
{
|
||||
String[] str = key.split("\\.", 2);
|
||||
if (str.length == 2)
|
||||
{
|
||||
configManager.unsetConfiguration(str[0], str[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clientThread.invokeLater(() ->
|
||||
{
|
||||
tabInterface.destroy();
|
||||
tabInterface.init();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void startUp()
|
||||
{
|
||||
cleanConfig();
|
||||
mouseManager.registerMouseWheelListener(this);
|
||||
clientThread.invokeLater(tabInterface::init);
|
||||
spriteManager.addSpriteOverrides(TabSprites.values());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void cleanConfig()
|
||||
{
|
||||
removeInvalidTags("tagtabs");
|
||||
|
||||
List<String> tags = configManager.getConfigurationKeys(CONFIG_GROUP + ".item_");
|
||||
tags.forEach(s ->
|
||||
{
|
||||
String[] split = s.split("\\.", 2);
|
||||
removeInvalidTags(split[1]);
|
||||
});
|
||||
|
||||
List<String> icons = configManager.getConfigurationKeys(CONFIG_GROUP + ".icon_");
|
||||
icons.forEach(s ->
|
||||
{
|
||||
String[] split = s.split("\\.", 2);
|
||||
String replaced = split[1].replaceAll("[<>/]", "");
|
||||
if (!split[1].equals(replaced))
|
||||
{
|
||||
String value = configManager.getConfiguration(CONFIG_GROUP, split[1]);
|
||||
configManager.unsetConfiguration(CONFIG_GROUP, split[1]);
|
||||
if (replaced.length() > "icon_".length())
|
||||
{
|
||||
configManager.setConfiguration(CONFIG_GROUP, replaced, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void removeInvalidTags(final String key)
|
||||
{
|
||||
final String value = configManager.getConfiguration(CONFIG_GROUP, key);
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String replaced = value.replaceAll("[<>:/]", "");
|
||||
if (!value.equals(replaced))
|
||||
{
|
||||
replaced = Text.toCSV(Text.fromCSV(replaced));
|
||||
if (replaced.isEmpty())
|
||||
{
|
||||
configManager.unsetConfiguration(CONFIG_GROUP, key);
|
||||
}
|
||||
else
|
||||
{
|
||||
configManager.setConfiguration(CONFIG_GROUP, key, replaced);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutDown()
|
||||
{
|
||||
mouseManager.unregisterMouseWheelListener(this);
|
||||
clientThread.invokeLater(tabInterface::destroy);
|
||||
spriteManager.removeSpriteOverrides(TabSprites.values());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGrandExchangeSearched(GrandExchangeSearched event)
|
||||
{
|
||||
final String input = client.getVar(VarClientStr.INPUT_TEXT);
|
||||
if (!input.startsWith(TAG_SEARCH))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
event.consume();
|
||||
|
||||
final String tag = input.substring(TAG_SEARCH.length()).trim();
|
||||
final Set<Integer> ids = tagManager.getItemsForTag(tag)
|
||||
.stream()
|
||||
.mapToInt(Math::abs)
|
||||
.mapToObj(ItemVariationMapping::getVariations)
|
||||
.flatMap(Collection::stream)
|
||||
.distinct()
|
||||
.filter(i -> itemManager.getItemComposition(i).isTradeable())
|
||||
.limit(MAX_RESULT_COUNT)
|
||||
.collect(Collectors.toCollection(TreeSet::new));
|
||||
|
||||
client.setGeSearchResultIndex(0);
|
||||
client.setGeSearchResultCount(ids.size());
|
||||
client.setGeSearchResultIds(Shorts.toArray(ids));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onScriptCallbackEvent(ScriptCallbackEvent event)
|
||||
{
|
||||
String eventName = event.getEventName();
|
||||
|
||||
int[] intStack = client.getIntStack();
|
||||
String[] stringStack = client.getStringStack();
|
||||
int intStackSize = client.getIntStackSize();
|
||||
int stringStackSize = client.getStringStackSize();
|
||||
|
||||
tabInterface.handleScriptEvent(event);
|
||||
|
||||
switch (eventName)
|
||||
{
|
||||
case "setSearchBankInputText":
|
||||
stringStack[stringStackSize - 1] = SEARCH_BANK_INPUT_TEXT;
|
||||
break;
|
||||
case "setSearchBankInputTextFound":
|
||||
{
|
||||
int matches = intStack[intStackSize - 1];
|
||||
stringStack[stringStackSize - 1] = String.format(SEARCH_BANK_INPUT_TEXT_FOUND, matches);
|
||||
break;
|
||||
}
|
||||
case "bankSearchFilter":
|
||||
final int itemId = intStack[intStackSize - 1];
|
||||
final String searchfilter = stringStack[stringStackSize - 1];
|
||||
|
||||
// This event only fires when the bank is in search mode. It will fire even if there is no search
|
||||
// input. We prevent having a tag tab open while also performing a normal search, so if a tag tab
|
||||
// is active here it must mean we have placed the bank into search mode. See onScriptPostFired().
|
||||
TagTab activeTab = tabInterface.getActiveTab();
|
||||
String search = activeTab != null ? TAG_SEARCH + activeTab.getTag() : searchfilter;
|
||||
|
||||
if (search.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
boolean tagSearch = search.startsWith(TAG_SEARCH);
|
||||
if (tagSearch)
|
||||
{
|
||||
search = search.substring(TAG_SEARCH.length()).trim();
|
||||
}
|
||||
|
||||
if (tagManager.findTag(itemId, search))
|
||||
{
|
||||
// return true
|
||||
intStack[intStackSize - 2] = 1;
|
||||
}
|
||||
else if (tagSearch)
|
||||
{
|
||||
// if the item isn't tagged we return false to prevent the item matching if the item name happens
|
||||
// to contain the tag name.
|
||||
intStack[intStackSize - 2] = 0;
|
||||
}
|
||||
break;
|
||||
case "getSearchingTagTab":
|
||||
intStack[intStackSize - 1] = tabInterface.isActive() ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMenuEntryAdded(MenuEntryAdded event)
|
||||
{
|
||||
MenuEntry[] entries = client.getMenuEntries();
|
||||
|
||||
if (event.getActionParam1() == WidgetInfo.BANK_ITEM_CONTAINER.getId()
|
||||
&& event.getOption().equals("Examine"))
|
||||
{
|
||||
Widget container = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
|
||||
Widget item = container.getChild(event.getActionParam());
|
||||
int itemID = item.getItemId();
|
||||
String text = EDIT_TAGS_MENU_OPTION;
|
||||
int tagCount = tagManager.getTags(itemID, false).size() + tagManager.getTags(itemID, true).size();
|
||||
|
||||
if (tagCount > 0)
|
||||
{
|
||||
text += " (" + tagCount + ")";
|
||||
}
|
||||
|
||||
MenuEntry editTags = new MenuEntry();
|
||||
editTags.setActionParam(event.getActionParam());
|
||||
editTags.setParam1(event.getActionParam1());
|
||||
editTags.setTarget(event.getTarget());
|
||||
editTags.setOption(text);
|
||||
editTags.setType(MenuAction.RUNELITE.getId());
|
||||
editTags.setIdentifier(event.getIdentifier());
|
||||
entries = Arrays.copyOf(entries, entries.length + 1);
|
||||
entries[entries.length - 1] = editTags;
|
||||
client.setMenuEntries(entries);
|
||||
}
|
||||
|
||||
tabInterface.handleAdd(event);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMenuOptionClicked(MenuOptionClicked event)
|
||||
{
|
||||
if (event.getWidgetId() == WidgetInfo.BANK_ITEM_CONTAINER.getId()
|
||||
&& event.getMenuAction() == MenuAction.RUNELITE
|
||||
&& event.getMenuOption().startsWith(EDIT_TAGS_MENU_OPTION))
|
||||
{
|
||||
event.consume();
|
||||
int inventoryIndex = event.getActionParam();
|
||||
ItemContainer bankContainer = client.getItemContainer(InventoryID.BANK);
|
||||
if (bankContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Item[] items = bankContainer.getItems();
|
||||
if (inventoryIndex < 0 || inventoryIndex >= items.length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Item item = bankContainer.getItems()[inventoryIndex];
|
||||
if (item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int itemId = item.getId();
|
||||
ItemComposition itemComposition = itemManager.getItemComposition(itemId);
|
||||
String name = itemComposition.getName();
|
||||
|
||||
// Get both tags and vartags and append * to end of vartags name
|
||||
Collection<String> tags = tagManager.getTags(itemId, false);
|
||||
tagManager.getTags(itemId, true).stream()
|
||||
.map(i -> i + "*")
|
||||
.forEach(tags::add);
|
||||
|
||||
String initialValue = Text.toCSV(tags);
|
||||
|
||||
chatboxPanelManager.openTextInput(name + " tags:<br>(append " + VAR_TAG_SUFFIX + " for variation tag)")
|
||||
.addCharValidator(FILTERED_CHARS)
|
||||
.value(initialValue)
|
||||
.onDone((Consumer<String>) (newValue) ->
|
||||
clientThread.invoke(() ->
|
||||
{
|
||||
// Split inputted tags to vartags (ending with *) and regular tags
|
||||
final Collection<String> newTags = new ArrayList<>(Text.fromCSV(newValue.toLowerCase()));
|
||||
final Collection<String> newVarTags = new ArrayList<>(newTags).stream().filter(s -> s.endsWith(VAR_TAG_SUFFIX)).map(s ->
|
||||
{
|
||||
newTags.remove(s);
|
||||
return s.substring(0, s.length() - VAR_TAG_SUFFIX.length());
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// And save them
|
||||
tagManager.setTagString(itemId, Text.toCSV(newTags), false);
|
||||
tagManager.setTagString(itemId, Text.toCSV(newVarTags), true);
|
||||
|
||||
// Check both previous and current tags in case the tag got removed in new tags or in case
|
||||
// the tag got added in new tags
|
||||
tabInterface.updateTabIfActive(Text.fromCSV(initialValue.toLowerCase().replaceAll(Pattern.quote(VAR_TAG_SUFFIX), "")));
|
||||
tabInterface.updateTabIfActive(Text.fromCSV(newValue.toLowerCase().replaceAll(Pattern.quote(VAR_TAG_SUFFIX), "")));
|
||||
}))
|
||||
.build();
|
||||
}
|
||||
else
|
||||
{
|
||||
tabInterface.handleClick(event);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged configChanged)
|
||||
{
|
||||
if (configChanged.getGroup().equals(CONFIG_GROUP) && configChanged.getKey().equals("useTabs"))
|
||||
{
|
||||
if (config.tabs())
|
||||
{
|
||||
clientThread.invokeLater(tabInterface::init);
|
||||
}
|
||||
else
|
||||
{
|
||||
clientThread.invokeLater(tabInterface::destroy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onScriptPreFired(ScriptPreFired event)
|
||||
{
|
||||
int scriptId = event.getScriptId();
|
||||
if (scriptId == ScriptID.BANKMAIN_FINISHBUILDING)
|
||||
{
|
||||
// Since we apply tag tab search filters even when the bank is not in search mode,
|
||||
// bankkmain_build will reset the bank title to "The Bank of Gielinor". So apply our
|
||||
// own title.
|
||||
TagTab activeTab = tabInterface.getActiveTab();
|
||||
if (tabInterface.isTagTabActive())
|
||||
{
|
||||
// Tag tab tab has its own title since it isn't a real tag
|
||||
Widget bankTitle = client.getWidget(WidgetInfo.BANK_TITLE_BAR);
|
||||
bankTitle.setText("Tag tab tab");
|
||||
}
|
||||
else if (activeTab != null)
|
||||
{
|
||||
Widget bankTitle = client.getWidget(WidgetInfo.BANK_TITLE_BAR);
|
||||
bankTitle.setText("Tag tab <col=ff0000>" + activeTab.getTag() + "</col>");
|
||||
}
|
||||
}
|
||||
else if (scriptId == ScriptID.BANKMAIN_SEARCH_TOGGLE)
|
||||
{
|
||||
tabInterface.handleSearch();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onScriptPostFired(ScriptPostFired event)
|
||||
{
|
||||
if (event.getScriptId() == ScriptID.BANKMAIN_SEARCHING)
|
||||
{
|
||||
// The return value of bankmain_searching is on the stack. If we have a tag tab active
|
||||
// make it return true to put the bank in a searching state.
|
||||
if (tabInterface.getActiveTab() != null || tabInterface.isTagTabActive())
|
||||
{
|
||||
client.getIntStack()[client.getIntStackSize() - 1] = 1; // true
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getScriptId() != ScriptID.BANKMAIN_BUILD || !config.removeSeparators())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tabInterface.isActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Widget itemContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
|
||||
if (itemContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int items = 0;
|
||||
|
||||
Widget[] containerChildren = itemContainer.getDynamicChildren();
|
||||
|
||||
// sort the child array as the items are not in the displayed order
|
||||
Arrays.sort(containerChildren, Comparator.comparing(Widget::getOriginalY)
|
||||
.thenComparing(Widget::getOriginalX));
|
||||
|
||||
for (Widget child : containerChildren)
|
||||
{
|
||||
if (child.getItemId() != -1 && !child.isHidden())
|
||||
{
|
||||
// calculate correct item position as if this was a normal tab
|
||||
int adjYOffset = (items / ITEMS_PER_ROW) * ITEM_VERTICAL_SPACING;
|
||||
int adjXOffset = (items % ITEMS_PER_ROW) * ITEM_HORIZONTAL_SPACING + ITEM_ROW_START;
|
||||
|
||||
if (child.getOriginalY() != adjYOffset)
|
||||
{
|
||||
child.setOriginalY(adjYOffset);
|
||||
child.revalidate();
|
||||
}
|
||||
|
||||
if (child.getOriginalX() != adjXOffset)
|
||||
{
|
||||
child.setOriginalX(adjXOffset);
|
||||
child.revalidate();
|
||||
}
|
||||
|
||||
items++;
|
||||
}
|
||||
|
||||
// separator line or tab text
|
||||
if (child.getSpriteId() == SpriteID.RESIZEABLE_MODE_SIDE_PANEL_BACKGROUND
|
||||
|| child.getText().contains("Tab"))
|
||||
{
|
||||
child.setHidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
final Widget bankItemContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER);
|
||||
int itemContainerHeight = bankItemContainer.getHeight();
|
||||
// add a second row of height here to allow users to scroll down when the last row is partially visible
|
||||
int adjustedScrollHeight = (items / ITEMS_PER_ROW) * ITEM_VERTICAL_SPACING + ITEM_VERTICAL_SPACING;
|
||||
itemContainer.setScrollHeight(Math.max(adjustedScrollHeight, itemContainerHeight));
|
||||
|
||||
final int itemContainerScroll = bankItemContainer.getScrollY();
|
||||
clientThread.invokeLater(() ->
|
||||
client.runScript(ScriptID.UPDATE_SCROLLBAR,
|
||||
WidgetInfo.BANK_SCROLLBAR.getId(),
|
||||
WidgetInfo.BANK_ITEM_CONTAINER.getId(),
|
||||
itemContainerScroll));
|
||||
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
tabInterface.update();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onDraggingWidgetChanged(DraggingWidgetChanged event)
|
||||
{
|
||||
final boolean shiftPressed = client.isKeyPressed(KeyCode.KC_SHIFT);
|
||||
tabInterface.handleDrag(event.isDraggingWidget(), shiftPressed);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onWidgetLoaded(WidgetLoaded event)
|
||||
{
|
||||
if (event.getGroupId() == WidgetID.BANK_GROUP_ID)
|
||||
{
|
||||
tabInterface.init();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MouseWheelEvent mouseWheelMoved(MouseWheelEvent event)
|
||||
{
|
||||
tabInterface.handleWheel(event);
|
||||
return event;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* 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.banktags;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.game.ItemVariationMapping;
|
||||
import static net.runelite.client.plugins.banktags.BankTagsPlugin.CONFIG_GROUP;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollService;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.ClueScroll;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.CoordinateClue;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.EmoteClue;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.FairyRingClue;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.HotColdClue;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.MapClue;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement;
|
||||
import net.runelite.client.util.Text;
|
||||
|
||||
@Singleton
|
||||
public class TagManager
|
||||
{
|
||||
static final String ITEM_KEY_PREFIX = "item_";
|
||||
private final ConfigManager configManager;
|
||||
private final ItemManager itemManager;
|
||||
private final ClueScrollService clueScrollService;
|
||||
|
||||
@Inject
|
||||
private TagManager(
|
||||
final ItemManager itemManager,
|
||||
final ConfigManager configManager,
|
||||
final ClueScrollService clueScrollService)
|
||||
{
|
||||
this.itemManager = itemManager;
|
||||
this.configManager = configManager;
|
||||
this.clueScrollService = clueScrollService;
|
||||
}
|
||||
|
||||
String getTagString(int itemId, boolean variation)
|
||||
{
|
||||
itemId = getItemId(itemId, variation);
|
||||
|
||||
String config = configManager.getConfiguration(CONFIG_GROUP, ITEM_KEY_PREFIX + itemId);
|
||||
if (config == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
Collection<String> getTags(int itemId, boolean variation)
|
||||
{
|
||||
return new LinkedHashSet<>(Text.fromCSV(getTagString(itemId, variation).toLowerCase()));
|
||||
}
|
||||
|
||||
void setTagString(int itemId, String tags, boolean variation)
|
||||
{
|
||||
itemId = getItemId(itemId, variation);
|
||||
|
||||
if (Strings.isNullOrEmpty(tags))
|
||||
{
|
||||
configManager.unsetConfiguration(CONFIG_GROUP, ITEM_KEY_PREFIX + itemId);
|
||||
}
|
||||
else
|
||||
{
|
||||
configManager.setConfiguration(CONFIG_GROUP, ITEM_KEY_PREFIX + itemId, tags);
|
||||
}
|
||||
}
|
||||
|
||||
public void addTags(int itemId, final Collection<String> t, boolean variation)
|
||||
{
|
||||
final Collection<String> tags = getTags(itemId, variation);
|
||||
if (tags.addAll(t))
|
||||
{
|
||||
setTags(itemId, tags, variation);
|
||||
}
|
||||
}
|
||||
|
||||
public void addTag(int itemId, String tag, boolean variation)
|
||||
{
|
||||
final Collection<String> tags = getTags(itemId, variation);
|
||||
if (tags.add(Text.standardize(tag)))
|
||||
{
|
||||
setTags(itemId, tags, variation);
|
||||
}
|
||||
}
|
||||
|
||||
private void setTags(int itemId, Collection<String> tags, boolean variation)
|
||||
{
|
||||
setTagString(itemId, Text.toCSV(tags), variation);
|
||||
}
|
||||
|
||||
boolean findTag(int itemId, String search)
|
||||
{
|
||||
if (search.equals("clue") && testClue(itemId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Collection<String> tags = getTags(itemId, false);
|
||||
tags.addAll(getTags(itemId, true));
|
||||
return tags.stream().anyMatch(tag -> tag.startsWith(Text.standardize(search)));
|
||||
}
|
||||
|
||||
public List<Integer> getItemsForTag(String tag)
|
||||
{
|
||||
final String prefix = CONFIG_GROUP + "." + ITEM_KEY_PREFIX;
|
||||
return configManager.getConfigurationKeys(prefix).stream()
|
||||
.map(item -> Integer.parseInt(item.replace(prefix, "")))
|
||||
.filter(item -> getTags(item, false).contains(tag) || getTags(item, true).contains(tag))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void removeTag(String tag)
|
||||
{
|
||||
final String prefix = CONFIG_GROUP + "." + ITEM_KEY_PREFIX;
|
||||
configManager.getConfigurationKeys(prefix).forEach(item ->
|
||||
{
|
||||
int id = Integer.parseInt(item.replace(prefix, ""));
|
||||
removeTag(id, tag);
|
||||
});
|
||||
}
|
||||
|
||||
public void removeTag(int itemId, String tag)
|
||||
{
|
||||
Collection<String> tags = getTags(itemId, false);
|
||||
if (tags.remove(Text.standardize(tag)))
|
||||
{
|
||||
setTags(itemId, tags, false);
|
||||
}
|
||||
|
||||
tags = getTags(itemId, true);
|
||||
if (tags.remove(Text.standardize(tag)))
|
||||
{
|
||||
setTags(itemId, tags, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void renameTag(String oldTag, String newTag)
|
||||
{
|
||||
List<Integer> items = getItemsForTag(Text.standardize(oldTag));
|
||||
items.forEach(id ->
|
||||
{
|
||||
Collection<String> tags = getTags(id, id < 0);
|
||||
|
||||
tags.remove(Text.standardize(oldTag));
|
||||
tags.add(Text.standardize(newTag));
|
||||
|
||||
setTags(id, tags, id < 0);
|
||||
});
|
||||
}
|
||||
|
||||
private int getItemId(int itemId, boolean variation)
|
||||
{
|
||||
itemId = Math.abs(itemId);
|
||||
itemId = itemManager.canonicalize(itemId);
|
||||
|
||||
if (variation)
|
||||
{
|
||||
itemId = ItemVariationMapping.map(itemId) * -1;
|
||||
}
|
||||
|
||||
return itemId;
|
||||
}
|
||||
|
||||
private boolean testClue(int itemId)
|
||||
{
|
||||
ClueScroll c = clueScrollService.getClue();
|
||||
|
||||
if (c == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c instanceof EmoteClue)
|
||||
{
|
||||
EmoteClue emote = (EmoteClue) c;
|
||||
|
||||
for (ItemRequirement ir : emote.getItemRequirements())
|
||||
{
|
||||
if (ir.fulfilledBy(itemId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (c instanceof CoordinateClue || c instanceof HotColdClue || c instanceof FairyRingClue)
|
||||
{
|
||||
return itemId == ItemID.SPADE;
|
||||
}
|
||||
else if (c instanceof MapClue)
|
||||
{
|
||||
MapClue mapClue = (MapClue) c;
|
||||
|
||||
return mapClue.getObjectId() == -1 && itemId == ItemID.SPADE;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* 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.banktags.tabs;
|
||||
|
||||
class MenuIndexes
|
||||
{
|
||||
static class NewTab
|
||||
{
|
||||
static final int NEW_TAB = 2;
|
||||
static final int IMPORT_TAB = 3;
|
||||
static final int OPEN_TAB_MENU = 4;
|
||||
}
|
||||
|
||||
static class Tab
|
||||
{
|
||||
static final int OPEN_TAG = 2;
|
||||
static final int CHANGE_ICON = 3;
|
||||
static final int DELETE_TAB = 4;
|
||||
static final int EXPORT_TAB = 5;
|
||||
static final int RENAME_TAB = 6;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* 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.banktags.tabs;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import static net.runelite.client.plugins.banktags.BankTagsPlugin.CONFIG_GROUP;
|
||||
import static net.runelite.client.plugins.banktags.BankTagsPlugin.ICON_SEARCH;
|
||||
import static net.runelite.client.plugins.banktags.BankTagsPlugin.TAG_TABS_CONFIG;
|
||||
import net.runelite.client.util.Text;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
|
||||
@Singleton
|
||||
class TabManager
|
||||
{
|
||||
@Getter
|
||||
private final List<TagTab> tabs = new ArrayList<>();
|
||||
private final ConfigManager configManager;
|
||||
|
||||
@Inject
|
||||
private TabManager(ConfigManager configManager)
|
||||
{
|
||||
this.configManager = configManager;
|
||||
}
|
||||
|
||||
void add(TagTab tagTab)
|
||||
{
|
||||
if (!contains(tagTab.getTag()))
|
||||
{
|
||||
tabs.add(tagTab);
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
tabs.forEach(t -> t.setHidden(true));
|
||||
tabs.clear();
|
||||
}
|
||||
|
||||
TagTab find(String tag)
|
||||
{
|
||||
Optional<TagTab> first = tabs.stream().filter(t -> t.getTag().equals(Text.standardize(tag))).findAny();
|
||||
return first.orElse(null);
|
||||
}
|
||||
|
||||
List<String> getAllTabs()
|
||||
{
|
||||
return Text.fromCSV(MoreObjects.firstNonNull(configManager.getConfiguration(CONFIG_GROUP, TAG_TABS_CONFIG), ""));
|
||||
}
|
||||
|
||||
TagTab load(String tag)
|
||||
{
|
||||
TagTab tagTab = find(tag);
|
||||
|
||||
if (tagTab == null)
|
||||
{
|
||||
tag = Text.standardize(tag);
|
||||
String item = configManager.getConfiguration(CONFIG_GROUP, ICON_SEARCH + tag);
|
||||
int itemid = NumberUtils.toInt(item, ItemID.SPADE);
|
||||
tagTab = new TagTab(itemid, tag);
|
||||
}
|
||||
|
||||
return tagTab;
|
||||
}
|
||||
|
||||
void swap(String tagToMove, String tagDestination)
|
||||
{
|
||||
tagToMove = Text.standardize(tagToMove);
|
||||
tagDestination = Text.standardize(tagDestination);
|
||||
|
||||
if (contains(tagToMove) && contains(tagDestination))
|
||||
{
|
||||
Collections.swap(tabs, indexOf(tagToMove), indexOf(tagDestination));
|
||||
}
|
||||
}
|
||||
|
||||
void insert(String tagToMove, String tagDestination)
|
||||
{
|
||||
tagToMove = Text.standardize(tagToMove);
|
||||
tagDestination = Text.standardize(tagDestination);
|
||||
|
||||
if (contains(tagToMove) && contains(tagDestination))
|
||||
{
|
||||
tabs.add(indexOf(tagDestination), tabs.remove(indexOf(tagToMove)));
|
||||
}
|
||||
}
|
||||
|
||||
void remove(String tag)
|
||||
{
|
||||
TagTab tagTab = find(tag);
|
||||
|
||||
if (tagTab != null)
|
||||
{
|
||||
tagTab.setHidden(true);
|
||||
tabs.remove(tagTab);
|
||||
removeIcon(tag);
|
||||
}
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
String tags = Text.toCSV(tabs.stream().map(TagTab::getTag).collect(Collectors.toList()));
|
||||
configManager.setConfiguration(CONFIG_GROUP, TAG_TABS_CONFIG, tags);
|
||||
}
|
||||
|
||||
void removeIcon(final String tag)
|
||||
{
|
||||
configManager.unsetConfiguration(CONFIG_GROUP, ICON_SEARCH + Text.standardize(tag));
|
||||
}
|
||||
|
||||
void setIcon(final String tag, final String icon)
|
||||
{
|
||||
configManager.setConfiguration(CONFIG_GROUP, ICON_SEARCH + Text.standardize(tag), icon);
|
||||
}
|
||||
|
||||
int size()
|
||||
{
|
||||
return tabs.size();
|
||||
}
|
||||
|
||||
private boolean contains(String tag)
|
||||
{
|
||||
return tabs.stream().anyMatch(t -> t.getTag().equals(tag));
|
||||
}
|
||||
|
||||
private int indexOf(TagTab tagTab)
|
||||
{
|
||||
return tabs.indexOf(tagTab);
|
||||
}
|
||||
|
||||
private int indexOf(String tag)
|
||||
{
|
||||
return indexOf(find(tag));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* 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.banktags.tabs;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.runelite.client.game.SpriteOverride;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public enum TabSprites implements SpriteOverride
|
||||
{
|
||||
INCINERATOR(-200, "incinerator.png"),
|
||||
TAB_BACKGROUND(-201, "tag-tab.png"),
|
||||
TAB_BACKGROUND_ACTIVE(-202, "tag-tab-active.png"),
|
||||
UP_ARROW(-203, "up-arrow.png"),
|
||||
DOWN_ARROW(-204, "down-arrow.png"),
|
||||
NEW_TAB(-205, "new-tab.png");
|
||||
|
||||
@Getter
|
||||
private final int spriteId;
|
||||
|
||||
@Getter
|
||||
private final String fileName;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* 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.banktags.tabs;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(of = "tag")
|
||||
public class TagTab
|
||||
{
|
||||
private String tag;
|
||||
private int iconItemId;
|
||||
private Widget background;
|
||||
private Widget icon;
|
||||
private Widget menu;
|
||||
|
||||
TagTab(int iconItemId, String tag)
|
||||
{
|
||||
this.iconItemId = iconItemId;
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
void setHidden(boolean hide)
|
||||
{
|
||||
if (background != null)
|
||||
{
|
||||
background.setHidden(hide);
|
||||
}
|
||||
|
||||
if (icon != null)
|
||||
{
|
||||
icon.setHidden(hide);
|
||||
}
|
||||
|
||||
if (menu != null)
|
||||
{
|
||||
menu.setHidden(hide);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Cameron <https://github.com/noremac201>
|
||||
* Copyright (c) 2018, Jacob M <https://github.com/jacoblairm>
|
||||
* 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.barbarianassault;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("barbarianAssault")
|
||||
public interface BarbarianAssaultConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "showTimer",
|
||||
name = "Show call change timer",
|
||||
description = "Show time to next call change"
|
||||
)
|
||||
default boolean showTimer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showHealerBars",
|
||||
name = "Show health bars for teammates when healer",
|
||||
description = "Displays team health for healer"
|
||||
)
|
||||
default boolean showHealerBars()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "waveTimes",
|
||||
name = "Show wave and game duration",
|
||||
description = "Displays wave and game duration"
|
||||
)
|
||||
default boolean waveTimes()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Cameron <https://github.com/noremac201>
|
||||
* Copyright (c) 2018, Jacob M <https://github.com/jacoblairm>
|
||||
* 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.barbarianassault;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.Image;
|
||||
import javax.inject.Inject;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.VarbitChanged;
|
||||
import net.runelite.api.events.WidgetLoaded;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.chat.ChatColorType;
|
||||
import net.runelite.client.chat.ChatMessageBuilder;
|
||||
import net.runelite.client.chat.ChatMessageManager;
|
||||
import net.runelite.client.chat.QueuedMessage;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Barbarian Assault",
|
||||
description = "Show a timer to the next call change and game/wave duration in chat.",
|
||||
tags = {"minigame", "overlay", "timer"}
|
||||
)
|
||||
public class BarbarianAssaultPlugin extends Plugin
|
||||
{
|
||||
private static final int BA_WAVE_NUM_INDEX = 2;
|
||||
private static final String START_WAVE = "1";
|
||||
private static final String ENDGAME_REWARD_NEEDLE_TEXT = "<br>5";
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private Image clockImage;
|
||||
private int inGameBit = 0;
|
||||
private String currentWave = START_WAVE;
|
||||
private GameTimer gameTime;
|
||||
|
||||
@Getter
|
||||
private Round currentRound;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ChatMessageManager chatMessageManager;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private BarbarianAssaultConfig config;
|
||||
|
||||
@Inject
|
||||
private TimerOverlay timerOverlay;
|
||||
|
||||
@Inject
|
||||
private HealerOverlay healerOverlay;
|
||||
|
||||
@Provides
|
||||
BarbarianAssaultConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(BarbarianAssaultConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(timerOverlay);
|
||||
overlayManager.add(healerOverlay);
|
||||
|
||||
clockImage = ImageUtil.getResourceStreamFromClass(getClass(), "clock.png");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
overlayManager.remove(timerOverlay);
|
||||
overlayManager.remove(healerOverlay);
|
||||
gameTime = null;
|
||||
currentWave = START_WAVE;
|
||||
inGameBit = 0;
|
||||
clockImage = null;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onWidgetLoaded(WidgetLoaded event)
|
||||
{
|
||||
switch (event.getGroupId())
|
||||
{
|
||||
case WidgetID.BA_REWARD_GROUP_ID:
|
||||
{
|
||||
Widget rewardWidget = client.getWidget(WidgetInfo.BA_REWARD_TEXT);
|
||||
|
||||
if (config.waveTimes() && rewardWidget != null && rewardWidget.getText().contains(ENDGAME_REWARD_NEEDLE_TEXT) && gameTime != null)
|
||||
{
|
||||
announceTime("Game finished, duration: ", gameTime.getTime(false));
|
||||
gameTime = null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case WidgetID.BA_ATTACKER_GROUP_ID:
|
||||
{
|
||||
setRound(Role.ATTACKER);
|
||||
break;
|
||||
}
|
||||
case WidgetID.BA_DEFENDER_GROUP_ID:
|
||||
{
|
||||
setRound(Role.DEFENDER);
|
||||
break;
|
||||
}
|
||||
case WidgetID.BA_HEALER_GROUP_ID:
|
||||
{
|
||||
setRound(Role.HEALER);
|
||||
break;
|
||||
}
|
||||
case WidgetID.BA_COLLECTOR_GROUP_ID:
|
||||
{
|
||||
setRound(Role.COLLECTOR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onChatMessage(ChatMessage event)
|
||||
{
|
||||
if (event.getType() == ChatMessageType.GAMEMESSAGE
|
||||
&& event.getMessage().startsWith("---- Wave:"))
|
||||
{
|
||||
String[] message = event.getMessage().split(" ");
|
||||
currentWave = message[BA_WAVE_NUM_INDEX];
|
||||
|
||||
if (currentWave.equals(START_WAVE))
|
||||
{
|
||||
gameTime = new GameTimer();
|
||||
}
|
||||
else if (gameTime != null)
|
||||
{
|
||||
gameTime.setWaveStartTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onVarbitChanged(VarbitChanged event)
|
||||
{
|
||||
int inGame = client.getVar(Varbits.IN_GAME_BA);
|
||||
|
||||
if (inGameBit != inGame)
|
||||
{
|
||||
if (inGameBit == 1)
|
||||
{
|
||||
currentRound = null;
|
||||
|
||||
// Use an instance check to determine if this is exiting a game or a tutorial
|
||||
// After exiting tutorials there is a small delay before changing IN_GAME_BA back to
|
||||
// 0 whereas when in a real wave it changes while still in the instance.
|
||||
if (config.waveTimes() && gameTime != null && client.isInInstancedRegion())
|
||||
{
|
||||
announceTime("Wave " + currentWave + " duration: ", gameTime.getTime(true));
|
||||
}
|
||||
}
|
||||
|
||||
inGameBit = inGame;
|
||||
}
|
||||
}
|
||||
|
||||
private void setRound(Role role)
|
||||
{
|
||||
// Prevent changing rounds when a round is already set, as widgets can be
|
||||
// loaded multiple times in game from eg. opening and closing the horn
|
||||
// of glory.
|
||||
if (currentRound == null)
|
||||
{
|
||||
currentRound = new Round(role);
|
||||
}
|
||||
}
|
||||
|
||||
private void announceTime(String preText, String time)
|
||||
{
|
||||
final String chatMessage = new ChatMessageBuilder()
|
||||
.append(ChatColorType.NORMAL)
|
||||
.append(preText)
|
||||
.append(ChatColorType.HIGHLIGHT)
|
||||
.append(time)
|
||||
.build();
|
||||
|
||||
chatMessageManager.queue(QueuedMessage.builder()
|
||||
.type(ChatMessageType.CONSOLE)
|
||||
.runeLiteFormattedMessage(chatMessage)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Jacob M <https://github.com/jacoblairm>
|
||||
* 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.barbarianassault;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import static net.runelite.client.util.RSTimeUnit.GAME_TICKS;
|
||||
|
||||
class GameTimer
|
||||
{
|
||||
final private Instant startTime = Instant.now();
|
||||
private Instant prevWave = startTime;
|
||||
|
||||
String getTime(boolean waveTime)
|
||||
{
|
||||
final Instant now = Instant.now();
|
||||
final Duration elapsed;
|
||||
|
||||
if (waveTime)
|
||||
{
|
||||
elapsed = Duration.between(prevWave, now);
|
||||
}
|
||||
else
|
||||
{
|
||||
elapsed = Duration.between(startTime, now).minus(Duration.of(1, GAME_TICKS));
|
||||
}
|
||||
|
||||
return formatTime(LocalTime.ofSecondOfDay(elapsed.getSeconds()));
|
||||
}
|
||||
|
||||
void setWaveStartTime()
|
||||
{
|
||||
prevWave = Instant.now();
|
||||
}
|
||||
|
||||
private static String formatTime(LocalTime time)
|
||||
{
|
||||
if (time.getHour() > 0)
|
||||
{
|
||||
return time.format(DateTimeFormatter.ofPattern("HH:mm"));
|
||||
}
|
||||
else if (time.getMinute() > 9)
|
||||
{
|
||||
return time.format(DateTimeFormatter.ofPattern("mm:ss"));
|
||||
}
|
||||
else
|
||||
{
|
||||
return time.format(DateTimeFormatter.ofPattern("m:ss"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 2019, whartd <github.com/whartd>
|
||||
* 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.barbarianassault;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.inject.Inject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Client;
|
||||
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||
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 static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
|
||||
import net.runelite.client.ui.overlay.OverlayMenuEntry;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
class HealerOverlay extends Overlay
|
||||
{
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
private enum HealerTeam
|
||||
{
|
||||
TEAMMATE1(WidgetInfo.BA_HEAL_TEAMMATE1, 28, 2, 115),
|
||||
TEAMMATE2(WidgetInfo.BA_HEAL_TEAMMATE2, 26, 2, 115),
|
||||
TEAMMATE3(WidgetInfo.BA_HEAL_TEAMMATE3, 26, 2, 115),
|
||||
TEAMMATE4(WidgetInfo.BA_HEAL_TEAMMATE4, 25, 2, 115);
|
||||
|
||||
private WidgetInfo teammate;
|
||||
private int offsetX;
|
||||
private int offsetY;
|
||||
private int width;
|
||||
}
|
||||
|
||||
private static final Color HP_HIGH = new Color(10, 146, 5, 125);
|
||||
private static final Color HP_MID = new Color(146, 146, 0, 230);
|
||||
private static final Color HP_LOW = new Color(225, 35, 0, 125);
|
||||
|
||||
private final Client client;
|
||||
private final BarbarianAssaultPlugin plugin;
|
||||
private final BarbarianAssaultConfig config;
|
||||
|
||||
@Inject
|
||||
private HealerOverlay(Client client, BarbarianAssaultPlugin plugin, BarbarianAssaultConfig config)
|
||||
{
|
||||
super(plugin);
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.UNDER_WIDGETS);
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "B.A. overlay"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
Round round = plugin.getCurrentRound();
|
||||
if (round == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Role role = round.getRoundRole();
|
||||
if (config.showHealerBars() && role == Role.HEALER)
|
||||
{
|
||||
for (HealerTeam teammate : HealerTeam.values())
|
||||
{
|
||||
Widget widget = client.getWidget(teammate.getTeammate());
|
||||
if (widget == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] teammateHealth = widget.getText().split(" / ");
|
||||
int curHealth = Integer.parseInt(teammateHealth[0]);
|
||||
int maxHealth = Integer.parseInt(teammateHealth[1]);
|
||||
|
||||
int width = teammate.getWidth();
|
||||
double hpRatio = (double) curHealth / maxHealth;
|
||||
int filledWidth = getBarWidth(hpRatio, width);
|
||||
Color barColor = getBarColor(hpRatio);
|
||||
|
||||
int offsetX = teammate.getOffsetX();
|
||||
int offsetY = teammate.getOffsetY();
|
||||
int x = widget.getCanvasLocation().getX() - offsetX;
|
||||
int y = widget.getCanvasLocation().getY() - offsetY;
|
||||
|
||||
graphics.setColor(barColor);
|
||||
graphics.fillRect(x, y, filledWidth, 20);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int getBarWidth(double ratio, int size)
|
||||
{
|
||||
if (ratio >= 1)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
return (int) Math.round(ratio * size);
|
||||
}
|
||||
|
||||
private Color getBarColor(double ratio)
|
||||
{
|
||||
if (ratio <= 0.33)
|
||||
{
|
||||
return HP_LOW;
|
||||
}
|
||||
|
||||
if (ratio <= 0.66)
|
||||
{
|
||||
return HP_MID;
|
||||
}
|
||||
|
||||
return HP_HIGH;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Cameron <https://github.com/noremac201>
|
||||
* 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.barbarianassault;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
enum Role
|
||||
{
|
||||
ATTACKER(WidgetInfo.BA_ATK_LISTEN_TEXT, WidgetInfo.BA_ATK_CALL_TEXT, WidgetInfo.BA_ATK_ROLE_TEXT, WidgetInfo.BA_ATK_ROLE_SPRITE),
|
||||
DEFENDER(WidgetInfo.BA_DEF_LISTEN_TEXT, WidgetInfo.BA_DEF_CALL_TEXT, WidgetInfo.BA_DEF_ROLE_TEXT, WidgetInfo.BA_DEF_ROLE_SPRITE),
|
||||
COLLECTOR(WidgetInfo.BA_COLL_LISTEN_TEXT, WidgetInfo.BA_COLL_CALL_TEXT, WidgetInfo.BA_COLL_ROLE_TEXT, WidgetInfo.BA_COLL_ROLE_SPRITE),
|
||||
HEALER(WidgetInfo.BA_HEAL_LISTEN_TEXT, WidgetInfo.BA_HEAL_CALL_TEXT, WidgetInfo.BA_HEAL_ROLE_TEXT, WidgetInfo.BA_HEAL_ROLE_SPRITE);
|
||||
|
||||
private final WidgetInfo listen;
|
||||
private final WidgetInfo call;
|
||||
private final WidgetInfo roleText;
|
||||
private final WidgetInfo roleSprite;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return name();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Cameron <https://github.com/noremac201>
|
||||
* 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.barbarianassault;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import javax.inject.Inject;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import static net.runelite.client.util.RSTimeUnit.GAME_TICKS;
|
||||
|
||||
class Round
|
||||
{
|
||||
private final Instant roundStartTime;
|
||||
@Getter
|
||||
private final Role roundRole;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean runnersKilled;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean rangersKilled;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean healersKilled;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean fightersKilled;
|
||||
|
||||
@Inject
|
||||
public Round(@NonNull Role role)
|
||||
{
|
||||
this.roundRole = role;
|
||||
this.roundStartTime = Instant.now().plus(Duration.of(2, GAME_TICKS));
|
||||
}
|
||||
|
||||
public int getTimeToChange()
|
||||
{
|
||||
return 30 + ((int) Duration.between(Instant.now(), roundStartTime).getSeconds() % 30);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Cameron <https://github.com/noremac201>
|
||||
* 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.barbarianassault;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
|
||||
import net.runelite.client.ui.overlay.OverlayMenuEntry;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
class TimerOverlay extends Overlay
|
||||
{
|
||||
private final Client client;
|
||||
private final BarbarianAssaultPlugin plugin;
|
||||
private final BarbarianAssaultConfig config;
|
||||
|
||||
@Inject
|
||||
private TimerOverlay(Client client, BarbarianAssaultPlugin plugin, BarbarianAssaultConfig config)
|
||||
{
|
||||
super(plugin);
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_WIDGETS);
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "B.A. overlay"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
Round round = plugin.getCurrentRound();
|
||||
if (round == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Role role = round.getRoundRole();
|
||||
Widget roleText = client.getWidget(role.getRoleText());
|
||||
Widget roleSprite = client.getWidget(role.getRoleSprite());
|
||||
|
||||
if (config.showTimer() && roleText != null && roleSprite != null)
|
||||
{
|
||||
roleText.setText(String.format("00:%02d", round.getTimeToChange()));
|
||||
Rectangle spriteBounds = roleSprite.getBounds();
|
||||
roleSprite.setHidden(true);
|
||||
graphics.drawImage(plugin.getClockImage(), spriteBounds.x, spriteBounds.y, null);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Abex
|
||||
* 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.camera;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.Range;
|
||||
|
||||
@ConfigGroup("zoom") // using the old plugin's group name
|
||||
public interface CameraConfig extends Config
|
||||
{
|
||||
int OUTER_LIMIT_MIN = -400;
|
||||
int OUTER_LIMIT_MAX = 400;
|
||||
/**
|
||||
* The largest (most zoomed in) value that can be used without the client crashing.
|
||||
*
|
||||
* Larger values trigger an overflow in the engine's fov to scale code.
|
||||
*/
|
||||
int INNER_ZOOM_LIMIT = 1004;
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "inner",
|
||||
name = "Expand inner zoom limit",
|
||||
description = "Configures whether or not the inner zoom limit is reduced",
|
||||
position = 1
|
||||
)
|
||||
default boolean innerLimit()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Range(
|
||||
min = OUTER_LIMIT_MIN,
|
||||
max = OUTER_LIMIT_MAX
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "outerLimit",
|
||||
name = "Expand outer zoom limit",
|
||||
description = "Configures how much the outer zoom limit is adjusted",
|
||||
position = 2
|
||||
)
|
||||
default int outerLimit()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "relaxCameraPitch",
|
||||
name = "Vertical camera",
|
||||
description = "Relax the camera's upper pitch limit",
|
||||
position = 3
|
||||
)
|
||||
default boolean relaxCameraPitch()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "controlFunction",
|
||||
name = "Control Function",
|
||||
description = "Configures the zoom function when control is pressed",
|
||||
position = 4
|
||||
)
|
||||
default ControlFunction controlFunction()
|
||||
{
|
||||
return ControlFunction.NONE;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "ctrlZoomValue",
|
||||
name = "Reset zoom position",
|
||||
description = "Position of zoom when it is reset",
|
||||
position = 5
|
||||
)
|
||||
@Range(
|
||||
min = OUTER_LIMIT_MIN,
|
||||
max = INNER_ZOOM_LIMIT
|
||||
)
|
||||
default int ctrlZoomValue()
|
||||
{
|
||||
return 512;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "zoomIncrement",
|
||||
name = "Zoom Speed",
|
||||
description = "Speed of zoom",
|
||||
position = 6
|
||||
)
|
||||
default int zoomIncrement()
|
||||
{
|
||||
return 25;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "rightClickMovesCamera",
|
||||
name = "Right click moves camera",
|
||||
description = "Remaps right click to middle mouse click if there are no menu options",
|
||||
position = 7
|
||||
)
|
||||
default boolean rightClickMovesCamera()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "ignoreExamine",
|
||||
name = "Ignore Examine",
|
||||
description = "Ignore the Examine menu entry",
|
||||
position = 8
|
||||
)
|
||||
default boolean ignoreExamine()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "middleClickMenu",
|
||||
name = "Middle-button opens menu",
|
||||
description = "Middle-mouse button always opens the menu",
|
||||
position = 9
|
||||
)
|
||||
default boolean middleClickMenu()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "compassLook",
|
||||
name = "Compass options",
|
||||
description = "Adds Look South, East, and West options to the compass",
|
||||
position = 10
|
||||
)
|
||||
default boolean compassLook()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "invertYaw",
|
||||
name = "Invert Yaw",
|
||||
description = "Makes moving the camera horizontally with the mouse backwards",
|
||||
position = 11
|
||||
)
|
||||
default boolean invertYaw()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "invertPitch",
|
||||
name = "Invert Pitch",
|
||||
description = "Makes moving the camera vertically with the mouse backwards",
|
||||
position = 12
|
||||
)
|
||||
default boolean invertPitch()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,529 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Abex
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* Copyright (c) 2019, Wynadorn <https://github.com/Wynadorn>
|
||||
* 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.camera;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Arrays;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.SwingUtilities;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.MenuAction;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.ScriptID;
|
||||
import net.runelite.api.SettingID;
|
||||
import net.runelite.api.VarClientInt;
|
||||
import net.runelite.api.VarPlayer;
|
||||
import net.runelite.api.events.BeforeRender;
|
||||
import net.runelite.api.events.ClientTick;
|
||||
import net.runelite.api.events.FocusChanged;
|
||||
import net.runelite.api.events.MenuEntryAdded;
|
||||
import net.runelite.api.events.ScriptCallbackEvent;
|
||||
import net.runelite.api.events.ScriptPreFired;
|
||||
import net.runelite.api.events.WidgetLoaded;
|
||||
import net.runelite.api.widgets.JavaScriptCallback;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.input.KeyListener;
|
||||
import net.runelite.client.input.KeyManager;
|
||||
import net.runelite.client.input.MouseListener;
|
||||
import net.runelite.client.input.MouseManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.tooltip.Tooltip;
|
||||
import net.runelite.client.ui.overlay.tooltip.TooltipManager;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Camera",
|
||||
description = "Expands zoom limit, provides vertical camera, and remaps mouse input keys",
|
||||
tags = {"zoom", "limit", "vertical", "click", "mouse"},
|
||||
enabledByDefault = false
|
||||
)
|
||||
public class CameraPlugin extends Plugin implements KeyListener, MouseListener
|
||||
{
|
||||
private static final int DEFAULT_ZOOM_INCREMENT = 25;
|
||||
private static final int DEFAULT_OUTER_ZOOM_LIMIT = 128;
|
||||
static final int DEFAULT_INNER_ZOOM_LIMIT = 896;
|
||||
|
||||
private static final String LOOK_NORTH = "Look North";
|
||||
private static final String LOOK_SOUTH = "Look South";
|
||||
private static final String LOOK_EAST = "Look East";
|
||||
private static final String LOOK_WEST = "Look West";
|
||||
|
||||
private boolean controlDown;
|
||||
// flags used to store the mousedown states
|
||||
private boolean rightClick;
|
||||
private boolean middleClick;
|
||||
/**
|
||||
* Whether or not the current menu has any non-ignored menu entries
|
||||
*/
|
||||
private boolean menuHasEntries;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private CameraConfig config;
|
||||
|
||||
@Inject
|
||||
private KeyManager keyManager;
|
||||
|
||||
@Inject
|
||||
private MouseManager mouseManager;
|
||||
|
||||
@Inject
|
||||
private TooltipManager tooltipManager;
|
||||
|
||||
private Tooltip sliderTooltip;
|
||||
|
||||
@Provides
|
||||
CameraConfig getConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(CameraConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp()
|
||||
{
|
||||
rightClick = false;
|
||||
middleClick = false;
|
||||
menuHasEntries = false;
|
||||
copyConfigs();
|
||||
keyManager.registerKeyListener(this);
|
||||
mouseManager.registerMouseListener(this);
|
||||
clientThread.invoke(() ->
|
||||
{
|
||||
Widget sideSlider = client.getWidget(WidgetInfo.SETTINGS_SIDE_CAMERA_ZOOM_SLIDER_TRACK);
|
||||
if (sideSlider != null)
|
||||
{
|
||||
addZoomTooltip(sideSlider);
|
||||
}
|
||||
|
||||
Widget settingsInit = client.getWidget(WidgetInfo.SETTINGS_INIT);
|
||||
if (settingsInit != null)
|
||||
{
|
||||
//TODO: Implement client.createScriptEvent
|
||||
//client.createScriptEvent(settingsInit.getOnLoadListener())
|
||||
//.setSource(settingsInit)
|
||||
//.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
client.setCameraPitchRelaxerEnabled(false);
|
||||
client.setInvertYaw(false);
|
||||
client.setInvertPitch(false);
|
||||
keyManager.unregisterKeyListener(this);
|
||||
mouseManager.unregisterMouseListener(this);
|
||||
controlDown = false;
|
||||
|
||||
clientThread.invoke(() ->
|
||||
{
|
||||
Widget sideSlider = client.getWidget(WidgetInfo.SETTINGS_SIDE_CAMERA_ZOOM_SLIDER_TRACK);
|
||||
if (sideSlider != null)
|
||||
{
|
||||
sideSlider.setOnMouseRepeatListener((Object[]) null);
|
||||
}
|
||||
|
||||
Widget settingsInit = client.getWidget(WidgetInfo.SETTINGS_INIT);
|
||||
if (settingsInit != null)
|
||||
{
|
||||
//TODO: Implement client.createScriptEvent
|
||||
//client.createScriptEvent(settingsInit.getOnLoadListener())
|
||||
//.setSource(settingsInit)
|
||||
//.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void copyConfigs()
|
||||
{
|
||||
client.setCameraPitchRelaxerEnabled(config.relaxCameraPitch());
|
||||
client.setInvertYaw(config.invertYaw());
|
||||
client.setInvertPitch(config.invertPitch());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMenuEntryAdded(MenuEntryAdded menuEntryAdded)
|
||||
{
|
||||
if (menuEntryAdded.getType() == MenuAction.CC_OP.getId() && menuEntryAdded.getOption().equals(LOOK_NORTH) && config.compassLook())
|
||||
{
|
||||
MenuEntry[] menuEntries = client.getMenuEntries();
|
||||
int len = menuEntries.length;
|
||||
MenuEntry north = menuEntries[len - 1];
|
||||
|
||||
menuEntries = Arrays.copyOf(menuEntries, len + 3);
|
||||
|
||||
// The handling for these entries is done in ToplevelCompassOp.rs2asm
|
||||
menuEntries[--len] = createCameraLookEntry(menuEntryAdded, 4, LOOK_WEST);
|
||||
menuEntries[++len] = createCameraLookEntry(menuEntryAdded, 3, LOOK_EAST);
|
||||
menuEntries[++len] = createCameraLookEntry(menuEntryAdded, 2, LOOK_SOUTH);
|
||||
menuEntries[++len] = north;
|
||||
|
||||
client.setMenuEntries(menuEntries);
|
||||
}
|
||||
}
|
||||
|
||||
private MenuEntry createCameraLookEntry(MenuEntryAdded lookNorth, int identifier, String option)
|
||||
{
|
||||
MenuEntry m = new MenuEntry();
|
||||
m.setOption(option);
|
||||
m.setTarget(lookNorth.getTarget());
|
||||
m.setIdentifier(identifier);
|
||||
m.setType(MenuAction.CC_OP.getId());
|
||||
m.setParam0(lookNorth.getActionParam0());
|
||||
m.setParam1(lookNorth.getActionParam1());
|
||||
return m;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onScriptCallbackEvent(ScriptCallbackEvent event)
|
||||
{
|
||||
if (client.getIndexScripts().isOverlayOutdated())
|
||||
{
|
||||
// if any cache overlay fails to load then assume at least one of the zoom scripts is outdated
|
||||
// and prevent zoom extending entirely.
|
||||
return;
|
||||
}
|
||||
|
||||
int[] intStack = client.getIntStack();
|
||||
int intStackSize = client.getIntStackSize();
|
||||
|
||||
if (!controlDown && "scrollWheelZoom".equals(event.getEventName()) && config.controlFunction() == ControlFunction.CONTROL_TO_ZOOM)
|
||||
{
|
||||
intStack[intStackSize - 1] = 1;
|
||||
}
|
||||
|
||||
if ("innerZoomLimit".equals(event.getEventName()) && config.innerLimit())
|
||||
{
|
||||
intStack[intStackSize - 1] = CameraConfig.INNER_ZOOM_LIMIT;
|
||||
return;
|
||||
}
|
||||
|
||||
if ("outerZoomLimit".equals(event.getEventName()))
|
||||
{
|
||||
int outerLimit = Ints.constrainToRange(config.outerLimit(), CameraConfig.OUTER_LIMIT_MIN, CameraConfig.OUTER_LIMIT_MAX);
|
||||
int outerZoomLimit = DEFAULT_OUTER_ZOOM_LIMIT - outerLimit;
|
||||
intStack[intStackSize - 1] = outerZoomLimit;
|
||||
return;
|
||||
}
|
||||
|
||||
if ("scrollWheelZoomIncrement".equals(event.getEventName()) && config.zoomIncrement() != DEFAULT_ZOOM_INCREMENT)
|
||||
{
|
||||
intStack[intStackSize - 1] = config.zoomIncrement();
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.innerLimit())
|
||||
{
|
||||
// This lets the options panel's slider have an exponential rate
|
||||
final double exponent = 2.d;
|
||||
switch (event.getEventName())
|
||||
{
|
||||
case "zoomLinToExp":
|
||||
{
|
||||
double range = intStack[intStackSize - 1];
|
||||
double value = intStack[intStackSize - 2];
|
||||
value = Math.pow(value / range, exponent) * range;
|
||||
intStack[intStackSize - 2] = (int) value;
|
||||
break;
|
||||
}
|
||||
case "zoomExpToLin":
|
||||
{
|
||||
double range = intStack[intStackSize - 1];
|
||||
double value = intStack[intStackSize - 2];
|
||||
value = Math.pow(value / range, 1.d / exponent) * range;
|
||||
intStack[intStackSize - 2] = (int) value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onFocusChanged(FocusChanged event)
|
||||
{
|
||||
if (!event.isFocused())
|
||||
{
|
||||
controlDown = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged ev)
|
||||
{
|
||||
copyConfigs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e)
|
||||
{
|
||||
if (e.getKeyCode() == KeyEvent.VK_CONTROL)
|
||||
{
|
||||
controlDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e)
|
||||
{
|
||||
if (e.getKeyCode() == KeyEvent.VK_CONTROL)
|
||||
{
|
||||
controlDown = false;
|
||||
|
||||
if (config.controlFunction() == ControlFunction.CONTROL_TO_RESET)
|
||||
{
|
||||
final int zoomValue = Ints.constrainToRange(config.ctrlZoomValue(), CameraConfig.OUTER_LIMIT_MIN, CameraConfig.INNER_ZOOM_LIMIT);
|
||||
clientThread.invokeLater(() -> client.runScript(ScriptID.CAMERA_DO_ZOOM, zoomValue, zoomValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the menu has any non-ignored entries
|
||||
*/
|
||||
private boolean hasMenuEntries(MenuEntry[] menuEntries)
|
||||
{
|
||||
for (MenuEntry menuEntry : menuEntries)
|
||||
{
|
||||
MenuAction action = MenuAction.of(menuEntry.getType());
|
||||
switch (action)
|
||||
{
|
||||
case CANCEL:
|
||||
case WALK:
|
||||
break;
|
||||
case EXAMINE_OBJECT:
|
||||
case EXAMINE_NPC:
|
||||
case EXAMINE_ITEM_GROUND:
|
||||
case EXAMINE_ITEM:
|
||||
case CC_OP_LOW_PRIORITY:
|
||||
if (config.ignoreExamine())
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the menu has any options, because menu entries are built each
|
||||
* tick and the MouseListener runs on the awt thread
|
||||
*/
|
||||
@Subscribe
|
||||
public void onClientTick(ClientTick event)
|
||||
{
|
||||
menuHasEntries = hasMenuEntries(client.getMenuEntries());
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onScriptPreFired(ScriptPreFired ev)
|
||||
{
|
||||
if (ev.getScriptId() == ScriptID.SETTINGS_SLIDER_CHOOSE_ONOP)
|
||||
{
|
||||
int arg = client.getIntStackSize() - 7;
|
||||
int[] is = client.getIntStack();
|
||||
|
||||
if (is[arg] == SettingID.CAMERA_ZOOM)
|
||||
{
|
||||
//TODO: Implement client.createScriptEvent
|
||||
//addZoomTooltip(client.getScriptActiveWidget());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onWidgetLoaded(WidgetLoaded ev)
|
||||
{
|
||||
if (ev.getGroupId() == WidgetID.SETTINGS_SIDE_GROUP_ID)
|
||||
{
|
||||
addZoomTooltip(client.getWidget(WidgetInfo.SETTINGS_SIDE_CAMERA_ZOOM_SLIDER_TRACK));
|
||||
}
|
||||
}
|
||||
|
||||
private void addZoomTooltip(Widget w)
|
||||
{
|
||||
w.setOnMouseRepeatListener((JavaScriptCallback) ev ->
|
||||
{
|
||||
int value = client.getVar(VarClientInt.CAMERA_ZOOM_RESIZABLE_VIEWPORT);
|
||||
int max = config.innerLimit() ? config.INNER_ZOOM_LIMIT : CameraPlugin.DEFAULT_INNER_ZOOM_LIMIT;
|
||||
sliderTooltip = new Tooltip("Camera Zoom: " + value + " / " + max);
|
||||
});
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onBeforeRender(BeforeRender ev)
|
||||
{
|
||||
if (sliderTooltip != null)
|
||||
{
|
||||
tooltipManager.add(sliderTooltip);
|
||||
sliderTooltip = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The event that is triggered when a mouse button is pressed
|
||||
* In this method the right click is changed to a middle-click to enable rotating the camera
|
||||
* <p>
|
||||
* This method also provides the config option to enable the middle-mouse button to always open the right click menu
|
||||
*/
|
||||
@Override
|
||||
public MouseEvent mousePressed(MouseEvent mouseEvent)
|
||||
{
|
||||
if (SwingUtilities.isRightMouseButton(mouseEvent) && config.rightClickMovesCamera())
|
||||
{
|
||||
boolean oneButton = client.getVar(VarPlayer.MOUSE_BUTTONS) == 1;
|
||||
// Only move the camera if there is nothing at the menu, or if
|
||||
// in one-button mode. In one-button mode, left and right click always do the same thing,
|
||||
// so always treat it as the menu is empty
|
||||
if (!menuHasEntries || oneButton)
|
||||
{
|
||||
// Set the rightClick flag to true so we can release the button in mouseReleased() later
|
||||
rightClick = true;
|
||||
// Change the mousePressed() MouseEvent to the middle mouse button
|
||||
mouseEvent = new MouseEvent((java.awt.Component) mouseEvent.getSource(),
|
||||
mouseEvent.getID(),
|
||||
mouseEvent.getWhen(),
|
||||
mouseEvent.getModifiersEx(),
|
||||
mouseEvent.getX(),
|
||||
mouseEvent.getY(),
|
||||
mouseEvent.getClickCount(),
|
||||
mouseEvent.isPopupTrigger(),
|
||||
MouseEvent.BUTTON2);
|
||||
}
|
||||
}
|
||||
else if (SwingUtilities.isMiddleMouseButton((mouseEvent)) && config.middleClickMenu())
|
||||
{
|
||||
// Set the middleClick flag to true so we can release it later in mouseReleased()
|
||||
middleClick = true;
|
||||
// Chance the middle mouse button MouseEvent to a right-click
|
||||
mouseEvent = new MouseEvent((java.awt.Component) mouseEvent.getSource(),
|
||||
mouseEvent.getID(),
|
||||
mouseEvent.getWhen(),
|
||||
mouseEvent.getModifiersEx(),
|
||||
mouseEvent.getX(),
|
||||
mouseEvent.getY(),
|
||||
mouseEvent.getClickCount(),
|
||||
mouseEvent.isPopupTrigger(),
|
||||
MouseEvent.BUTTON3);
|
||||
}
|
||||
return mouseEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the MouseEvent to release the correct button
|
||||
*/
|
||||
@Override
|
||||
public MouseEvent mouseReleased(MouseEvent mouseEvent)
|
||||
{
|
||||
if (rightClick)
|
||||
{
|
||||
rightClick = false;
|
||||
// Change the MouseEvent to button 2 so the middle mouse button will be released
|
||||
mouseEvent = new MouseEvent((java.awt.Component) mouseEvent.getSource(),
|
||||
mouseEvent.getID(),
|
||||
mouseEvent.getWhen(),
|
||||
mouseEvent.getModifiersEx(),
|
||||
mouseEvent.getX(),
|
||||
mouseEvent.getY(),
|
||||
mouseEvent.getClickCount(),
|
||||
mouseEvent.isPopupTrigger(),
|
||||
MouseEvent.BUTTON2);
|
||||
|
||||
}
|
||||
if (middleClick)
|
||||
{
|
||||
middleClick = false;
|
||||
// Change the MouseEvent ot button 3 so the right mouse button will be released
|
||||
mouseEvent = new MouseEvent((java.awt.Component) mouseEvent.getSource(),
|
||||
mouseEvent.getID(),
|
||||
mouseEvent.getWhen(),
|
||||
mouseEvent.getModifiersEx(),
|
||||
mouseEvent.getX(),
|
||||
mouseEvent.getY(),
|
||||
mouseEvent.getClickCount(),
|
||||
mouseEvent.isPopupTrigger(),
|
||||
MouseEvent.BUTTON3);
|
||||
}
|
||||
return mouseEvent;
|
||||
}
|
||||
|
||||
/*
|
||||
* These methods are unused but required to be present in a MouseListener implementation
|
||||
*/
|
||||
// region Unused MouseListener methods
|
||||
@Override
|
||||
public MouseEvent mouseDragged(MouseEvent mouseEvent)
|
||||
{
|
||||
return mouseEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MouseEvent mouseMoved(MouseEvent mouseEvent)
|
||||
{
|
||||
return mouseEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MouseEvent mouseClicked(MouseEvent mouseEvent)
|
||||
{
|
||||
return mouseEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MouseEvent mouseEntered(MouseEvent mouseEvent)
|
||||
{
|
||||
return mouseEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MouseEvent mouseExited(MouseEvent mouseEvent)
|
||||
{
|
||||
return mouseEvent;
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Jacob M <https://github.com/jacoblairm>
|
||||
* 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 HOLDER 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.camera;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ControlFunction
|
||||
{
|
||||
NONE("None"),
|
||||
CONTROL_TO_ZOOM("Hold to zoom"),
|
||||
CONTROL_TO_RESET("Reset zoom");
|
||||
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <http://github.com/sethtroll>
|
||||
* 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.cluescrolls;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("cluescroll")
|
||||
public interface ClueScrollConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "displayHintArrows",
|
||||
name = "Display hint arrows",
|
||||
description = "Configures whether or not to display hint arrows for clues"
|
||||
)
|
||||
default boolean displayHintArrows()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.ClueScroll;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.EmoteClue;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
class ClueScrollEmoteOverlay extends Overlay
|
||||
{
|
||||
private final ClueScrollPlugin plugin;
|
||||
private final Client client;
|
||||
|
||||
private boolean hasScrolled;
|
||||
|
||||
@Inject
|
||||
private ClueScrollEmoteOverlay(ClueScrollPlugin plugin, Client client)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_WIDGETS);
|
||||
this.plugin = plugin;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
ClueScroll clue = plugin.getClue();
|
||||
|
||||
if (!(clue instanceof EmoteClue))
|
||||
{
|
||||
hasScrolled = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
EmoteClue emoteClue = (EmoteClue) clue;
|
||||
|
||||
if (!emoteClue.getFirstEmote().hasSprite())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget emoteContainer = client.getWidget(WidgetInfo.EMOTE_CONTAINER);
|
||||
|
||||
if (emoteContainer == null || emoteContainer.isHidden())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget emoteWindow = client.getWidget(WidgetInfo.EMOTE_WINDOW);
|
||||
|
||||
if (emoteWindow == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget firstEmoteWidget = null;
|
||||
Widget secondEmoteWidget = null;
|
||||
|
||||
for (Widget emoteWidget : emoteContainer.getDynamicChildren())
|
||||
{
|
||||
if (emoteWidget.getSpriteId() == emoteClue.getFirstEmote().getSpriteId())
|
||||
{
|
||||
firstEmoteWidget = emoteWidget;
|
||||
plugin.highlightWidget(graphics, emoteWidget, emoteWindow, null,
|
||||
emoteClue.getSecondEmote() != null ? "1st" : null);
|
||||
}
|
||||
else if (emoteClue.getSecondEmote() != null
|
||||
&& emoteWidget.getSpriteId() == emoteClue.getSecondEmote().getSpriteId())
|
||||
{
|
||||
secondEmoteWidget = emoteWidget;
|
||||
plugin.highlightWidget(graphics, emoteWidget, emoteWindow, null, "2nd");
|
||||
}
|
||||
}
|
||||
if (!hasScrolled)
|
||||
{
|
||||
hasScrolled = true;
|
||||
plugin.scrollToWidget(WidgetInfo.EMOTE_CONTAINER, WidgetInfo.EMOTE_SCROLLBAR, firstEmoteWidget, secondEmoteWidget);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.cluescrolls;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.ClueScroll;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.MusicClue;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
class ClueScrollMusicOverlay extends Overlay
|
||||
{
|
||||
private static final Rectangle PADDING = new Rectangle(2, 1, 0, 1);
|
||||
|
||||
private final ClueScrollPlugin plugin;
|
||||
private final Client client;
|
||||
|
||||
private boolean hasScrolled;
|
||||
|
||||
@Inject
|
||||
private ClueScrollMusicOverlay(ClueScrollPlugin plugin, Client client)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_WIDGETS);
|
||||
this.plugin = plugin;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
ClueScroll clue = plugin.getClue();
|
||||
|
||||
if (!(clue instanceof MusicClue))
|
||||
{
|
||||
hasScrolled = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
MusicClue musicClue = (MusicClue) clue;
|
||||
|
||||
Widget musicContainer = client.getWidget(WidgetInfo.MUSIC_WINDOW);
|
||||
|
||||
if (musicContainer == null || musicContainer.isHidden())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget trackList = client.getWidget(WidgetInfo.MUSIC_TRACK_LIST);
|
||||
String trackToFind = musicClue.getSong();
|
||||
Widget found = null;
|
||||
|
||||
if (trackList == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Widget track : trackList.getDynamicChildren())
|
||||
{
|
||||
if (track.getText().equals(trackToFind))
|
||||
{
|
||||
found = track;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!hasScrolled)
|
||||
{
|
||||
hasScrolled = true;
|
||||
plugin.scrollToWidget(WidgetInfo.MUSIC_TRACK_LIST, WidgetInfo.MUSIC_TRACK_SCROLLBAR, found);
|
||||
}
|
||||
plugin.highlightWidget(graphics, found, trackList, PADDING, null);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Seth <Sethtroll3@gmail.com>
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* Copyright (c) 2019, David <https://github.com/drahenshaw>
|
||||
* 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 HOLDER 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.cluescrolls;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Item;
|
||||
import static net.runelite.api.ItemID.*;
|
||||
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY;
|
||||
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.ClueScroll;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.AnyRequirementCollection;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirements.item;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.SingleItemRequirement;
|
||||
import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
|
||||
import net.runelite.client.ui.overlay.OverlayMenuEntry;
|
||||
import net.runelite.client.ui.overlay.OverlayPanel;
|
||||
import net.runelite.client.ui.overlay.OverlayPriority;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
|
||||
public class ClueScrollOverlay extends OverlayPanel
|
||||
{
|
||||
private static final ItemRequirement HAS_SPADE = new SingleItemRequirement(SPADE);
|
||||
private static final ItemRequirement HAS_LIGHT = new AnyRequirementCollection("Light Source",
|
||||
item(LIT_TORCH),
|
||||
item(LIT_CANDLE),
|
||||
item(LIT_BLACK_CANDLE),
|
||||
item(CANDLE_LANTERN_4531),
|
||||
item(CANDLE_LANTERN_4534), // lit black candle lantern
|
||||
item(OIL_LAMP_4524),
|
||||
item(OIL_LANTERN_4539),
|
||||
item(BULLSEYE_LANTERN_4550),
|
||||
item(SAPPHIRE_LANTERN_4702),
|
||||
item(EMERALD_LANTERN_9065),
|
||||
item(MINING_HELMET),
|
||||
item(FIREMAKING_CAPE),
|
||||
item(FIREMAKING_CAPET),
|
||||
item(KANDARIN_HEADGEAR_1),
|
||||
item(KANDARIN_HEADGEAR_2),
|
||||
item(KANDARIN_HEADGEAR_3),
|
||||
item(KANDARIN_HEADGEAR_4),
|
||||
item(BRUMA_TORCH),
|
||||
item(MAX_CAPE),
|
||||
item(MAX_CAPE_13342));
|
||||
|
||||
public static final Color TITLED_CONTENT_COLOR = new Color(190, 190, 190);
|
||||
|
||||
private final ClueScrollPlugin plugin;
|
||||
private final Client client;
|
||||
|
||||
@Inject
|
||||
private ClueScrollOverlay(ClueScrollPlugin plugin, Client client)
|
||||
{
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
this.client = client;
|
||||
setPriority(OverlayPriority.LOW);
|
||||
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Clue Scroll overlay"));
|
||||
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY, "Reset", "Clue Scroll overlay"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
ClueScroll clue = plugin.getClue();
|
||||
|
||||
if (clue == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
clue.makeOverlayHint(panelComponent, plugin);
|
||||
|
||||
final Item[] inventoryItems = plugin.getInventoryItems();
|
||||
final Item[] equippedItems = plugin.getEquippedItems();
|
||||
|
||||
if (clue.isRequiresSpade() && inventoryItems != null)
|
||||
{
|
||||
if (!HAS_SPADE.fulfilledBy(inventoryItems))
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Requires Spade!").leftColor(Color.RED).build());
|
||||
}
|
||||
}
|
||||
|
||||
if (clue.isRequiresLight()
|
||||
&& ((clue.getHasFirePit() == null || client.getVar(clue.getHasFirePit()) != 1)
|
||||
&& (inventoryItems == null || !HAS_LIGHT.fulfilledBy(inventoryItems))
|
||||
&& (equippedItems == null || !HAS_LIGHT.fulfilledBy(equippedItems))))
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Requires Light Source!").leftColor(Color.RED).build());
|
||||
}
|
||||
|
||||
if (clue.getEnemy() != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(clue.getEnemy().getText())
|
||||
.leftColor(Color.YELLOW)
|
||||
.build());
|
||||
}
|
||||
|
||||
return super.render(graphics);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* 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.cluescrolls;
|
||||
|
||||
import net.runelite.client.plugins.cluescrolls.clues.ClueScroll;
|
||||
|
||||
public interface ClueScrollService
|
||||
{
|
||||
/**
|
||||
* Get the clue scroll
|
||||
*
|
||||
* @return ClueScroll
|
||||
*/
|
||||
ClueScroll getClue();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Ron Young <https://github.com/raiyni>
|
||||
* 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.cluescrolls;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.ClueScroll;
|
||||
|
||||
@Singleton
|
||||
class ClueScrollServiceImpl implements ClueScrollService
|
||||
{
|
||||
private final ClueScrollPlugin plugin;
|
||||
|
||||
@Inject
|
||||
private ClueScrollServiceImpl(ClueScrollPlugin plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClueScroll getClue()
|
||||
{
|
||||
return plugin.getClue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Morgan Lewis <https://github.com/MESLewis>
|
||||
* 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.cluescrolls;
|
||||
|
||||
import java.awt.Graphics;
|
||||
import java.awt.image.BufferedImage;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.client.ui.overlay.worldmap.WorldMapPoint;
|
||||
|
||||
class ClueScrollWorldMapPoint extends WorldMapPoint
|
||||
{
|
||||
private final ClueScrollPlugin plugin;
|
||||
private final BufferedImage clueScrollWorldImage;
|
||||
private final Point clueScrollWorldImagePoint;
|
||||
|
||||
ClueScrollWorldMapPoint(final WorldPoint worldPoint, ClueScrollPlugin plugin)
|
||||
{
|
||||
super(worldPoint, null);
|
||||
|
||||
clueScrollWorldImage = new BufferedImage(plugin.getMapArrow().getWidth(), plugin.getMapArrow().getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics graphics = clueScrollWorldImage.getGraphics();
|
||||
graphics.drawImage(plugin.getMapArrow(), 0, 0, null);
|
||||
graphics.drawImage(plugin.getClueScrollImage(), 0, 0, null);
|
||||
clueScrollWorldImagePoint = new Point(
|
||||
clueScrollWorldImage.getWidth() / 2,
|
||||
clueScrollWorldImage.getHeight());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.setSnapToEdge(true);
|
||||
this.setJumpOnClick(true);
|
||||
this.setImage(clueScrollWorldImage);
|
||||
this.setImagePoint(clueScrollWorldImagePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEdgeSnap()
|
||||
{
|
||||
this.setImage(plugin.getClueScrollImage());
|
||||
this.setImagePoint(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEdgeUnsnap()
|
||||
{
|
||||
this.setImage(clueScrollWorldImage);
|
||||
this.setImagePoint(clueScrollWorldImagePoint);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.ClueScroll;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
public class ClueScrollWorldOverlay extends Overlay
|
||||
{
|
||||
public static final int IMAGE_Z_OFFSET = 30;
|
||||
|
||||
public static final Color CLICKBOX_BORDER_COLOR = Color.ORANGE;
|
||||
public static final Color CLICKBOX_HOVER_BORDER_COLOR = CLICKBOX_BORDER_COLOR.darker();
|
||||
public static final Color CLICKBOX_FILL_COLOR = new Color(0, 255, 0, 20);
|
||||
|
||||
private final ClueScrollPlugin plugin;
|
||||
|
||||
@Inject
|
||||
private ClueScrollWorldOverlay(ClueScrollPlugin plugin)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_SCENE);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
ClueScroll clue = plugin.getClue();
|
||||
|
||||
if (clue != null)
|
||||
{
|
||||
clue.makeWorldOverlayHint(graphics, plugin);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.ObjectID;
|
||||
import net.runelite.api.TileObject;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_BORDER_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_FILL_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_HOVER_BORDER_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
@Getter
|
||||
public class AnagramClue extends ClueScroll implements TextClueScroll, NpcClueScroll, ObjectClueScroll
|
||||
{
|
||||
private static final String ANAGRAM_TEXT = "This anagram reveals who to speak to next: ";
|
||||
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", "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", "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", "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", "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", "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(3238, 3220, 0), "Lumbridge"),
|
||||
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", "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("DARN DRAKE", "Daer Krand", new WorldPoint(3728, 3302, 0), "Sisterhood Sanctuary (Slepe Dungeon, northeast of Nightmare Arena)"),
|
||||
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", "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", "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", "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", "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", "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", "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", "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", "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", "Immenizz", 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", "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", "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", "How many items can carry water in Gielinor?", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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"),
|
||||
new AnagramClue("I CORD", "Doric", new WorldPoint(2951, 3450, 0), "North of Falador"),
|
||||
new AnagramClue("IN BAR", "Brian", new WorldPoint(3026, 3246, 0), "Port Sarim battleaxe shop"),
|
||||
new AnagramClue("RAIN COVE", "Veronica", new WorldPoint(3110, 3330, 0), "Outside Draynor Manor"),
|
||||
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", "How many graves are there in the city graveyard?", "38"),
|
||||
new AnagramClue("RUE GO", "Goreu", new WorldPoint(2335, 3162, 0), "Lletya")
|
||||
);
|
||||
|
||||
private final String text;
|
||||
private final String npc;
|
||||
private final WorldPoint location;
|
||||
private final String area;
|
||||
@Nullable
|
||||
private final String question;
|
||||
@Nullable
|
||||
private final String answer;
|
||||
private int objectId;
|
||||
|
||||
private AnagramClue(String text, String npc, WorldPoint location, String area)
|
||||
{
|
||||
this(text, npc, location, area, null, null);
|
||||
}
|
||||
|
||||
private AnagramClue(String text, String npc, WorldPoint location, String area, @Nullable String question, @Nullable 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 question, String answer)
|
||||
{
|
||||
this(text, npc, location, area, question, answer);
|
||||
this.objectId = objectId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Anagram Clue").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("NPC:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getNpc())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Location:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getArea())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
if (getAnswer() != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Answer:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getAnswer())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
if (!getLocation().isInScene(plugin.getClient()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark NPC
|
||||
if (objectId == -1 && plugin.getNpcsToMark() != null)
|
||||
{
|
||||
for (NPC npc : plugin.getNpcsToMark())
|
||||
{
|
||||
OverlayUtil.renderActorOverlayImage(graphics, npc, plugin.getClueScrollImage(), Color.ORANGE, IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark game object
|
||||
if (objectId != -1)
|
||||
{
|
||||
net.runelite.api.Point mousePosition = plugin.getClient().getMouseCanvasPosition();
|
||||
|
||||
for (TileObject gameObject : plugin.getObjectsToMark())
|
||||
{
|
||||
OverlayUtil.renderHoverableArea(graphics, gameObject.getClickbox(), mousePosition,
|
||||
CLICKBOX_FILL_COLOR, CLICKBOX_BORDER_COLOR, CLICKBOX_HOVER_BORDER_COLOR);
|
||||
|
||||
OverlayUtil.renderImageLocation(plugin.getClient(), graphics, gameObject.getLocalLocation(), plugin.getClueScrollImage(), IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static AnagramClue forText(String text)
|
||||
{
|
||||
for (AnagramClue clue : CLUES)
|
||||
{
|
||||
if (text.equalsIgnoreCase(ANAGRAM_TEXT + clue.text)
|
||||
|| text.equalsIgnoreCase(ANAGRAM_TEXT_BEGINNER + clue.text)
|
||||
|| text.equalsIgnoreCase(clue.question))
|
||||
{
|
||||
return clue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNpcs()
|
||||
{
|
||||
return new String[] {npc};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getObjectIds()
|
||||
{
|
||||
return new int[] {objectId};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
|
||||
@Getter
|
||||
public class BeginnerMapClue extends MapClue implements LocationClueScroll
|
||||
{
|
||||
private static final ImmutableList<BeginnerMapClue> CLUES = ImmutableList.of(
|
||||
new BeginnerMapClue(WidgetID.BEGINNER_CLUE_MAP_CHAMPIONS_GUILD, new WorldPoint(3166, 3361, 0), MapClue.CHAMPIONS_GUILD),
|
||||
new BeginnerMapClue(WidgetID.BEGINNER_CLUE_MAP_VARROCK_EAST_MINE, new WorldPoint(3290, 3374, 0), MapClue.VARROCK_EAST_MINE),
|
||||
new BeginnerMapClue(WidgetID.BEGINNER_CLUE_MAP_DRAYNOR, new WorldPoint(3093, 3226, 0), MapClue.SOUTH_OF_DRAYNOR_BANK),
|
||||
new BeginnerMapClue(WidgetID.BEGINNER_CLUE_MAP_NORTH_OF_FALADOR, new WorldPoint(3043, 3398, 0), MapClue.STANDING_STONES),
|
||||
new BeginnerMapClue(WidgetID.BEGINNER_CLUE_MAP_WIZARDS_TOWER, new WorldPoint(3110, 3152, 0), MapClue.WIZARDS_TOWER_DIS)
|
||||
);
|
||||
|
||||
private final int widgetGroupID;
|
||||
|
||||
private BeginnerMapClue(int widgetGroupID, WorldPoint location, String description)
|
||||
{
|
||||
super(-1, location, description);
|
||||
this.widgetGroupID = widgetGroupID;
|
||||
setRequiresSpade(true);
|
||||
}
|
||||
|
||||
// Beginner Map Clues all use the same ItemID, but the WidgetID used to display them is unique
|
||||
public static BeginnerMapClue forWidgetID(int widgetGroupID)
|
||||
{
|
||||
for (BeginnerMapClue clue : CLUES)
|
||||
{
|
||||
if (clue.widgetGroupID == widgetGroupID)
|
||||
{
|
||||
return clue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
@Getter
|
||||
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", "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("HQNM LZM STSNQ", "Iron Man tutor", 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 or in Prifddinas after Song of the Elves", "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;
|
||||
@Nullable
|
||||
private String question;
|
||||
@Nullable
|
||||
private String answer;
|
||||
|
||||
private CipherClue(String text, String npc, WorldPoint location, String area)
|
||||
{
|
||||
this(text, npc, location, area, null, null);
|
||||
}
|
||||
|
||||
private CipherClue(String text, String npc, WorldPoint location, String area, @Nullable String question, @Nullable 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Cipher Clue").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("NPC:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getNpc())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Location:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getArea())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
if (getAnswer() != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Answer:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getAnswer())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
if (!getLocation().isInScene(plugin.getClient()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.getNpcsToMark() != null)
|
||||
{
|
||||
for (NPC npc : plugin.getNpcsToMark())
|
||||
{
|
||||
OverlayUtil.renderActorOverlayImage(graphics, npc, plugin.getClueScrollImage(), Color.ORANGE, IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CipherClue forText(String text)
|
||||
{
|
||||
for (CipherClue clue : CLUES)
|
||||
{
|
||||
if (text.equalsIgnoreCase(clue.text) || text.equalsIgnoreCase(clue.question))
|
||||
{
|
||||
return clue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNpcs()
|
||||
{
|
||||
return new String[] {npc};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import java.awt.Graphics2D;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
|
||||
public abstract class ClueScroll
|
||||
{
|
||||
@Setter(AccessLevel.PROTECTED)
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
private boolean requiresSpade;
|
||||
|
||||
@Setter(AccessLevel.PROTECTED)
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
private boolean requiresLight;
|
||||
|
||||
@Setter(AccessLevel.PROTECTED)
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
private Varbits hasFirePit;
|
||||
|
||||
@Setter(AccessLevel.PROTECTED)
|
||||
@Getter(AccessLevel.PUBLIC)
|
||||
private Enemy enemy;
|
||||
|
||||
public abstract void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin);
|
||||
|
||||
public abstract void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin);
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.Enemy.*;
|
||||
|
||||
@Getter
|
||||
public class CoordinateClue extends ClueScroll implements TextClueScroll, LocationClueScroll
|
||||
{
|
||||
@Getter
|
||||
private static class CoordinateClueInfo
|
||||
{
|
||||
private final String directions;
|
||||
private final boolean lightRequired;
|
||||
private final Varbits lightSource;
|
||||
private final Enemy enemy;
|
||||
|
||||
private CoordinateClueInfo(@NonNull String directions)
|
||||
{
|
||||
this(directions, null);
|
||||
}
|
||||
|
||||
private CoordinateClueInfo(@NonNull String directions, Enemy enemy)
|
||||
{
|
||||
this.directions = directions;
|
||||
this.enemy = enemy;
|
||||
this.lightRequired = false;
|
||||
this.lightSource = null;
|
||||
}
|
||||
|
||||
private CoordinateClueInfo(@Nonnull String directions, Enemy enemy, boolean lightRequired, Varbits lightSource)
|
||||
{
|
||||
this.directions = directions;
|
||||
this.enemy = enemy;
|
||||
this.lightRequired = lightRequired;
|
||||
this.lightSource = lightSource;
|
||||
}
|
||||
}
|
||||
|
||||
private static final ImmutableMap<WorldPoint, CoordinateClueInfo> CLUES = new ImmutableMap.Builder<WorldPoint, CoordinateClueInfo>()
|
||||
// Medium
|
||||
.put(new WorldPoint(2479, 3158, 0), new CoordinateClueInfo("South of fruit tree patch, west of Tree Gnome Village."))
|
||||
.put(new WorldPoint(2887, 3154, 0), new CoordinateClueInfo("West of Banana plantation on Karamja."))
|
||||
.put(new WorldPoint(2743, 3151, 0), new CoordinateClueInfo("Entrance of Brimhaven dungeon."))
|
||||
.put(new WorldPoint(3184, 3150, 0), new CoordinateClueInfo("South of Lumbridge Swamp."))
|
||||
.put(new WorldPoint(3217, 3177, 0), new CoordinateClueInfo("East of Lumbridge Swamp."))
|
||||
.put(new WorldPoint(3007, 3144, 0), new CoordinateClueInfo("Near the entrance to the Asgarnian Ice Dungeon, south of Port Sarim (AIQ)."))
|
||||
.put(new WorldPoint(2896, 3119, 0), new CoordinateClueInfo("Near Karambwan fishing spot (DKP)."))
|
||||
.put(new WorldPoint(2697, 3207, 0), new CoordinateClueInfo("Centre of Moss Giant Island, west of Brimhaven."))
|
||||
.put(new WorldPoint(2679, 3110, 0), new CoordinateClueInfo("North of Hazelmere's house (CLS)."))
|
||||
.put(new WorldPoint(3510, 3074, 0), new CoordinateClueInfo("East of Uzer (DLQ)."))
|
||||
.put(new WorldPoint(3160, 3251, 0), new CoordinateClueInfo("West of trapdoor leading to H.A.M Hideout."))
|
||||
.put(new WorldPoint(2643, 3252, 0), new CoordinateClueInfo("South of Ardougne Zoo, North of Tower of Life (DJP)."))
|
||||
.put(new WorldPoint(2322, 3061, 0), new CoordinateClueInfo("South-west of Castle wars (BKP)."))
|
||||
.put(new WorldPoint(2875, 3046, 0), new CoordinateClueInfo("North of nature altar, north of Shilo Village (CKR)."))
|
||||
.put(new WorldPoint(2849, 3033, 0), new CoordinateClueInfo("West of nature altar, north of Shilo Village (CKR)."))
|
||||
.put(new WorldPoint(2848, 3296, 0), new CoordinateClueInfo("North of Crandor island."))
|
||||
.put(new WorldPoint(2583, 2990, 0), new CoordinateClueInfo("Feldip Hills, south-east of Gu'Thanoth (AKS)."))
|
||||
.put(new WorldPoint(3179, 3344, 0), new CoordinateClueInfo("In the cow pen north of the Lumbridge windmill."))
|
||||
.put(new WorldPoint(2383, 3370, 0), new CoordinateClueInfo("West of the outpost"))
|
||||
.put(new WorldPoint(3312, 3375, 0), new CoordinateClueInfo("North-west of Exam Centre, on the hill."))
|
||||
.put(new WorldPoint(3121, 3384, 0), new CoordinateClueInfo("North-east of Draynor Manor, near River Lum."))
|
||||
.put(new WorldPoint(3430, 3388, 0), new CoordinateClueInfo("West of Mort Myre Swamp (BKR)."))
|
||||
.put(new WorldPoint(2920, 3403, 0), new CoordinateClueInfo("South-east of Taverley, near Lady of the Lake."))
|
||||
.put(new WorldPoint(2594, 2899, 0), new CoordinateClueInfo("South-east of Feldip Hills, by the crimson swifts (AKS)."))
|
||||
.put(new WorldPoint(2387, 3435, 0), new CoordinateClueInfo("West of Tree Gnome Stronghold, near the pen containing terrorbirds."))
|
||||
.put(new WorldPoint(2512, 3467, 0), new CoordinateClueInfo("Baxtorian Falls (Bring rope)."))
|
||||
.put(new WorldPoint(2381, 3468, 0), new CoordinateClueInfo("West of Tree Gnome Stronghold, north of the pen with terrorbirds."))
|
||||
.put(new WorldPoint(3005, 3475, 0), new CoordinateClueInfo("Ice Mountain, west of Edgeville Monastery."))
|
||||
.put(new WorldPoint(2585, 3505, 0), new CoordinateClueInfo("By the shore line north of the Coal Trucks."))
|
||||
.put(new WorldPoint(3443, 3515, 0), new CoordinateClueInfo("South of Slayer Tower (CKS)."))
|
||||
.put(new WorldPoint(2416, 3516, 0), new CoordinateClueInfo("Tree Gnome Stronghold, west of Grand Tree, near swamp."))
|
||||
.put(new WorldPoint(3429, 3523, 0), new CoordinateClueInfo("South of Slayer Tower (CKS)."))
|
||||
.put(new WorldPoint(2363, 3531, 0), new CoordinateClueInfo("North-east of Eagles' Peak (AKQ)."))
|
||||
.put(new WorldPoint(2919, 3535, 0), new CoordinateClueInfo("East of Burthorpe pub."))
|
||||
.put(new WorldPoint(3548, 3560, 0), new CoordinateClueInfo("Inside Fenkenstrain's Castle."))
|
||||
.put(new WorldPoint(1456, 3620, 0), new CoordinateClueInfo("Graveyard west of Shayzien (DJR)."))
|
||||
.put(new WorldPoint(2735, 3638, 0), new CoordinateClueInfo("East of Rellekka, north-west of Golden Apple Tree (AJR)."))
|
||||
.put(new WorldPoint(2681, 3653, 0), new CoordinateClueInfo("Rellekka, in the garden of the south-east house."))
|
||||
.put(new WorldPoint(2537, 3881, 0), new CoordinateClueInfo("Miscellania (CIP)."))
|
||||
.put(new WorldPoint(2828, 3234, 0), new CoordinateClueInfo("Southern coast of Crandor."))
|
||||
.put(new WorldPoint(1247, 3726, 0), new CoordinateClueInfo("Just inside the Farming Guild"))
|
||||
.put(new WorldPoint(3770, 3898, 0), new CoordinateClueInfo("On the small island north-east of Fossil Island's mushroom forest."))
|
||||
// Hard
|
||||
.put(new WorldPoint(2209, 3161, 0), new CoordinateClueInfo("North-east of Tyras Camp (BJS if 76 Agility).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2181, 3206, 0), new CoordinateClueInfo("South of Iorwerth Camp.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3081, 3209, 0), new CoordinateClueInfo("Small Island (CLP).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3399, 3246, 0), new CoordinateClueInfo("Behind the Duel Arena."))
|
||||
.put(new WorldPoint(2699, 3251, 0), new CoordinateClueInfo("Little island (AIR).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3546, 3251, 0), new CoordinateClueInfo("North-east of Burgh de Rott.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3544, 3256, 0), new CoordinateClueInfo("North-east of Burgh de Rott.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2841, 3267, 0), new CoordinateClueInfo("Crandor island.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3168, 3041, 0), new CoordinateClueInfo("Bedabin Camp.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2542, 3031, 0), new CoordinateClueInfo("Gu'Tanoth, may require 20gp.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2581, 3030, 0), new CoordinateClueInfo("Gu'Tanoth island, enter cave north-west of Feldip Hills (AKS).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2961, 3024, 0), new CoordinateClueInfo("Ship yard (DKP).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2339, 3311, 0), new CoordinateClueInfo("East of Prifddinas on Arandar mountain pass.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3440, 3341, 0), new CoordinateClueInfo("Nature Spirit's grotto (BIP).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2763, 2974, 0), new CoordinateClueInfo("Cairn Isle, west of Shilo Village (CKR).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3138, 2969, 0), new CoordinateClueInfo("West of Bandit Camp in Kharidian Desert.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2924, 2963, 0), new CoordinateClueInfo("On the southern part of eastern Karamja.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2838, 2914, 0), new CoordinateClueInfo("Kharazi Jungle, near water pool (CKR).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3441, 3419, 0), new CoordinateClueInfo("Mort Myre Swamp (BKR).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2950, 2902, 0), new CoordinateClueInfo("South-east of Kharazi Jungle.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2775, 2891, 0), new CoordinateClueInfo("South-west of Kharazi Jungle.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3113, 3602, 0), new CoordinateClueInfo("Wilderness. North of Edgeville (level 11).", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(2892, 3675, 0), new CoordinateClueInfo("On the summit of Trollheim.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3168, 3677, 0), new CoordinateClueInfo("Wilderness. Graveyard of Shadows.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(2853, 3690, 0), new CoordinateClueInfo("Entrance to the troll Stronghold.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3305, 3692, 0), new CoordinateClueInfo("Wilderness. West of eastern green dragon.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3055, 3696, 0), new CoordinateClueInfo("Wilderness. Bandit Camp.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3302, 3696, 0), new CoordinateClueInfo("Wilderness. West of eastern green dragon.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(1479, 3696, 0), new CoordinateClueInfo("Lizardman Canyon (DJR).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2712, 3732, 0), new CoordinateClueInfo("North-east of Rellekka (DKS).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2970, 3749, 0), new CoordinateClueInfo("Wilderness. Forgotten Cemetery.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3094, 3764, 0), new CoordinateClueInfo("Wilderness. Mining site north of Bandit Camp.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3311, 3769, 0), new CoordinateClueInfo("Wilderness. North of Venenatis.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(1460, 3782, 0), new CoordinateClueInfo("Lovakengj, near burning man.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3244, 3792, 0), new CoordinateClueInfo("Wilderness. South-east of Lava Dragon Isle by some Chaos Dwarves.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3140, 3804, 0), new CoordinateClueInfo("Wilderness. North of Ruins.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(2946, 3819, 0), new CoordinateClueInfo("Wilderness. Chaos Temple (level 38).", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3771, 3825, 0), new CoordinateClueInfo("Fossil Island. East of Museum Camp.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3013, 3846, 0), new CoordinateClueInfo("Wilderness. West of Lava Maze, before KBD's lair.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3058, 3884, 0), new CoordinateClueInfo("Wilderness. Near runite ore north of Lava Maze.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3290, 3889, 0), new CoordinateClueInfo("Wilderness. Demonic Ruins.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3770, 3897, 0), new CoordinateClueInfo("Small Island north of Fossil Island.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(2505, 3899, 0), new CoordinateClueInfo("Small Island north-west of Miscellania (AJS).", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3285, 3942, 0), new CoordinateClueInfo("Wilderness. Rogues' Castle.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3159, 3959, 0), new CoordinateClueInfo("Wilderness. North of Deserted Keep, west of Resource Area.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3039, 3960, 0), new CoordinateClueInfo("Wilderness. Pirates' Hideout.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(2987, 3963, 0), new CoordinateClueInfo("Wilderness. West of Wilderness Agility Course.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(3189, 3963, 0), new CoordinateClueInfo("Wilderness. North of Resource Area, near magic axe hut.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(2341, 3697, 0), new CoordinateClueInfo("North-east of the Piscatoris Fishing Colony bank.", SARADOMIN_WIZARD))
|
||||
.put(new WorldPoint(3143, 3774, 0), new CoordinateClueInfo("In level 32 Wilderness, by the black chinchompa hunting area.", ZAMORAK_WIZARD))
|
||||
.put(new WorldPoint(2992, 3941, 0), new CoordinateClueInfo("Wilderness Agility Course, past the log balance.", ZAMORAK_WIZARD))
|
||||
// Elite
|
||||
.put(new WorldPoint(2357, 3151, 0), new CoordinateClueInfo("Lletya.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3587, 3180, 0), new CoordinateClueInfo("Meiyerditch.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2820, 3078, 0), new CoordinateClueInfo("Tai Bwo Wannai. Hardwood Grove. 100 Trading sticks or elite Karamja diary completion is needed to enter.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3811, 3060, 0), new CoordinateClueInfo("Small island north-east of Mos Le'Harmless.", ARMADYLIAN_OR_BANDOSIAN_GUARD, true, Varbits.FIRE_PIT_MOS_LE_HARMLESS))
|
||||
.put(new WorldPoint(2180, 3282, 0), new CoordinateClueInfo("North of Iorwerth Camp.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2870, 2997, 0), new CoordinateClueInfo("North-east corner in Shilo Village.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3302, 2988, 0), new CoordinateClueInfo("On top of a cliff to the west of Pollnivneach.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2511, 2980, 0), new CoordinateClueInfo("Just south of Gu'Tanoth, west of gnome glider.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2732, 3372, 0), new CoordinateClueInfo("Legends' Guild.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3573, 3425, 0), new CoordinateClueInfo("North of Dessous's tomb from Desert Treasure.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3828, 2848, 0), new CoordinateClueInfo("East of Harmony Island.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3225, 2838, 0), new CoordinateClueInfo("South of Desert Treasure pyramid.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(1773, 3510, 0), new CoordinateClueInfo("Ruins north of the Hosidius mine.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3822, 3562, 0), new CoordinateClueInfo("North-east of Dragontooth Island. Bring a Ghostspeak Amulet and 25 Ecto-tokens to reach the island.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3603, 3564, 0), new CoordinateClueInfo("North of the wrecked ship, outside of Port Phasmatys.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2936, 2721, 0), new CoordinateClueInfo("Eastern shore of Crash Island.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2697, 2705, 0), new CoordinateClueInfo("South-west of Ape Atoll.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2778, 3678, 0), new CoordinateClueInfo("Mountain Camp.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2827, 3740, 0), new CoordinateClueInfo("West of the entrance to the Ice Path, where the Troll child resides.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2359, 3799, 0), new CoordinateClueInfo("Neitiznot.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2194, 3807, 0), new CoordinateClueInfo("Pirates' Cove.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2700, 3808, 0), new CoordinateClueInfo("Northwestern part of the Trollweiss and Rellekka Hunter area (DKS).", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3215, 3835, 0), new CoordinateClueInfo("Wilderness. Lava Dragon Isle.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3369, 3894, 0), new CoordinateClueInfo("Wilderness. Fountain of Rune.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2065, 3923, 0), new CoordinateClueInfo("Outside the western wall on Lunar Isle.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3188, 3933, 0), new CoordinateClueInfo("Wilderness. Resource Area.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2997, 3953, 0), new CoordinateClueInfo("Wilderness. Inside Agility Training Area.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3380, 3963, 0), new CoordinateClueInfo("Wilderness. North of Volcano.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3051, 3736, 0), new CoordinateClueInfo("East of the Wilderness Obelisk in 28 Wilderness.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2316, 3814, 0), new CoordinateClueInfo("West of Neitiznot, near the bridge.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2872, 3937, 0), new CoordinateClueInfo("Weiss.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2484, 4016, 0), new CoordinateClueInfo("Northeast corner of the Island of Stone.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(2222, 3331, 0), new CoordinateClueInfo("Prifddinas, west of the Tower of Voices", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
.put(new WorldPoint(3560, 3987, 0), new CoordinateClueInfo("Lithkren. Digsite pendant teleport if unlocked, otherwise take rowboat from west of Mushroom Meadow Mushtree.", ARMADYLIAN_OR_BANDOSIAN_GUARD))
|
||||
// Master
|
||||
.put(new WorldPoint(2178, 3209, 0), new CoordinateClueInfo("South of Iorwerth Camp.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(2155, 3100, 0), new CoordinateClueInfo("South of Port Tyras (BJS if 76 Agility).", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(2217, 3092, 0), new CoordinateClueInfo("Poison Waste island (DLR).", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(3830, 3060, 0), new CoordinateClueInfo("Small island located north-east of Mos Le'Harmless.", BRASSICAN_MAGE, true, Varbits.FIRE_PIT_MOS_LE_HARMLESS))
|
||||
.put(new WorldPoint(2834, 3271, 0), new CoordinateClueInfo("Crandor island.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(2732, 3284, 0), new CoordinateClueInfo("Witchaven.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(3622, 3320, 0), new CoordinateClueInfo("Meiyerditch. Outside mine.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(2303, 3328, 0), new CoordinateClueInfo("East of Prifddinas.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(3570, 3405, 0), new CoordinateClueInfo("North of Dessous's tomb from Desert Treasure.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(2840, 3423, 0), new CoordinateClueInfo("Water Obelisk Island.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(3604, 3564, 0), new CoordinateClueInfo("North of the wrecked ship, outside of Port Phasmatys (ALQ).", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(3085, 3569, 0), new CoordinateClueInfo("Wilderness. Obelisk of Air.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(2934, 2727, 0), new CoordinateClueInfo("Eastern shore of Crash Island.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(1451, 3695, 0), new CoordinateClueInfo("West side of Lizardman Canyon with Lizardman shaman.", ANCIENT_WIZARDS))
|
||||
.put(new WorldPoint(2538, 3739, 0), new CoordinateClueInfo("Waterbirth Island. Bring a pet rock and rune thrownaxe.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(1248, 3751, 0), new CoordinateClueInfo("In the north wing of the Farming Guild.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(1698, 3792, 0), new CoordinateClueInfo("Arceuus church.", ANCIENT_WIZARDS))
|
||||
.put(new WorldPoint(2951, 3820, 0), new CoordinateClueInfo("Wilderness. Chaos Temple (level 38).", ANCIENT_WIZARDS))
|
||||
.put(new WorldPoint(2202, 3825, 0), new CoordinateClueInfo("Pirates' Cove, between Lunar Isle and Rellekka.", ANCIENT_WIZARDS))
|
||||
.put(new WorldPoint(1761, 3853, 0), new CoordinateClueInfo("Arceuus essence mine (CIS).", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(2090, 3863, 0), new CoordinateClueInfo("South of Lunar Isle, west of Astral altar.", ANCIENT_WIZARDS))
|
||||
.put(new WorldPoint(1442, 3878, 0), new CoordinateClueInfo("Sulphur Mine.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(3380, 3929, 0), new CoordinateClueInfo("Wilderness. Near Volcano.", ANCIENT_WIZARDS))
|
||||
.put(new WorldPoint(3188, 3939, 0), new CoordinateClueInfo("Wilderness. Resource Area.", BRASSICAN_MAGE))
|
||||
.put(new WorldPoint(3304, 3941, 0), new CoordinateClueInfo("Wilderness. East of Rogues' Castle.", ANCIENT_WIZARDS))
|
||||
.put(new WorldPoint(2994, 3961, 0), new CoordinateClueInfo("Wilderness. Inside Agility Training Area.", BRASSICAN_MAGE))
|
||||
.build();
|
||||
|
||||
private final String text;
|
||||
private final WorldPoint location;
|
||||
/**
|
||||
* For regions which are mirrored, the location of the the clue in the mirrored region.
|
||||
*/
|
||||
@Nullable
|
||||
private final WorldPoint mirrorLocation;
|
||||
|
||||
public CoordinateClue(String text, WorldPoint location, WorldPoint mirrorLocation)
|
||||
{
|
||||
this.text = text;
|
||||
this.location = location;
|
||||
this.mirrorLocation = mirrorLocation;
|
||||
|
||||
final CoordinateClueInfo clueInfo = CLUES.get(location);
|
||||
if (clueInfo != null)
|
||||
{
|
||||
setHasFirePit(clueInfo.getLightSource());
|
||||
setRequiresLight(clueInfo.lightRequired);
|
||||
setEnemy(clueInfo.getEnemy());
|
||||
}
|
||||
setRequiresSpade(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldPoint[] getLocations()
|
||||
{
|
||||
if (mirrorLocation != null)
|
||||
{
|
||||
return new WorldPoint[]{location, mirrorLocation};
|
||||
}
|
||||
else
|
||||
{
|
||||
return new WorldPoint[]{location};
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Coordinate Clue").build());
|
||||
|
||||
final CoordinateClueInfo solution = CLUES.get(location);
|
||||
|
||||
if (solution != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(solution.getDirections())
|
||||
.build());
|
||||
panelComponent.getChildren().add(LineComponent.builder().build());
|
||||
}
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Click the clue scroll on your world map to see dig location.")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
for (WorldPoint worldPoint : getLocations())
|
||||
{
|
||||
LocalPoint localLocation = LocalPoint.fromWorld(plugin.getClient(), worldPoint);
|
||||
|
||||
if (localLocation != null)
|
||||
{
|
||||
OverlayUtil.renderTileOverlay(plugin.getClient(), graphics, localLocation, plugin.getSpadeImage(), Color.ORANGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.NPC;
|
||||
import static net.runelite.api.NullObjectID.NULL_1293;
|
||||
import net.runelite.api.ObjectComposition;
|
||||
import static net.runelite.api.ObjectID.*;
|
||||
import net.runelite.api.TileObject;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_BORDER_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_FILL_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_HOVER_BORDER_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
@Getter
|
||||
public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueScroll, ObjectClueScroll
|
||||
{
|
||||
public static final Set<CrypticClue> CLUES = ImmutableSet.of(
|
||||
new CrypticClue("Show this to Sherlock.", "Sherlock", new WorldPoint(2733, 3415, 0), "Sherlock is located to the east of the Sorcerer's tower in Seers' Village."),
|
||||
new CrypticClue("Talk to the bartender of the Rusty Anchor in Port Sarim.", "Bartender", new WorldPoint(3045, 3256, 0), "The Rusty Anchor is located in the north of Port Sarim."),
|
||||
new CrypticClue("The keeper of Melzars... Spare? Skeleton? Anar?", "Oziach", new WorldPoint(3068, 3516, 0), "Speak to Oziach in Edgeville."),
|
||||
new CrypticClue("Speak to Ulizius.", "Ulizius", new WorldPoint(3444, 3461, 0), "Ulizius is the monk who guards the gate into Mort Myre Swamp. South of fairy ring CKS."),
|
||||
new CrypticClue("Search for a crate in a building in Hemenster.", CRATE_357, new WorldPoint(2636, 3453, 0), "House northwest of the Ranging Guild. West of Grandpa Jack."),
|
||||
new CrypticClue("A reck you say; let's pray there aren't any ghosts.", "Father Aereck", new WorldPoint(3242, 3207, 0), "Speak to Father Aereck in Lumbridge."),
|
||||
new CrypticClue("Search the bucket in the Port Sarim jail.", BUCKET_9568, new WorldPoint(3013, 3179, 0), "Talk to Shantay & identify yourself as an outlaw, refuse to pay the 5gp fine twice and you will be sent to the Port Sarim jail."),
|
||||
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 Seers' 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 of the Wizards' Tower."),
|
||||
new CrypticClue("Come have a cip with this great soot covered denizen.", "Miner Magnus", new WorldPoint(2527, 3891, 0), "Talk to Miner Magnus on Miscellania, 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."),
|
||||
new CrypticClue("Search for a crate on the ground floor of a house in Seers' Village.", CRATE_25775, new WorldPoint(2699, 3470, 0), "Search inside Phantuwti Fanstuwi Farsight's house, located south of the pub in Seers' Village."),
|
||||
new CrypticClue("Snah? I feel all confused, like one of those cakes...", "Hans", new WorldPoint(3211, 3219, 0), "Talk to Hans roaming around Lumbridge Castle."),
|
||||
new CrypticClue("Speak to Sir Kay in Camelot Castle.", "Sir Kay", new WorldPoint(2759, 3497, 0), "Sir Kay can be found in the courtyard at Camelot castle."),
|
||||
new CrypticClue("Gold I see, yet gold I require. Give me 875 if death you desire.", "Saniboch", new WorldPoint(2745, 3151, 0), "Speak to Saniboch at the Brimhaven Dungeon entrance."),
|
||||
new CrypticClue("Find a crate close to the monks that like to paaarty!", CRATE_354, new WorldPoint(2614, 3204, 0), "The crate is in the east side of the Ardougne monastery, near Brother Omad."),
|
||||
new CrypticClue("Identify the back of this over-acting brother. (He's a long way from home.)", "Hamid", new WorldPoint(3376, 3284, 0), "Talk to Hamid, the monk at the altar in the Duel Arena."),
|
||||
new CrypticClue("In a town where thieves steal from stalls, search for some drawers in the upstairs of a house near the bank.", "Guard", DRAWERS, new WorldPoint(2611, 3324, 1), "Kill any Guard located around East Ardougne for a medium key. Then search the drawers in the upstairs hallway of Jerico's house, which is the house with pigeon cages located south of the northern East Ardougne bank."),
|
||||
new CrypticClue("His bark is worse than his bite.", "Barker", new WorldPoint(3499, 3503, 0), "Speak to the Barker at Canifis's Barkers' Haberdashery."),
|
||||
new CrypticClue("The beasts to my east snap claws and tails, The rest to my west can slide and eat fish. The force to my north will jump and they'll wail, Come dig by my fire and make a wish.", new WorldPoint(2598, 3267, 0), "Dig by the torch in the Ardougne Zoo, between the penguins and the scorpions."),
|
||||
new CrypticClue("A town with a different sort of night-life is your destination. Search for some crates in one of the houses.", CRATE_24344, new WorldPoint(3498, 3507, 0), "Search the crate inside of the clothes shop in Canifis."),
|
||||
new CrypticClue("Stop crying! Talk to the head.", "Head mourner", new WorldPoint(2042, 4630, 0), "Talk to the Head mourner in the mourner headquarters in West Ardougne."),
|
||||
new CrypticClue("Search the crate near a cart in Port Khazard.", CRATE_366, new WorldPoint(2660, 3149, 0), "Search by the southern Khazard General Store in Port Khazard."),
|
||||
new CrypticClue("Speak to the bartender of the Blue Moon Inn in Varrock.", "Bartender", new WorldPoint(3226, 3399, 0), "Talk to the bartender in Blue Moon Inn in Varrock."),
|
||||
new CrypticClue("This aviator is at the peak of his profession.", "Captain Bleemadge", new WorldPoint(2846, 1749, 0), "Captain Bleemadge, the gnome glider pilot, is found at the top of White Wolf Mountain."),
|
||||
new CrypticClue("Search the crates in the shed just north of East Ardougne.", CRATE_355, new WorldPoint(2617, 3347, 0), "The crates in the shed north of the northern Ardougne bank."),
|
||||
new CrypticClue("I wouldn't wear this jean on my legs.", "Father Jean", new WorldPoint(1734, 3576, 0), "Talk to father Jean in the Hosidius church."),
|
||||
new CrypticClue("Search the crate in the Toad and Chicken pub.", CRATE_354, new WorldPoint(2913, 3536, 0), "The Toad and Chicken pub is located in Burthorpe."),
|
||||
new CrypticClue("Search chests found in the upstairs of shops in Port Sarim.", CLOSED_CHEST_375, new WorldPoint(3016, 3205, 1), "Search the chest on the east wall found upstairs of Wydin's Food Store in Port Sarim."),
|
||||
new CrypticClue("Right on the blessed border, cursed by the evil ones. On the spot inaccessible by both; I will be waiting. The bugs' imminent possession holds the answer.", new WorldPoint(3410, 3324, 0), "B I P. Dig right under the fairy ring."),
|
||||
new CrypticClue("The dead, red dragon watches over this chest. He must really dig the view.", "Barbarian", 375, new WorldPoint(3353, 3332, 0), "Search the chest underneath the Red Dragon's head in the Exam Centre. Kill a MALE Barbarian in Barbarian Village or Barbarian Outpost to receive the key."),
|
||||
new CrypticClue("My home is grey, and made of stone; A castle with a search for a meal. Hidden in some drawers I am, across from a wooden wheel.", DRAWERS_5618, new WorldPoint(3213, 3216, 1), "Open the drawers inside the room with the spinning wheel on the first floor of Lumbridge Castle."),
|
||||
new CrypticClue("Come to the evil ledge, Yew know yew want to. Try not to get stung.", new WorldPoint(3089, 3468, 0), "Dig in Edgeville, just east of the Southern Yew tree."),
|
||||
new CrypticClue("Look in the ground floor crates of houses in Falador.", CRATES_24088, new WorldPoint(3029, 3355, 0), "The house east of the eastern bank in Falador."),
|
||||
new CrypticClue("You were 3 and I was the 6th. Come speak to me.", "Vannaka", new WorldPoint(3146, 9913, 0), "Speak to Vannaka in Edgeville Dungeon."),
|
||||
new CrypticClue("Search the crates in Draynor Manor.", CRATE_11485, new WorldPoint(3106, 3369, 2), "Top floor of the Draynor Manor."),
|
||||
new CrypticClue("Search the crates near a cart in Varrock.", CRATE_5107, new WorldPoint(3226, 3452, 0), "South east of Varrock Palace, south of the tree farming patch."),
|
||||
new CrypticClue("A Guthixian ring lies between two peaks. Search the stones and you'll find what you seek.", STONES_26633, new WorldPoint(2922, 3484, 0), "Search the stones several steps west of the Guthixian stone circle in Taverley."),
|
||||
new CrypticClue("Search the boxes in the house near the south entrance to Varrock.", BOXES_5111, new WorldPoint(3203, 3384, 0), "The first house on the left when entering Varrock from the southern entrance."),
|
||||
new CrypticClue("His head might be hollow, but the crates nearby are filled with surprises.", CRATE_354, new WorldPoint(3478, 3091, 0), "Search the crates near the Clay golem in the ruins of Uzer."),
|
||||
new CrypticClue("One of the sailors in Port Sarim is your next destination.", "Captain Tobias", new WorldPoint(3026, 3216, 0), "Speak to Captain Tobias on the docks of Port Sarim."),
|
||||
new CrypticClue("THEY'RE EVERYWHERE!!!! But they were here first. Dig for treasure where the ground is rich with ore.", new WorldPoint(3081, 3421, 0), "Dig at Barbarian Village, next to the Stronghold of Security."),
|
||||
new CrypticClue("Talk to the mother of a basement dwelling son.", "Doris", new WorldPoint(3079, 3493, 0), "Evil Dave's mother, Doris, is located in the house west of Edgeville bank."),
|
||||
new CrypticClue("Speak to Ned in Draynor Village.", "Ned", new WorldPoint(3098, 3258, 0), "Ned is found north of the Draynor bank."),
|
||||
new CrypticClue("Speak to Hans to solve the clue.", "Hans", new WorldPoint(3211, 3219, 0), "Hans can be found at Lumbridge Castle."),
|
||||
new CrypticClue("Search the crates in Canifis.", CRATE_24344, new WorldPoint(3509, 3497, 0), "Search inside Rufus' Meat Emporium in Canifis."),
|
||||
new CrypticClue("Search the crates in the Dwarven mine.", CRATE_357, new WorldPoint(3035, 9849, 0), "Search the crate in the room east of the Ice Mountain ladder entrance in the Drogo's Mining Emporium in the Dwarven Mine."),
|
||||
new CrypticClue("A crate found in the tower of a church is your next location.", CRATE_357, new WorldPoint(2612, 3304, 1), "Climb the ladder and search the crates on the first floor in the Church in Ardougne."),
|
||||
new CrypticClue("Covered in shadows, the centre of the circle is where you will find the answer.", new WorldPoint(3488, 3289, 0), "Dig in the centre of Mort'ton, where the roads intersect."),
|
||||
new CrypticClue("I lie lonely and forgotten in mid wilderness, where the dead rise from their beds. Feel free to quarrel and wind me up, and dig while you shoot their heads.", new WorldPoint(3174, 3663, 0), "Directly under the crossbow respawn in the Graveyard of Shadows in level 18 Wilderness."),
|
||||
new CrypticClue("In the city where merchants are said to have lived, talk to a man with a splendid cape, but a hat dropped by goblins.", "Head chef", new WorldPoint(3143, 3445, 0), "Talk to the Head chef in Cooks' Guild west of Varrock. You will need a chef hat or cooking cape to enter."),
|
||||
new CrypticClue("The mother of the reptilian sacrifice.", "Zul-Cheray", new WorldPoint(2204, 3050, 0), "Talk to Zul-Cheray in a house near the sacrificial boat at Zul-Andra."),
|
||||
new CrypticClue("I watch the sea. I watch you fish. I watch your tree.", "Ellena", new WorldPoint(2860, 3431, 0), "Speak to Ellena at Catherby fruit tree patch."),
|
||||
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 in Draynor Village 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", "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"),
|
||||
new CrypticClue("Even the seers say this clue goes right over their heads.", CRATE_14934, new WorldPoint(2707, 3488, 2), "Search the crate on the Seers Agility Course in Seers Village"),
|
||||
new CrypticClue("Speak to a Wyse man.", "Wyson the gardener", new WorldPoint(3026, 3378, 0), "Talk to Wyson the gardener at Falador Park."),
|
||||
new CrypticClue("You'll need to look for a town with a central fountain. Look for a locked chest in the town's chapel.", "Monk" , CLOSED_CHEST_5108, new WorldPoint(3256, 3487, 0), "Search the chest by the stairs in the Varrock church. Kill a Monk in Ardougne Monastery to obtain the key."),
|
||||
new CrypticClue("Talk to Ambassador Spanfipple in the White Knights Castle.", "Ambassador Spanfipple", new WorldPoint(2979, 3340, 0), "Ambassador Spanfipple can be found roaming on the first floor of the White Knights Castle."),
|
||||
new CrypticClue("Mine was the strangest birth under the sun. I left the crimson sack, yet life had not begun. Entered the world, and yet was seen by none.", new WorldPoint(2832, 9586, 0), "Inside Karamja Volcano, dig directly underneath the Red spiders' eggs respawn."),
|
||||
new CrypticClue("Search for a crate in Varrock Castle.", CRATE_5113, new WorldPoint(3224, 3492, 0), "Search the crate in the corner of the kitchen in Varrock Castle."),
|
||||
new CrypticClue("And so on, and so on, and so on. Walking from the land of many unimportant things leads to a choice of paths.", new WorldPoint(2591, 3879, 0), "Dig on Etceteria next to the Evergreen tree in front of the castle walls."),
|
||||
new CrypticClue("Speak to Donovan, the Family Handyman.", "Donovan the Family Handyman", new WorldPoint(2743, 3578, 0), "Donovan the Family Handyman is found on the first floor of Sinclair Mansion, north of Seers' Village."),
|
||||
new CrypticClue("Search the crates in the Barbarian Village helmet shop.", CRATES_11600, new WorldPoint(3073, 3430, 0), "Peksa's Helmet Shop in Barbarian Village."),
|
||||
new CrypticClue("Search the boxes of Falador's general store.", CRATES_24088, new WorldPoint(2955, 3390, 0), "Falador general store."),
|
||||
new CrypticClue("In a village made of bamboo, look for some crates under one of the houses.", CRATE_356, new WorldPoint(2800, 3074, 0), "Search the crate by the house at the northern point of the broken jungle fence in Tai Bwo Wannai."),
|
||||
new CrypticClue("This crate is mine, all mine, even if it is in the middle of the desert.", CRATE_18889, new WorldPoint(3289, 3022, 0), "Center of desert Mining Camp. Search the crates. Requires the metal key from Tourist Trap to enter."),
|
||||
new CrypticClue("Dig where 4 siblings and I all live with our evil overlord.", new WorldPoint(3195, 3357, 0), "Dig in the chicken pen inside the Champions' Guild"),
|
||||
new CrypticClue("In a town where the guards are armed with maces, search the upstairs rooms of the Public House.", "Guard dog", 348, new WorldPoint(2574, 3326, 1), "Search the drawers upstairs in the pub north of Ardougne Castle. Kill a Guard dog at Handelmort Mansion to obtain the key."),
|
||||
new CrypticClue("Four blades I have, yet draw no blood; Still I turn my prey to powder. If you are brave, come search my roof; It is there my blades are louder.", CRATE_12963, new WorldPoint(3166, 3309, 2), "Lumbridge windmill, search the crates on the top floor."),
|
||||
new CrypticClue("Search through some drawers in the upstairs of a house in Rimmington.", DRAWERS_352, new WorldPoint(2970, 3214, 1), "On the first floor of the house north of Hetty the Witch's house in Rimmington."),
|
||||
new CrypticClue("Probably filled with books on magic.", BOOKCASE_380, new WorldPoint(3096, 9572, 0), "Search the bookcase in the basement of Wizards' Tower. Fairy ring DIS."),
|
||||
new CrypticClue("If you look closely enough, it seems that the archers have lost more than their needles.", HAYSTACK, new WorldPoint(2672, 3416, 0), "Search the haystack by the south corner of the Ranging Guild."),
|
||||
new CrypticClue("Search the crate in the left-hand tower of Lumbridge Castle.", CRATE_357, new WorldPoint(3228, 3212, 1), "Located on the first floor of the southern tower at the Lumbridge Castle entrance."),
|
||||
new CrypticClue("'Small shoe.' Often found with rod on mushroom.", "Gnome trainer", new WorldPoint(2476, 3428, 0), "Talk to any Gnome trainer in the agility area of the Tree Gnome Stronghold."),
|
||||
new CrypticClue("I live in a deserted crack collecting soles.", "Genie", new WorldPoint(3371, 9320, 0), "Enter the crack west of Nardah Rug merchant, and talk to the Genie. You'll need a light source and a rope.", true),
|
||||
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), "Slide down to where Trollweiss grows on Trollweiss Mountain. Bring a sled."),
|
||||
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 Falador 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", "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 Edgeville 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."),
|
||||
new CrypticClue("Search the crates in a house in Yanille that has a piano.", CRATE_357, new WorldPoint(2598, 3105, 0), "The house is located northwest of the bank in Yanille."),
|
||||
new CrypticClue("Speak to the staff of Sinclair mansion.", "Louisa", new WorldPoint(2736, 3578, 0), "Speak to Louisa, on the ground floor, found at the Sinclair Mansion. Fairy ring CJR"),
|
||||
new CrypticClue("I am a token of the greatest love. I have no beginning or end. My eye is red, I can fit like a glove. Go to the place where it's money they lend, And dig by the gate to be my friend.", new WorldPoint(3191, 9825, 0), "Dig by the gate in the basement of the West Varrock bank."),
|
||||
new CrypticClue("Speak to Kangai Mau.", "Kangai Mau", new WorldPoint(2791, 3183, 0), "Kangai Mau is found in the Shrimp and Parrot in Brimhaven."),
|
||||
new CrypticClue("Speak to Hajedy.", "Hajedy", new WorldPoint(2779, 3211, 0), "Hajedy is found by the cart, located just south of the Brimhaven docks."),
|
||||
new CrypticClue("Must be full of railings.", BOXES_6176, new WorldPoint(2576, 3464, 0), "Search the boxes around the hut where the broken Dwarf Cannon is, close to the start of the Dwarf Cannon quest."),
|
||||
new CrypticClue("I wonder how many bronze swords he has handed out.", "Vannaka", new WorldPoint(3164, 9913, 0), "Talk to Vannaka. He can be found in Edgeville Dungeon."),
|
||||
new CrypticClue("Read 'How to breed scorpions.' By O.W.Thathurt.", BOOKCASE_380, new WorldPoint(2703, 3409, 1), "Search the northern bookcase on the first floor of the Sorcerer's Tower."),
|
||||
new CrypticClue("Search the crates in the Port Sarim Fishing shop.", CRATE_9534, new WorldPoint(3012, 3222, 0), "Search the crates, by the door, in Gerrant's Fishy Business in Port Sarim."),
|
||||
new CrypticClue("Speak to The Lady of the Lake.", "The Lady of the Lake", new WorldPoint(2924, 3405, 0), "Talk to The Lady of the Lake in Taverley."),
|
||||
new CrypticClue("Rotting next to a ditch. Dig next to the fish.", new WorldPoint(3547, 3183, 0), "Dig next to a fishing spot on the south-east side of Burgh de Rott."),
|
||||
new CrypticClue("The King's magic won't be wasted by me.", "Guardian Mummy", new WorldPoint(1934, 4427, 0), "Talk to the Guardian mummy inside the Pyramid Plunder minigame in Sophanem."),
|
||||
new CrypticClue("Dig where the forces of Zamorak and Saradomin collide.", new WorldPoint(3049, 4839, 0), "Dig next to the law rift in the Abyss."),
|
||||
new CrypticClue("Search the boxes in the goblin house near Lumbridge.", BOXES, new WorldPoint(3245, 3245, 0), "Goblin house on the eastern side of the river outside of Lumbridge."),
|
||||
new CrypticClue("W marks the spot.", new WorldPoint(2867, 3546, 0), "Dig in the middle of the Warriors' Guild entrance hall."),
|
||||
new CrypticClue("There is no 'worthier' lord.", "Lord Iorwerth", new WorldPoint(2205, 3252, 0), "Speak to Lord Iorwerth in the elven camp southwest of Prifddinas."),
|
||||
new CrypticClue("Surviving.", "Sir Vyvin", new WorldPoint(2983, 3338, 0), "Talk to Sir Vyvin on the second floor of Falador castle."),
|
||||
new CrypticClue("My name is like a tree, yet it is spelt with a 'g'. Come see the fur which is right near me.", "Wilough", new WorldPoint(3221, 3435, 0), "Speak to Wilough, next to the Fur Merchant in Varrock Square."),
|
||||
new CrypticClue("Speak to Jatix in Taverley.", "Jatix", new WorldPoint(2898, 3428, 0), "Jatix is found in the middle of Taverley."),
|
||||
new CrypticClue("Speak to Gaius in Taverley.", "Gaius", new WorldPoint(2884, 3450, 0), "Gaius is found at the northwest corner in Taverley."),
|
||||
new CrypticClue("If a man carried my burden, he would break his back. I am not rich, but leave silver in my track. Speak to the keeper of my trail.", "Gerrant", new WorldPoint(3014, 3222, 0), "Speak to Gerrant in the fish shop in Port Sarim."),
|
||||
new CrypticClue("Search the drawers in Falador's chain mail shop.", DRAWERS, new WorldPoint(2969, 3311, 0), "Wayne's Chains - Chainmail Specialist store at the southern Falador walls."),
|
||||
new CrypticClue("Talk to the barber in the Falador barber shop.", "Hairdresser", new WorldPoint(2945, 3379, 0), "The Hairdresser can be found in the barber shop, north of the west Falador bank."),
|
||||
new CrypticClue("Often sought out by scholars of histories past, find me where words of wisdom speak volumes.", "Examiner", new WorldPoint(3362, 3341, 0), "Speak to an examiner at the Exam Centre."),
|
||||
new CrypticClue("Generally speaking, his nose was very bent.", "General Bentnoze", new WorldPoint(2957, 3511, 0), "Talk to General Bentnoze in the Goblin Village north of Falador."),
|
||||
new CrypticClue("Search the bush at the digsite centre.", BUSH_2357, new WorldPoint(3345, 3378, 0), "The bush is on the east side of the first pathway towards the digsite from the Exam Centre."),
|
||||
new CrypticClue("Someone watching the fights in the Duel Arena is your next destination.", "Jeed", new WorldPoint(3360, 3242, 0), "Talk to Jeed, found on the upper floors, at the Duel Arena."),
|
||||
new CrypticClue("It seems to have reached the end of the line, and it's still empty.", MINE_CART_6045, new WorldPoint(3041, 9820, 0), "Search the carts in the northern part of the Dwarven Mine."),
|
||||
new CrypticClue("You'll have to plug your nose if you use this source of herbs.", null, "Kill an Aberrant or Deviant spectre."),
|
||||
new CrypticClue("When you get tired of fighting, go deep, deep down until you need an antidote.", CRATE_357, new WorldPoint(2576, 9583, 0), "Go to Yanille Agility dungeon and fall into the place with the poison spiders by praying at the Chaos altar. Search the crate by the stairs leading up."),
|
||||
new CrypticClue("Search the bookcase in the monastery.", BOOKCASE_380, new WorldPoint(3054, 3484, 0), "Search the southeastern bookcase at Edgeville Monastery."),
|
||||
new CrypticClue("Surprising? I bet he is...", "Sir Prysin", new WorldPoint(3205, 3474, 0), "Talk to Sir Prysin in Varrock Palace."),
|
||||
new CrypticClue("Search upstairs in the houses of Seers' Village for some drawers.", DRAWERS_25766, new WorldPoint(2716, 3471, 1), "Located in the house with the spinning wheel. South of the Seers' Village bank."),
|
||||
new CrypticClue("Leader of the Yak City.", "Mawnis Burowgar", new WorldPoint(2336, 3799, 0), "Talk to Mawnis Burowgar in Neitiznot."),
|
||||
new CrypticClue("Speak to Arhein in Catherby.", "Arhein", new WorldPoint(2803, 3430, 0), "Arhein is just south of the Catherby bank."),
|
||||
new CrypticClue("Speak to Doric, who lives north of Falador.", "Doric", new WorldPoint(2951, 3451, 0), "Doric is found north of Falador and east of the Taverley gate."),
|
||||
new CrypticClue("Where the best are commemorated, and a celebratory cup, not just for beer.", new WorldPoint(3388, 3152, 0), "Dig at the Clan Cup Trophy south-west of Citharede Abbey."),
|
||||
new CrypticClue("'See you in your dreams' said the vegetable man.", "Dominic Onion", new WorldPoint(2608, 3116, 0), "Speak to Dominic Onion at the Nightmare Zone teleport spot."),
|
||||
new CrypticClue("Try not to step on any aquatic nasties while searching this crate.", CRATE_18204, new WorldPoint(2764, 3273, 0), "Search the crate in Bailey's house on the Fishing Platform."),
|
||||
new CrypticClue("The cheapest water for miles around, but they react badly to religious icons.", CRATE_354, new WorldPoint(3178, 2987, 0), "Search the crates in the General Store tent in the Desert Bandit Camp."),
|
||||
new CrypticClue("This village has a problem with cartloads of the undead. Try checking the bookcase to find an answer.", BOOKCASE_394, new WorldPoint(2833, 2992, 0), "Search the bookcase by the doorway of the building just south east of the Shilo Village Gem Mine."),
|
||||
new CrypticClue("Dobson is my last name, and with gardening I seek fame.", "Horacio", new WorldPoint(2635, 3310, 0), "Horacio, located in the garden of the Handelmort Mansion in East Ardougne."),
|
||||
new CrypticClue("The magic of 4 colours, an early experience you could learn. The large beast caged up top, rages, as his demised kin's loot now returns.", "Wizard Mizgog", new WorldPoint(3103, 3163, 2), "Speak to Wizard Mizgog at the top of the Wizard's Tower south of Draynor."),
|
||||
new CrypticClue("Aggie I see. Lonely and southern I feel. I am neither inside nor outside the house, yet no home would be complete without me. The treasure lies beneath me!", new WorldPoint(3085, 3255, 0), "Dig outside the window of Aggie's house in Draynor Village."),
|
||||
new CrypticClue("Search the chest in Barbarian Village.", CLOSED_CHEST_375, new WorldPoint(3085, 3429, 0), "The chest located in the house with a spinning wheel in Barbarian Village."),
|
||||
new CrypticClue("Search the crates in the outhouse of the long building in Taverley.", CRATE_357, new WorldPoint(2914, 3433, 0), "Located in the small building attached by a fence to the main building in Taverley. Climb over the stile."),
|
||||
new CrypticClue("Talk to Ermin.", "Ermin", new WorldPoint(2488, 3409, 1), "Ermin can be found on the first floor of the tree house south-east of the Gnome Agility Course."),
|
||||
new CrypticClue("Ghostly bones.", null, "Kill an Ankou."),
|
||||
new CrypticClue("Search through chests found in the upstairs of houses in eastern Falador.", CLOSED_CHEST_375, new WorldPoint(3041, 3364, 1), "The house is located southwest of the Falador Party Room. There are two chests in the room, search the northern chest."),
|
||||
new CrypticClue("Let's hope you don't meet a watery death when you encounter this fiend.", null, "Kill a waterfiend."),
|
||||
new CrypticClue("Reflection is the weakness for these eyes of evil.", null, "Kill a basilisk."),
|
||||
new CrypticClue("Search a bookcase in Lumbridge swamp.", BOOKCASE_9523, new WorldPoint(3146, 3177, 0), "Located in Father Urhney's house in Lumbridge Swamp."),
|
||||
new CrypticClue("Surround my bones in fire, ontop the wooden pyre. Finally lay me to rest, before my one last test.", null, "Kill a confused/lost barbarian to receive mangled bones. Construct and burn a pyre ship. Kill the ferocious barbarian spirit that spawns to receive a clue casket."),
|
||||
new CrypticClue("Fiendish cooks probably won't dig the dirty dishes.", new WorldPoint(3043, 4974, 1), "Dig by the fire in the Rogues' Den."),
|
||||
new CrypticClue("My life was spared but these voices remain, now guarding these iron gates is my bane.", "Key Master", new WorldPoint(1310, 1251, 0), "Speak to the Key Master in Cerberus' Lair."),
|
||||
new CrypticClue("Search the boxes in one of the tents in Al Kharid.", BOXES_361, new WorldPoint(3308, 3206, 0), "Search the boxes in the tent east of the Al Kharid Silk trader."),
|
||||
new CrypticClue("One of several rhyming brothers, in business attire with an obsession for paper work.", "Piles", new WorldPoint(3186, 3936, 0), "Speak to Piles in the Wilderness Resource Area. An entry fee of 7,500 coins is required, or less if Wilderness Diaries have been completed."),
|
||||
new CrypticClue("Search the drawers on the ground floor of a building facing Ardougne's Market.", DRAWERS_350, new WorldPoint(2653, 3320, 0), "Inside Noella's house north of the East Ardougne market."),
|
||||
new CrypticClue("'A bag belt only?', he asked his balding brothers.", "Abbot Langley", new WorldPoint(3058, 3487, 0), "Talk-to Abbot Langley in Monastery west of Edgeville"),
|
||||
new CrypticClue("Search the drawers upstairs in Falador's shield shop.", DRAWERS, new WorldPoint(2971, 3386, 1), "Cassie's Shield Shop at the northern Falador entrance."),
|
||||
new CrypticClue("Go to this building to be illuminated, and check the drawers while you are there.", "Market Guard", DRAWERS_350 , new WorldPoint(2512, 3641, 1), "Search the drawers in the first floor of the Lighthouse. Kill a Rellekka marketplace guard to obtain the key."),
|
||||
new CrypticClue("Dig near some giant mushrooms, behind the Grand Tree.", new WorldPoint(2458, 3504, 0), "Dig near the red mushrooms northwest of the Grand Tree."),
|
||||
new CrypticClue("Pentagrams and demons, burnt bones and remains, I wonder what the blood contains.", new WorldPoint(3297, 3890, 0), "Dig under the blood rune spawn next to the Demonic Ruins."),
|
||||
new CrypticClue("Search the drawers above Varrock's shops.", DRAWERS_7194, new WorldPoint(3206, 3419, 1), "Located upstairs in Thessalia's Fine Clothes shop in Varrock."),
|
||||
new CrypticClue("Search the drawers in one of Gertrude's bedrooms.", DRAWERS_7194, new WorldPoint(3156, 3406, 0), "Kanel's bedroom (southeastern room), in Gertrude's house south of the Cooking Guild."),
|
||||
new CrypticClue("Under a giant robotic bird that cannot fly.", new WorldPoint(1756, 4940, 0), "Dig next to the terrorbird display in the south exhibit of Varrock Museum's basement."),
|
||||
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", "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 Mage shop in Port Sarim. 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."),
|
||||
new CrypticClue("Strength potions with red spiders' eggs? He is quite a herbalist.", "Apothecary", new WorldPoint(3194, 3403, 0), "Talk to Apothecary in the South-western Varrock. (the) apothecary is just north-west of the Varrock Swordshop."),
|
||||
new CrypticClue("Robin wishes to see your finest ranged equipment.", "Robin", new WorldPoint(3673, 3492, 0), "Robin at the inn in Port Phasmatys. Speak to him with +182 in ranged attack bonus. Bonus granted by the toxic blowpipe is ignored."),
|
||||
new CrypticClue("You will need to under-cook to solve this one.", CRATE_357, new WorldPoint(3219, 9617, 0), "Search the crate in the Lumbridge basement."),
|
||||
new CrypticClue("Search through some drawers found in Taverley's houses.", DRAWERS_350, new WorldPoint(2894, 3418, 0), "The south-eastern most house in Taverley, south of Jatix's Herblore Shop."),
|
||||
new CrypticClue("Anger Abbot Langley.", "Abbot Langley", new WorldPoint(3058, 3487, 0), "Speak to Abbot Langley in the Edgeville Monastery while you have a negative prayer bonus (currently only possible with an Ancient staff)."),
|
||||
new CrypticClue("Dig where only the skilled, the wealthy, or the brave can choose not to visit again.", new WorldPoint(3221, 3219, 0), "Dig at Lumbridge spawn"),
|
||||
new CrypticClue("Scattered coins and gems fill the floor. The chest you seek is in the north east.", "King Black Dragon", CLOSED_CHEST_375, new WorldPoint(2288, 4702, 0), "Kill the King Black Dragon for a key (elite), and then open the closed chest in the NE corner of the lair."),
|
||||
new CrypticClue("A ring of water surrounds 4 powerful rings, dig above the ladder located there.", new WorldPoint(1910, 4367, 0), "Dig by the ladder leading to the Dagannoth Kings room in the Waterbirth Island Dungeon. Bring a pet rock and rune thrownaxe."),
|
||||
new CrypticClue("This place sure is a mess.", "Ewesey", new WorldPoint(1646, 3631, 0), "Ewesey is located in the mess hall in Hosidius."),
|
||||
new CrypticClue("Here, there are tears, but nobody is crying. Speak to the guardian and show off your alignment to balance.", "Juna", JUNA, new WorldPoint(3252, 9517, 2), "Talk to Juna while wearing three Guthix related items."),
|
||||
new CrypticClue("You might have to turn over a few stones to progress.", null, "Kill a rock crab."),
|
||||
new CrypticClue("Dig under Razorlor's toad batta.", new WorldPoint(3139, 4554, 0), "Dig on the toad batta spawn in Tarn's Lair."),
|
||||
new CrypticClue("Talk to Cassie in Falador.", "Cassie", new WorldPoint(2975, 3383, 0), "Cassie is found just south-east of the northern Falador gate."),
|
||||
new CrypticClue("Faint sounds of 'Arr', fire giants found deep, the eastern tip of a lake, are the rewards you could reap.", new WorldPoint(3055, 10338, 0), "Dig south of the pillar in the Deep Wilderness Dungeon in the room with the fire giants."),
|
||||
new CrypticClue("If you're feeling brave, dig beneath the dragon's eye.", new WorldPoint(2410, 4714, 0), "Dig below the mossy rock under the Viyeldi caves (Legend's Quest). Items needed: Pickaxe, unpowered orb, lockpick, spade, any charge orb spell, and either 79 agility or an axe and machete."),
|
||||
new CrypticClue("Search the tents in the Imperial Guard camp in Burthorpe for some boxes.", BOXES_3686, new WorldPoint(2885, 3540, 0), "Search in the tents in the northwest corner of the soldiers' camp in Burthorpe."),
|
||||
new CrypticClue("A dwarf, approaching death, but very much in the light.", "Thorgel", new WorldPoint(1863, 4639, 0), "Speak to Thorgel at the entrance to the Death altar."),
|
||||
new CrypticClue("You must be 100 to play with me.", "Squire (Veteran)", new WorldPoint(2638, 2656, 0), "Speak to the Veteran boat squire at Pest Control."),
|
||||
new CrypticClue("Three rule below and three sit at top. Come dig at my entrance.", new WorldPoint(2523, 3739, 0), "Dig in front of the entrance to the Waterbirth Island Dungeon."),
|
||||
new CrypticClue("Search the drawers in the ground floor of a shop in Yanille.", DRAWERS_350, new WorldPoint(2570, 3085, 0), "Search the drawers in Yanille's hunting shop."),
|
||||
new CrypticClue("Search the drawers of houses in Burthorpe.", DRAWERS, new WorldPoint(2929, 3570, 0), "Inside Hild's house in the northeast corner of Burthorpe."),
|
||||
new CrypticClue("Where safe to speak, the man who offers the pouch of smallest size wishes to see your alignment.", "Mage of Zamorak", new WorldPoint(3260, 3385, 0), "Speak to the Mage of Zamorak south of the Rune Shop in Varrock while wearing three Zamorakian items."),
|
||||
new CrypticClue("Search the crates in the guard house of the northern gate of East Ardougne.", CRATE_356, new WorldPoint(2645, 3338, 0), "The guard house northwest of the East Ardougne market."),
|
||||
new CrypticClue("Go to the village being attacked by trolls, search the drawers in one of the houses.", "Penda", DRAWERS_350, new WorldPoint(2921, 3577, 0), "Go to Dunstan's house in the northeast corner of Burthorpe. Kill Penda in the Toad and Chicken to obtain the key."),
|
||||
new CrypticClue("You'll get licked.", null, "Kill a Bloodveld."),
|
||||
new CrypticClue("She's small but can build both literally and figuratively, as long as you have their favour.", "Lovada", new WorldPoint(1486, 3834, 0), "Speak to Lovada by the entrance to the blast mine in Lovakengj."),
|
||||
new CrypticClue("Dig in front of the icy arena where 1 of 4 was fought.", new WorldPoint(2874, 3757, 0), "North of Trollheim, where you fought Kamil from Desert Treasure."),
|
||||
new CrypticClue("Speak to Roavar.", "Roavar", new WorldPoint(3494, 3474, 0), "Talk to Roavar in the Canifis tavern."),
|
||||
new CrypticClue("Search the drawers downstairs of houses in the eastern part of Falador.", DRAWERS_350, new WorldPoint(3039, 3342, 0), "House is located east of the eastern Falador bank and south of the fountain. The house is indicated by a cooking range icon on the minimap."),
|
||||
new CrypticClue("Search the drawers found upstairs in East Ardougne's houses.", DRAWERS, new WorldPoint(2574, 3326, 1), "Upstairs of the pub north of the Ardougne Castle."),
|
||||
new CrypticClue("The far north eastern corner where 1 of 4 was defeated, the shadows still linger.", new WorldPoint(2744, 5116, 0), "Dig on the northeastern-most corner of the Shadow Dungeon. Bring a ring of visibility."),
|
||||
new CrypticClue("Search the drawers in a house in Draynor Village.", DRAWERS_350, new WorldPoint(3097, 3277, 0), "The drawer is located in the northernmost house in Draynor Village."),
|
||||
new CrypticClue("Search the boxes in a shop in Taverley.", BOXES_360, new WorldPoint(2886, 3449, 0), "The box inside Gaius' Two Handed Shop in Taverley."),
|
||||
new CrypticClue("I lie beneath the first descent to the holy encampment.", new WorldPoint(2914, 5300, 1), "Dig immediately after climbing down the first set of rocks towards Saradomin's encampment within the God Wars Dungeon."),
|
||||
new CrypticClue("Search the upstairs drawers of a house in a village where pirates are known to have a good time.", "Pirate", 348, new WorldPoint(2809, 3165, 1), "The house in the southeast corner of Brimhaven, northeast of Davon's Amulet Store. Kill any Pirate located around Brimhaven to obtain the key."),
|
||||
new CrypticClue("Search the chest in the Duke of Lumbridge's bedroom.", CLOSED_CHEST_375, new WorldPoint(3209, 3218, 1), "The Duke's room is on the first floor in Lumbridge Castle."),
|
||||
new CrypticClue("Talk to the Doomsayer.", "Doomsayer", new WorldPoint(3232, 3228, 0), "Doomsayer can be found just north of Lumbridge Castle entrance."),
|
||||
new CrypticClue("Search the chests upstairs in Al Kharid Palace.", CLOSED_CHEST_375, new WorldPoint(3301, 3169, 1), "The chest is located, in the northeast corner, on the first floor of the Al Kharid Palace."),
|
||||
new CrypticClue("Search the boxes just outside the Armour shop in East Ardougne.", BOXES_361, new WorldPoint(2654, 3299, 0), "Outside Zenesha's Plate Mail Body Shop in East Ardougne."),
|
||||
new CrypticClue("Surrounded by white walls and gems.", "Herquin", new WorldPoint(2945, 3335, 0), "Talk to Herquin, the gem store owner in Falador."),
|
||||
new CrypticClue("Monk's residence in the far west. See robe storage device.", DRAWERS_350, new WorldPoint(1746, 3490, 0), "Search the drawers in the south tent of the monk's camp on the southern coast of Hosidius, directly south of the player-owned house portal."),
|
||||
new CrypticClue("Search the drawers in Catherby's Archery shop.", DRAWERS_350, new WorldPoint(2825, 3442, 0), "Hickton's Archery Emporium in Catherby."),
|
||||
new CrypticClue("The hand ain't listening.", "The Face", new WorldPoint(3019, 3232, 0), "Talk to The Face located by the manhole just north of the Port Sarim fishing shop."),
|
||||
new CrypticClue("Search the chest in the left-hand tower of Camelot Castle.", CLOSED_CHEST_25592, new WorldPoint(2748, 3495, 2), "Located on the second floor of the western tower of Camelot."),
|
||||
new CrypticClue("Anger those who adhere to Saradomin's edicts to prevent travel.", "Monk of Entrana", new WorldPoint(3042, 3236, 0), "Port Sarim Docks, try to charter a ship to Entrana with armour or weapons equipped."),
|
||||
new CrypticClue("South of a river in a town surrounded by the undead, what lies beneath the furnace?", new WorldPoint(2857, 2966, 0), "Dig in front of the Shilo Village furnace."),
|
||||
new CrypticClue("Talk to the Squire in the White Knights' castle in Falador.", "Squire", new WorldPoint(2977, 3343, 0), "The squire is located in the courtyard of the White Knights' Castle."),
|
||||
new CrypticClue("Thanks, Grandma!", "Tynan", new WorldPoint(1836, 3786, 0), "Tynan can be found in the north-east corner of Port Piscarilius."),
|
||||
new CrypticClue("In a town where everyone has perfect vision, seek some locked drawers in a house that sits opposite a workshop.", "Chicken", DRAWERS_25766, new WorldPoint(2709, 3478, 0), "The Seers' Village house south of the Elemental Workshop entrance. Kill any Chicken to obtain a key."),
|
||||
new CrypticClue("The treasure is buried in a small building full of bones. Here is a hint: it's not near a graveyard.", new WorldPoint(3356, 3507, 0), "In the western building near the Limestone quarry east of Varrock. Dig south of the box of bones in the smaller building."),
|
||||
new CrypticClue("Search the crates in East Ardougne's general store.", CRATE_357, new WorldPoint(2615, 3291, 0), "Located south of the Ardougne church."),
|
||||
new CrypticClue("Come brave adventurer, your sense is on fire. If you talk to me, it's an old god you desire.", "Viggora", null, "Speak to Viggora while wearing a ring of visibility and a Ghostspeak amulet."),
|
||||
new CrypticClue("2 musical birds. Dig in front of the spinning light.", new WorldPoint(2671, 10396, 0), "Dig in front of the spinning light in Ping and Pong's room inside the Iceberg"),
|
||||
new CrypticClue("Search the wheelbarrow in Rimmington mine.", WHEELBARROW_9625, new WorldPoint(2978, 3239, 0), "The Rimmington mining site is located north of Rimmington."),
|
||||
new CrypticClue("Belladonna, my dear. If only I had gloves, then I could hold you at last.", "Tool Leprechaun", new WorldPoint(3088, 3357, 0), "Talk to Tool Leprechaun at Draynor Manor."),
|
||||
new CrypticClue("Impossible to make angry", "Abbot Langley", new WorldPoint(3059, 3486, 0), "Speak to Abbot Langley at the Edgeville Monastery."),
|
||||
new CrypticClue("Search the crates in Horvik's armoury.", CRATE_5106, new WorldPoint(3228, 3433, 0), "Horvik's in Varrock."),
|
||||
new CrypticClue("Ghommal wishes to be impressed by how strong your equipment is.", "Ghommal", new WorldPoint(2878, 3546, 0), "Speak to Ghommal at the Warriors' Guild with a total Melee Strength bonus of over 100."),
|
||||
new CrypticClue("Shhhh!", "Logosia", new WorldPoint(1633, 3808, 0), "Speak to Logosia in the Arceuus Library's ground floor."),
|
||||
new CrypticClue("Salty peter.", "Konoo", new WorldPoint(1703, 3524, 0), "Talk to Konoo who is digging saltpetre in Hosidius, north-east of the Woodcutting Guild."),
|
||||
new CrypticClue("Talk to Zeke in Al Kharid.", "Zeke", new WorldPoint(3287, 3190, 0), "Zeke is the owner of the scimitar shop in Al Kharid."),
|
||||
new CrypticClue("Guthix left his mark in a fiery lake, dig at the tip of it.", new WorldPoint(3069, 3935, 0), "Dig at the tip of the lava lake that is shaped like a Guthixian symbol, west of the Mage Arena."),
|
||||
new CrypticClue("Search the drawers in the upstairs of a house in Catherby.", DRAWERS_350, new WorldPoint(2809, 3451, 1), "Perdu's house in Catherby."),
|
||||
new CrypticClue("Search a crate in the Haymaker's arms.", CRATE_27532, new WorldPoint(1720, 3652, 1), "Search the crate in the north-east corner of The Haymaker's Arms tavern east of Kourend Castle."),
|
||||
new CrypticClue("Desert insects is what I see. Taking care of them was my responsibility. Your solution is found by digging near me.", new WorldPoint(3307, 9505, 0), "Dig next to the Entomologist, Kalphite area, near Shantay Pass."),
|
||||
new CrypticClue("Search the crates in the most north-western house in Al Kharid.", CRATE_358, new WorldPoint(3289, 3202, 0), "Search the crates in the house, marked with a cooking range icon, southeast of the gem stall in Al Kharid."),
|
||||
new CrypticClue("You will have to fly high where a sword cannot help you.", null, "Kill an Aviansie."),
|
||||
new CrypticClue("A massive battle rages beneath so be careful when you dig by the large broken crossbow.", new WorldPoint(2927, 3761, 0), "NE of the God Wars Dungeon entrance, climb the rocky handholds & dig by large crossbow."),
|
||||
new CrypticClue("Mix yellow with blue and add heat, make sure you bring protection.", null, "Kill a green or brutal green dragon."),
|
||||
new CrypticClue("Speak to Ellis in Al Kharid.", "Ellis", new WorldPoint(3276, 3191, 0), "Ellis is tanner just north of Al Kharid bank."),
|
||||
new CrypticClue("Search the chests in the Dwarven Mine.", CLOSED_CHEST_375, new WorldPoint(3000, 9798, 0), "The chest is on the western wall, where Hura's Crossbow Shop is, in the Dwarven Mine."),
|
||||
new CrypticClue("In a while...", null, "Kill a crocodile."),
|
||||
new CrypticClue("A chisel and hammer reside in his home, strange for one of magic. Impress him with your magical equipment.", "Wizard Cromperty", new WorldPoint(2682, 3325, 0), "Wizard Cromperty, NE corner of East Ardougne. +100 magic attack bonus needed"),
|
||||
new CrypticClue("You have all of the elements available to solve this clue. Fortunately you do not have to go as far as to stand in a draft.", CRATE_18506, new WorldPoint(2723, 9891, 0), "Search the crate, west of the Air Elementals, inside the Elemental Workshop."),
|
||||
new CrypticClue("A demon's best friend holds the next step of this clue.", null, "Kill a hellhound."),
|
||||
new CrypticClue("Dig in the centre of a great kingdom of 5 cities.", new WorldPoint(1639, 3673, 0), "Dig in front of the large statue in the centre of Great Kourend."),
|
||||
new CrypticClue("Hopefully this set of armour will help you to keep surviving.", "Sir Vyvin", new WorldPoint(2982, 3336, 2), "Speak to Sir Vyvin, located in the White Knight's Castle, while wearing a white platebody and platelegs."),
|
||||
new CrypticClue("The beasts retreat, for their Queen is gone; the song of this town still plays on. Dig near the birthplace of a blade, be careful not to melt your spade.", new WorldPoint(2342, 3677, 0), "Dig in front of the small furnace in the Piscatoris Fishing Colony."),
|
||||
new CrypticClue("Darkness wanders around me, but fills my mind with knowledge.", "Biblia", new WorldPoint(1633, 3825, 2), "Speak to Biblia on the Arceuus Library's top floor."),
|
||||
new CrypticClue("I would make a chemistry joke, but I'm afraid I wouldn't get a reaction.", "Chemist", new WorldPoint(2932, 3212, 0), "Talk to the Chemist in Rimmington"),
|
||||
new CrypticClue("Show this to Hazelmere.", "Hazelmere", new WorldPoint(2677, 3088, 1), "Located upstairs in the house east of Yanille, north of fairy ring CLS."),
|
||||
new CrypticClue("Does one really need a fire to stay warm here?", new WorldPoint(3816, 3810, 0), "Dig next to the fire near the Volcanic Mine entrance on Fossil Island."),
|
||||
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 east of Yanille, 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."),
|
||||
new CrypticClue("In a village of barbarians, I am the one who guards the village from up high.", "Hunding", new WorldPoint(3097, 3432, 2), "Talk to Hunding atop the tower on the east side of Barbarian Village."),
|
||||
new CrypticClue("Talk to Charlie the Tramp in Varrock.", "Charlie the Tramp", new WorldPoint(3209, 3390, 0), "Talk to Charlie the Tramp by the southern entrance to Varrock. He will give you a task."),
|
||||
new CrypticClue("Near the open desert I reside, to get past me you must abide. Go forward if you dare, for when you pass me, you'll be sweating by your hair.", "Shantay", new WorldPoint(3303, 3123, 0), "Talk to Shantay at the Shantay Pass south of Al Kharid."),
|
||||
new CrypticClue("Search the chest in Fred the Farmer's bedroom.", CLOSED_CHEST_375, new WorldPoint(3185, 3274, 0), "Search the chest by Fred the Farmer's bed in his house north-west of Lumbridge."),
|
||||
new CrypticClue("Search the eastern bookcase in Father Urhney's house.", BOOKCASE_9523, new WorldPoint(3149, 3177, 0), "Father Urhney's house is found in the western end of the Lumbridge Swamp."),
|
||||
new CrypticClue("Talk to Morgan in his house at Draynor Village.", "Morgan", new WorldPoint(3098, 3268, 0), "Morgan can be found in the house with the quest start map icon in Northern Draynor Village."),
|
||||
new CrypticClue("Talk to Charles at Port Piscarilius.", "Charles", new WorldPoint(1821, 3690, 0), "Charles is found by Veos' ship in Port Piscarilius."),
|
||||
new CrypticClue("Search the crate in Rommiks crafting shop in Rimmington.", CRATE_9533, new WorldPoint(2946, 3207, 0), "The crates in Rommik's Crafty Supplies in Rimmington."),
|
||||
new CrypticClue("Talk to Ali the Leaflet Dropper north of the Al Kharid mine.", "Ali the Leaflet Dropper", new WorldPoint(3283, 3329, 0), "Ali the Leaflet Dropper can be found roaming north of the Al Kharid mine."),
|
||||
new CrypticClue("Talk to the cook in the Blue Moon Inn in Varrock.", "Cook", new WorldPoint(3230, 3401, 0), "The Blue Moon Inn can be found by the southern entrance to Varrock."),
|
||||
new CrypticClue("Search the single crate in Horvik's smithy in Varrock.", CRATE_5106, new WorldPoint(3228, 3433, 0), "Horvik's Smithy is found north-east of of Varrock Square."),
|
||||
new CrypticClue("Search the crates in Falador General store.", CRATES_24088, new WorldPoint(2955, 3390, 0), "The Falador General Store can be found by the northern entrance to the city."),
|
||||
new CrypticClue("Talk to Wayne at Wayne's Chains in Falador.", "Wayne", new WorldPoint(2972, 3312, 0), "Wayne's shop is found directly south of the White Knights' Castle."),
|
||||
new CrypticClue("Search the boxes next to a chest that needs a crystal key.", BOXES_360, new WorldPoint(2915, 3452, 0), "The Crystal chest can be found in the house directly south of the Witch's house in Taverley."),
|
||||
new CrypticClue("Talk to Turael in Burthorpe.", "Turael", new WorldPoint(2930, 3536, 0), "Turael is located in the small house east of the Toad and Chicken inn in Burthorpe."),
|
||||
new CrypticClue("More resources than I can handle, but in a very dangerous area. Can't wait to strike gold!", new WorldPoint(3183, 3941, 0), "Dig between the three gold ores in the Wilderness Resource Area."),
|
||||
new CrypticClue("Observing someone in a swamp, under the telescope lies treasure.", new WorldPoint(2221, 3091, 0), "Dig next to the telescope on Broken Handz's island in the poison wastes. (Accessible only through fairy ring DLR)"),
|
||||
new CrypticClue("A general who sets a 'shining' example.", "General Hining", new WorldPoint(2186, 3148, 0), "Talk to General Hining in Tyras Camp."),
|
||||
new CrypticClue("Has no one told you it is rude to ask a lady her age?", "Mawrth", new WorldPoint(2333, 3165, 0), "Talk to Mawrth in Lletya."),
|
||||
new CrypticClue("Elvish onions.", new WorldPoint(3303, 6092, 0), "Dig in the onion patch east of the Prifddinas allotments.")
|
||||
);
|
||||
|
||||
private final String text;
|
||||
private final String npc;
|
||||
private final int objectId;
|
||||
private final WorldPoint location;
|
||||
private final String solution;
|
||||
@Nullable
|
||||
private final String questionText;
|
||||
|
||||
private CrypticClue(String text, WorldPoint location, String solution)
|
||||
{
|
||||
this(text, null, -1, location, solution);
|
||||
}
|
||||
|
||||
private CrypticClue(String text, int objectId, WorldPoint location, String solution)
|
||||
{
|
||||
this(text, null, objectId, location, solution, null);
|
||||
}
|
||||
|
||||
private CrypticClue(String text, String npc, WorldPoint location, String solution)
|
||||
{
|
||||
this(text, npc, -1, location, solution, null);
|
||||
}
|
||||
|
||||
private CrypticClue(String text, String npc, WorldPoint location, String solution, boolean requiresLight)
|
||||
{
|
||||
this(text, npc, location, solution);
|
||||
setRequiresLight(requiresLight);
|
||||
}
|
||||
|
||||
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, null);
|
||||
}
|
||||
|
||||
private CrypticClue(String text, String npc, int objectId, WorldPoint location, String solution, @Nullable 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Cryptic Clue").build());
|
||||
|
||||
if (getNpc() != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("NPC:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getNpc())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
}
|
||||
|
||||
if (objectId != -1)
|
||||
{
|
||||
ObjectComposition object = plugin.getClient().getObjectDefinition(objectId);
|
||||
|
||||
if (object != null && object.getImpostorIds() != null)
|
||||
{
|
||||
object = object.getImpostor();
|
||||
}
|
||||
|
||||
if (object != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Object:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(object.getName())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Solution:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getSolution())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
// Mark dig location
|
||||
if (getLocation() != null && getNpc() == null && objectId == -1)
|
||||
{
|
||||
LocalPoint localLocation = LocalPoint.fromWorld(plugin.getClient(), getLocation());
|
||||
|
||||
if (localLocation != null)
|
||||
{
|
||||
OverlayUtil.renderTileOverlay(plugin.getClient(), graphics, localLocation, plugin.getSpadeImage(), Color.ORANGE);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark NPC
|
||||
if (plugin.getNpcsToMark() != null)
|
||||
{
|
||||
for (NPC npc : plugin.getNpcsToMark())
|
||||
{
|
||||
OverlayUtil.renderActorOverlayImage(graphics, npc, plugin.getClueScrollImage(), Color.ORANGE, IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark game object
|
||||
if (objectId != -1)
|
||||
{
|
||||
net.runelite.api.Point mousePosition = plugin.getClient().getMouseCanvasPosition();
|
||||
|
||||
if (plugin.getObjectsToMark() != null)
|
||||
{
|
||||
for (TileObject gameObject : plugin.getObjectsToMark())
|
||||
{
|
||||
OverlayUtil.renderHoverableArea(graphics, gameObject.getClickbox(), mousePosition,
|
||||
CLICKBOX_FILL_COLOR, CLICKBOX_BORDER_COLOR, CLICKBOX_HOVER_BORDER_COLOR);
|
||||
|
||||
OverlayUtil.renderImageLocation(plugin.getClient(), graphics, gameObject.getLocalLocation(), plugin.getClueScrollImage(), IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static CrypticClue forText(String text)
|
||||
{
|
||||
for (CrypticClue clue : CLUES)
|
||||
{
|
||||
if (text.equalsIgnoreCase(clue.text) || text.equalsIgnoreCase(clue.questionText))
|
||||
{
|
||||
return clue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getObjectIds()
|
||||
{
|
||||
return new int[] {objectId};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNpcs()
|
||||
{
|
||||
return new String[] {npc};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Polygon;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Client;
|
||||
import static net.runelite.api.EquipmentInventorySlot.*;
|
||||
import static net.runelite.api.EquipmentInventorySlot.LEGS;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemID;
|
||||
import static net.runelite.api.ItemID.*;
|
||||
import net.runelite.api.Perspective;
|
||||
import net.runelite.api.ScriptID;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.emote.Emote;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.*;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.BULL_ROARER;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.*;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SHANTAY_PASS;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirements.*;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.Enemy.*;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
@Getter
|
||||
public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClueScroll
|
||||
{
|
||||
private static final Set<EmoteClue> CLUES = ImmutableSet.of(
|
||||
new EmoteClue("Beckon on the east coast of the Kharazi Jungle. Beware of double agents! Equip any vestment stole and a heraldic rune shield.", "Kharazi Jungle", NORTHEAST_CORNER_OF_THE_KHARAZI_JUNGLE, new WorldPoint(2954, 2933, 0), DOUBLE_AGENT_108, BECKON, any("Any stole", item(GUTHIX_STOLE), item(SARADOMIN_STOLE), item(ZAMORAK_STOLE), item(ARMADYL_STOLE), item(BANDOS_STOLE), item(ANCIENT_STOLE)), any("Any heraldic rune shield", item(RUNE_SHIELD_H1), item(RUNE_SHIELD_H2), item(RUNE_SHIELD_H3), item(RUNE_SHIELD_H4), item(RUNE_SHIELD_H5))),
|
||||
new EmoteClue("Cheer in the Barbarian Agility Arena. Headbang before you talk to me. Equip a steel platebody, maple shortbow and a Wilderness cape.", "Barbarian Outpost", BARBARIAN_OUTPOST_OBSTACLE_COURSE, new WorldPoint(2552, 3556, 0), CHEER, HEADBANG, item(STEEL_PLATEBODY), item(MAPLE_SHORTBOW), range("Any team cape", TEAM1_CAPE, TEAM50_CAPE)),
|
||||
new EmoteClue("Bow upstairs in the Edgeville Monastery. Equip a completed prayer book.", "Edgeville Monastery", SOUTHEAST_CORNER_OF_THE_MONASTERY, new WorldPoint(3056, 3484, 1), BOW, any("Any god book", item(HOLY_BOOK), item(BOOK_OF_BALANCE), item(UNHOLY_BOOK), item(BOOK_OF_LAW), item(BOOK_OF_WAR), item(BOOK_OF_DARKNESS))),
|
||||
new EmoteClue("Cheer in the Shadow dungeon. Equip a rune crossbow, climbing boots and any mitre.", "Shadow dungeon", ENTRANCE_OF_THE_CAVE_OF_DAMIS, new WorldPoint(2629, 5071, 0), CHEER, any("Any mitre", item(GUTHIX_MITRE), item(SARADOMIN_MITRE), item(ZAMORAK_MITRE), item(ANCIENT_MITRE), item(BANDOS_MITRE), item(ARMADYL_MITRE)), item(RUNE_CROSSBOW), item(CLIMBING_BOOTS), item(RING_OF_VISIBILITY)),
|
||||
new EmoteClue("Cheer at the top of the agility pyramid. Beware of double agents! Equip a blue mystic robe top, and any rune heraldic shield.", "Agility Pyramid", AGILITY_PYRAMID, new WorldPoint(3043, 4697, 3), DOUBLE_AGENT_108, CHEER, item(MYSTIC_ROBE_TOP), any("Any rune heraldic shield", item(RUNE_SHIELD_H1), item(RUNE_SHIELD_H2), item(RUNE_SHIELD_H3), item(RUNE_SHIELD_H4), item(RUNE_SHIELD_H5))),
|
||||
new EmoteClue("Dance in Iban's temple. Beware of double agents! Equip Iban's staff, a black mystic top and a black mystic bottom.", "Iban's temple", WELL_OF_VOYAGE, new WorldPoint(2011, 4712, 0), DOUBLE_AGENT_141, DANCE, any("Any iban's staff", item(IBANS_STAFF), item(IBANS_STAFF_U)), item(MYSTIC_ROBE_TOP_DARK), item(MYSTIC_ROBE_BOTTOM_DARK)),
|
||||
new EmoteClue("Dance on the Fishing Platform. Equip barrows gloves, an amulet of glory and a dragon med helm.", "Fishing Platform", SOUTHEAST_CORNER_OF_THE_FISHING_PLATFORM, new WorldPoint(2782, 3273, 0), DANCE, any("Any amulet of glory", item(AMULET_OF_GLORY), item(AMULET_OF_GLORY1), item(AMULET_OF_GLORY2), item(AMULET_OF_GLORY3), item(AMULET_OF_GLORY4), item(AMULET_OF_GLORY5), item(AMULET_OF_GLORY6)), item(BARROWS_GLOVES), item(DRAGON_MED_HELM)),
|
||||
new EmoteClue("Flap at the death altar. Beware of double agents! Equip a death tiara, a legend's cape and any ring of wealth.", "Death altar", DEATH_ALTAR, new WorldPoint(2205, 4838, 0), DOUBLE_AGENT_141, FLAP, any("Any ring of wealth", item(RING_OF_WEALTH), item(RING_OF_WEALTH_1), item(RING_OF_WEALTH_2), item(RING_OF_WEALTH_3), item(RING_OF_WEALTH_4), item(RING_OF_WEALTH_5), item(RING_OF_WEALTH_I), item(RING_OF_WEALTH_I1), item(RING_OF_WEALTH_I2), item(RING_OF_WEALTH_I3), item(RING_OF_WEALTH_I4), item(RING_OF_WEALTH_I5)), item(DEATH_TIARA), item(CAPE_OF_LEGENDS)),
|
||||
new EmoteClue("Headbang in the Fight Arena pub. Equip a pirate bandana, a dragonstone necklace and and a magic longbow.", "Fight Arena pub", OUTSIDE_THE_BAR_BY_THE_FIGHT_ARENA, new WorldPoint(2568, 3149, 0), HEADBANG, any("Any pirate bandana", item(PIRATE_BANDANA), item(PIRATE_BANDANA_7124), item(PIRATE_BANDANA_7130), item(PIRATE_BANDANA_7136)), item(DRAGON_NECKLACE), item(MAGIC_LONGBOW)),
|
||||
new EmoteClue("Do a jig at the barrows chest. Beware of double agents! Equip any full barrows set.", "Barrows chest", BARROWS_CHEST, new WorldPoint(3551, 9694, 0), DOUBLE_AGENT_141, JIG, any("Any full barrows set", all(any("Ahrim's hood", item(AHRIMS_HOOD), range(AHRIMS_HOOD_100, AHRIMS_HOOD_0)), any("Ahrim's staff", item(AHRIMS_STAFF), range(AHRIMS_STAFF_100, AHRIMS_STAFF_0)), any("Ahrim's robetop", item(AHRIMS_ROBETOP), range(AHRIMS_ROBETOP_100, AHRIMS_ROBETOP_0)), any("Ahrim's robeskirt", item(AHRIMS_ROBESKIRT), range(AHRIMS_ROBESKIRT_100, AHRIMS_ROBESKIRT_0))), all(any("Dharok's helm", item(DHAROKS_HELM), range(DHAROKS_HELM_100, DHAROKS_HELM_0)), any("Dharok's greataxe", item(DHAROKS_GREATAXE), range(DHAROKS_GREATAXE_100, DHAROKS_GREATAXE_0)), any("Dharok's platebody", item(DHAROKS_PLATEBODY), range(DHAROKS_PLATEBODY_100, DHAROKS_PLATEBODY_0)), any("Dharok's platelegs", item(DHAROKS_PLATELEGS), range(DHAROKS_PLATELEGS_100, DHAROKS_PLATELEGS_0))), all(any("Guthan's helm", item(GUTHANS_HELM), range(GUTHANS_HELM_100, GUTHANS_HELM_0)), any("Guthan's warspear", item(GUTHANS_WARSPEAR), range(GUTHANS_WARSPEAR_100, GUTHANS_WARSPEAR_0)), any("Guthan's platebody", item(GUTHANS_PLATEBODY), range(GUTHANS_PLATEBODY_100, GUTHANS_PLATEBODY_0)), any("Guthan's chainskirt", item(GUTHANS_CHAINSKIRT), range(GUTHANS_CHAINSKIRT_100, GUTHANS_CHAINSKIRT_0))), all(any("Karil's coif", item(KARILS_COIF), range(KARILS_COIF_100, KARILS_COIF_0)), any("Karil's crossbow", item(KARILS_CROSSBOW), range(KARILS_CROSSBOW_100, KARILS_CROSSBOW_0)), any("Karil's leathertop", item(KARILS_LEATHERTOP), range(KARILS_LEATHERTOP_100, KARILS_LEATHERTOP_0)), any("Karil's leatherskirt", item(KARILS_LEATHERSKIRT), range(KARILS_LEATHERSKIRT_100, KARILS_LEATHERSKIRT_0))), all(any("Torag's helm", item(TORAGS_HELM), range(TORAGS_HELM_100, TORAGS_HELM_0)), any("Torag's hammers", item(TORAGS_HAMMERS), range(TORAGS_HAMMERS_100, TORAGS_HAMMERS_0)), any("Torag's platebody", item(TORAGS_PLATEBODY), range(TORAGS_PLATEBODY_100, TORAGS_PLATEBODY_0)), any("Torag's platelegs", item(TORAGS_PLATELEGS), range(TORAGS_PLATELEGS_100, TORAGS_PLATELEGS_0))), all(any("Verac's helm", item(VERACS_HELM), range(VERACS_HELM_100, VERACS_HELM_0)), any("Verac's flail", item(VERACS_FLAIL), range(VERACS_FLAIL_100, VERACS_FLAIL_0)), any("Verac's brassard", item(VERACS_BRASSARD), range(VERACS_BRASSARD_100, VERACS_BRASSARD_0)), any("Verac's plateskirt", item(VERACS_PLATESKIRT), range(VERACS_PLATESKIRT_100, VERACS_PLATESKIRT_0))))),
|
||||
new EmoteClue("Jig at Jiggig. Beware of double agents! Equip a Rune spear, rune platelegs and any rune heraldic helm.", "Jiggig", IN_THE_MIDDLE_OF_JIGGIG, new WorldPoint(2477, 3047, 0), DOUBLE_AGENT_108, JIG, range("Any rune heraldic helm", RUNE_HELM_H1, RUNE_HELM_H5), item(RUNE_SPEAR), item(RUNE_PLATELEGS)),
|
||||
new EmoteClue("Cheer at the games room. Have nothing equipped at all when you do.", "Games room", null, new WorldPoint(2207, 4952, 0), CHEER, emptySlot("Nothing at all", HEAD, CAPE, AMULET, WEAPON, BODY, SHIELD, LEGS, GLOVES, BOOTS, RING, AMMO)),
|
||||
new EmoteClue("Panic on the pier where you catch the Fishing trawler. Have nothing equipped at all when you do.", "Fishing trawler", null, new WorldPoint(2676, 3169, 0), PANIC, emptySlot("Nothing at all", HEAD, CAPE, AMULET, WEAPON, BODY, SHIELD, LEGS, GLOVES, BOOTS, RING, AMMO)),
|
||||
new EmoteClue("Panic in the heart of the Haunted Woods. Beware of double agents! Have no items equipped when you do.", "Haunted Woods (ALQ)", null, new WorldPoint(3611, 3492, 0), DOUBLE_AGENT_108, PANIC, emptySlot("Nothing at all", HEAD, CAPE, AMULET, WEAPON, BODY, SHIELD, LEGS, GLOVES, BOOTS, RING, AMMO)),
|
||||
new EmoteClue("Show your anger towards the Statue of Saradomin in Ellamaria's garden. Beware of double agents! Equip a zamorak godsword.", "Varrock Castle", BY_THE_BEAR_CAGE_IN_VARROCK_PALACE_GARDENS, new WorldPoint(3230, 3478, 0), DOUBLE_AGENT_141, ANGRY, any("Zamorak godsword", item(ZAMORAK_GODSWORD), item(ZAMORAK_GODSWORD_OR))),
|
||||
new EmoteClue("Show your anger at the Wise old man. Beware of double agents! Equip an abyssal whip, a legend's cape and some spined chaps.", "Draynor Village", BEHIND_MISS_SCHISM_IN_DRAYNOR_VILLAGE, new WorldPoint(3088, 3254, 0), DOUBLE_AGENT_141, ANGRY, any("Abyssal whip", item(ABYSSAL_WHIP), item(VOLCANIC_ABYSSAL_WHIP), item(FROZEN_ABYSSAL_WHIP)), item(CAPE_OF_LEGENDS), item(SPINED_CHAPS)),
|
||||
new EmoteClue("Beckon by a collection of crystalline maple trees. Beware of double agents! Equip Bryophyta's staff and a nature tiara.", "North of Prifddinas", CRYSTALLINE_MAPLE_TREES, new WorldPoint(2211, 3427, 0), DOUBLE_AGENT_141, BECKON, range("Bryophyta's staff", BRYOPHYTAS_STAFF_UNCHARGED, BRYOPHYTAS_STAFF), item(NATURE_TIARA)),
|
||||
new EmoteClue("Beckon in the Digsite, near the eastern winch. Bow before you talk to me. Equip a green gnome hat, snakeskin boots and an iron pickaxe.", "Digsite", DIGSITE, new WorldPoint(3370, 3425, 0), BECKON, BOW, item(GREEN_HAT), item(SNAKESKIN_BOOTS), item(IRON_PICKAXE)),
|
||||
new EmoteClue("Beckon in Tai Bwo Wannai. Clap before you talk to me. Equip green dragonhide chaps, a ring of dueling and a mithril medium helmet.", "Tai Bwo Wannai", SOUTH_OF_THE_SHRINE_IN_TAI_BWO_WANNAI_VILLAGE, new WorldPoint(2803, 3073, 0), BECKON, CLAP, item(GREEN_DHIDE_CHAPS), any("Ring of dueling", item(RING_OF_DUELING1), item(RING_OF_DUELING2), item(RING_OF_DUELING3), item(RING_OF_DUELING4), item(RING_OF_DUELING5), item(RING_OF_DUELING6), item(RING_OF_DUELING7), item(RING_OF_DUELING8)), item(MITHRIL_MED_HELM)),
|
||||
new EmoteClue("Beckon in the combat ring of Shayzien. Show your anger before you talk to me. Equip an adamant platebody, adamant full helm and adamant platelegs.", "Shayzien combat ring", WEST_OF_THE_SHAYZIEN_COMBAT_RING, new WorldPoint(1545, 3594, 0), BECKON, ANGRY, item(ADAMANT_PLATELEGS), item(ADAMANT_PLATEBODY), item(ADAMANT_FULL_HELM)),
|
||||
new EmoteClue("Bow near Lord Iorwerth. Beware of double agents! Equip a charged crystal bow.", "Lord Iorwerth's camp", TENT_IN_LORD_IORWERTHS_ENCAMPMENT, new WorldPoint(2205, 3252, 0), DOUBLE_AGENT_141, BOW, any("Crystal Bow", item(CRYSTAL_BOW), item(CRYSTAL_BOW_24123))),
|
||||
new EmoteClue("Bow in the Iorwerth Camp. Beware of double agents! Equip a charged crystal bow.", "Lord Iorwerth's camp", TENT_IN_LORD_IORWERTHS_ENCAMPMENT, new WorldPoint(2205, 3252, 0), DOUBLE_AGENT_141, BOW, any("Crystal Bow", item(CRYSTAL_BOW), item(CRYSTAL_BOW_24123))),
|
||||
new EmoteClue("Bow outside the entrance to the Legends' Guild. Equip iron platelegs, an emerald amulet and an oak longbow.", "Legend's Guild", OUTSIDE_THE_LEGENDS_GUILD_GATES, new WorldPoint(2729, 3349, 0), BOW, item(IRON_PLATELEGS), item(OAK_LONGBOW), item(EMERALD_AMULET)),
|
||||
new EmoteClue("Bow on the ground floor of the Legend's guild. Equip Legend's cape, a dragon battleaxe and an amulet of glory.", "Legend's Guild", OUTSIDE_THE_LEGENDS_GUILD_DOOR, new WorldPoint(2728, 3377, 0), BOW, item(CAPE_OF_LEGENDS), item(DRAGON_BATTLEAXE), any("Any amulet of glory", item(AMULET_OF_GLORY), item(AMULET_OF_GLORY1), item(AMULET_OF_GLORY2), item(AMULET_OF_GLORY3), item(AMULET_OF_GLORY4), item(AMULET_OF_GLORY5), item(AMULET_OF_GLORY6))),
|
||||
new EmoteClue("Bow in the ticket office of the Duel Arena. Equip an iron chain body, leather chaps and coif.", "Duel Arena", MUBARIZS_ROOM_AT_THE_DUEL_ARENA, new WorldPoint(3314, 3241, 0), BOW, item(IRON_CHAINBODY), item(LEATHER_CHAPS), item(COIF)),
|
||||
new EmoteClue("Bow at the top of the lighthouse. Beware of double agents! Equip a blue dragonhide body, blue dragonhide vambraces and no jewelry.", "Lighthouse", TOP_FLOOR_OF_THE_LIGHTHOUSE, new WorldPoint(2511, 3641, 2), DOUBLE_AGENT_108, BOW, item(BLUE_DHIDE_BODY), item(BLUE_DHIDE_VAMBRACES), emptySlot("No jewellery", AMULET, RING)),
|
||||
new EmoteClue("Blow a kiss between the tables in Shilo Village bank. Beware of double agents! Equip a blue mystic hat, bone spear and rune platebody.", "Shilo Village", SHILO_VILLAGE_BANK, new WorldPoint(2851, 2954, 0), DOUBLE_AGENT_108, BLOW_KISS, item(MYSTIC_HAT), item(BONE_SPEAR), item(RUNE_PLATEBODY)),
|
||||
new EmoteClue("Blow a kiss in the heart of the lava maze. Equip black dragonhide chaps, a spotted cape and a rolling pin.", "Lava maze", NEAR_A_LADDER_IN_THE_WILDERNESS_LAVA_MAZE, new WorldPoint(3069, 3861, 0), BLOW_KISS, item(BLACK_DHIDE_CHAPS), any("Spotted cape", item(SPOTTED_CAPE), item(SPOTTED_CAPE_10073)), item(ROLLING_PIN)),
|
||||
new EmoteClue("Blow a kiss outside K'ril Tsutsaroth's chamber. Beware of double agents! Equip a zamorak full helm and the shadow sword.", "K'ril's chamber", OUTSIDE_KRIL_TSUTSAROTHS_ROOM, new WorldPoint(2925, 5333, 2), DOUBLE_AGENT_141, BLOW_KISS, item(ZAMORAK_FULL_HELM), item(SHADOW_SWORD)),
|
||||
new EmoteClue("Cheer at the Druids' Circle. Equip a blue wizard hat, a bronze two-handed sword and HAM boots.", "Taverley stone circle", TAVERLEY_STONE_CIRCLE, new WorldPoint(2924, 3478, 0), CHEER, item(BLUE_WIZARD_HAT), item(BRONZE_2H_SWORD), item(HAM_BOOTS)),
|
||||
new EmoteClue("Cheer in the Edgeville general store. Dance before you talk to me. Equip a brown apron, leather boots and leather gloves.", "Edgeville", NORTH_OF_EVIL_DAVES_HOUSE_IN_EDGEVILLE, new WorldPoint(3080, 3509, 0), CHEER, DANCE, item(BROWN_APRON), item(LEATHER_BOOTS), item(LEATHER_GLOVES)),
|
||||
new EmoteClue("Cheer in the Ogre Pen in the Training Camp. Show you are angry before you talk to me. Equip a green dragonhide body and chaps and a steel square shield.", "King Lathas' camp", OGRE_CAGE_IN_KING_LATHAS_TRAINING_CAMP, new WorldPoint(2527, 3375, 0), CHEER, ANGRY, item(GREEN_DHIDE_BODY), item(GREEN_DHIDE_CHAPS), item(STEEL_SQ_SHIELD)),
|
||||
new EmoteClue("Cheer in the Entrana church. Beware of double agents! Equip a full set of black dragonhide armour.", "Entrana church", ENTRANA_CHAPEL, new WorldPoint(2852, 3349, 0), DOUBLE_AGENT_141, CHEER, item(BLACK_DHIDE_VAMBRACES), item(BLACK_DHIDE_CHAPS), item(BLACK_DHIDE_BODY)),
|
||||
new EmoteClue("Cheer for the monks at Port Sarim. Equip a coif, steel plateskirt and a sapphire necklace.", "Port Sarim", NEAR_THE_ENTRANA_FERRY_IN_PORT_SARIM, new WorldPoint(3047, 3237, 0), CHEER, item(COIF), item(STEEL_PLATESKIRT), item(SAPPHIRE_NECKLACE)),
|
||||
new EmoteClue("Clap in the main exam room in the Exam Centre. Equip a white apron, green gnome boots and leather gloves.", "Exam Centre", OUTSIDE_THE_DIGSITE_EXAM_CENTRE, new WorldPoint(3361, 3339, 0), CLAP, item(WHITE_APRON), item(GREEN_BOOTS), item(LEATHER_GLOVES)),
|
||||
new EmoteClue("Clap on the causeway to the Wizards' Tower. Equip an iron medium helmet, emerald ring and a white apron.", "Wizards' Tower", ON_THE_BRIDGE_TO_THE_MISTHALIN_WIZARDS_TOWER, new WorldPoint(3113, 3196, 0), CLAP, item(IRON_MED_HELM), item(EMERALD_RING), item(WHITE_APRON)),
|
||||
new EmoteClue("Clap on the top level of the mill, north of East Ardougne. Equip a blue gnome robe top, HAM robe bottom and an unenchanted tiara.", "East Ardougne", UPSTAIRS_IN_THE_ARDOUGNE_WINDMILL, new WorldPoint(2635, 3385, 3), CLAP, item(BLUE_ROBE_TOP), item(HAM_ROBE), item(TIARA)),
|
||||
new EmoteClue("Clap in Seers court house. Spin before you talk to me. Equip an adamant halberd, blue mystic robe bottom and a diamond ring.", "Seers Village", OUTSIDE_THE_SEERS_VILLAGE_COURTHOUSE, new WorldPoint(2735, 3469, 0), CLAP, SPIN, item(ADAMANT_HALBERD), item(MYSTIC_ROBE_BOTTOM), item(DIAMOND_RING)),
|
||||
new EmoteClue("Clap in the magic axe hut. Beware of double agents! Equip only some flared trousers.", "Magic axe hut", OUTSIDE_THE_WILDERNESS_AXE_HUT, new WorldPoint(3191, 3960, 0), DOUBLE_AGENT_141, CLAP, item(FLARED_TROUSERS), item(LOCKPICK), emptySlot("Nothing else", HEAD, CAPE, AMULET, WEAPON, BODY, SHIELD, GLOVES, BOOTS, RING, AMMO)),
|
||||
new EmoteClue("Clap your hands north of Mount Karuulm Spin before you talk to me. Equip an adamant warhammer, a ring of life and a pair of mithril boots.", "Mount Karuulm", NORTH_OF_MOUNT_KARUULM, new WorldPoint(1306, 3839, 0), CLAP, SPIN, item(ADAMANT_WARHAMMER), item(RING_OF_LIFE), item(MITHRIL_BOOTS)),
|
||||
new EmoteClue("Cry in the Catherby Ranging shop. Bow before you talk to me. Equip blue gnome boots, a hard leather body and an unblessed silver sickle.", "Catherby", HICKTONS_ARCHERY_EMPORIUM, new WorldPoint(2823, 3443, 0), CRY, BOW, item(BLUE_BOOTS), item(HARDLEATHER_BODY), item(SILVER_SICKLE)),
|
||||
new EmoteClue("Cry on the shore of Catherby beach. Laugh before you talk to me, equip an adamant sq shield, a bone dagger and mithril platebody.", "Catherby", OUTSIDE_HARRYS_FISHING_SHOP_IN_CATHERBY, new WorldPoint(2852, 3429, 0), CRY, LAUGH, item(ADAMANT_SQ_SHIELD), item(BONE_DAGGER), item(MITHRIL_PLATEBODY)),
|
||||
new EmoteClue("Cry on top of the western tree in the Gnome Agility Arena. Indicate 'no' before you talk to me. Equip a steel kiteshield, ring of forging and green dragonhide chaps.", "Gnome Stronghold", GNOME_STRONGHOLD_BALANCING_ROPE, new WorldPoint(2473, 3420, 2), CRY, NO, item(STEEL_KITESHIELD), item(RING_OF_FORGING), item(GREEN_DHIDE_CHAPS)),
|
||||
new EmoteClue("Cry in the TzHaar gem store. Beware of double agents! Equip a fire cape and TokTz-Xil-Ul.", "Tzhaar gem store", TZHAAR_GEM_STORE, new WorldPoint(2463, 5149, 0), DOUBLE_AGENT_141, CRY, any("Fire cape", item(FIRE_CAPE), item(FIRE_CAPE_L), item(FIRE_MAX_CAPE), item(FIRE_MAX_CAPE_L), item(INFERNAL_CAPE), item(INFERNAL_CAPE_L), item(INFERNAL_MAX_CAPE_21285), item(INFERNAL_MAX_CAPE_L)), item(TOKTZXILUL)),
|
||||
new EmoteClue("Cry in the Draynor Village jail. Jump for joy before you talk to me. Equip an adamant sword, a sapphire amulet and an adamant plateskirt.", "Draynor Village jail", OUTSIDE_DRAYNOR_VILLAGE_JAIL, new WorldPoint(3128, 3245, 0), CRY, JUMP_FOR_JOY, item(ADAMANT_SWORD), item(SAPPHIRE_AMULET), item(ADAMANT_PLATESKIRT)),
|
||||
new EmoteClue("Dance at the crossroads north of Draynor. Equip an iron chain body, a sapphire ring and a longbow.", "Draynor Village", CROSSROADS_NORTH_OF_DRAYNOR_VILLAGE, new WorldPoint(3109, 3294, 0), DANCE, item(IRON_CHAINBODY), item(SAPPHIRE_RING), item(LONGBOW)),
|
||||
new EmoteClue("Dance in the Party Room. Equip a steel full helmet, steel platebody and an iron plateskirt.", "Falador Party Room", OUTSIDE_THE_FALADOR_PARTY_ROOM, new WorldPoint(3045, 3376, 0), DANCE, item(STEEL_FULL_HELM), item(STEEL_PLATEBODY), item(IRON_PLATESKIRT)),
|
||||
new EmoteClue("Dance in the shack in Lumbridge Swamp. Equip a bronze dagger, iron full helmet and a gold ring.", "Lumbridge swamp", NEAR_A_SHED_IN_LUMBRIDGE_SWAMP, new WorldPoint(3203, 3169, 0), DANCE, item(BRONZE_DAGGER), item(IRON_FULL_HELM), item(GOLD_RING)),
|
||||
new EmoteClue("Dance in the dark caves beneath Lumbridge Swamp. Blow a kiss before you talk to me. Equip an air staff, Bronze full helm and an amulet of power.", "Lumbridge swamp caves", LUMBRIDGE_SWAMP_CAVES, new WorldPoint(3168, 9571, 0), DANCE, BLOW_KISS, Varbits.FIRE_PIT_LUMBRIDGE_SWAMP, item(STAFF_OF_AIR), item(BRONZE_FULL_HELM), item(AMULET_OF_POWER)),
|
||||
new EmoteClue("Dance at the cat-doored pyramid in Sophanem. Beware of double agents! Equip a ring of life, an uncharged amulet of glory and an adamant two-handed sword.", "Pyramid Of Sophanem", OUTSIDE_THE_GREAT_PYRAMID_OF_SOPHANEM, new WorldPoint(3294, 2781, 0), DOUBLE_AGENT_108, DANCE, item(RING_OF_LIFE), item(AMULET_OF_GLORY), item(ADAMANT_2H_SWORD)),
|
||||
new EmoteClue("Dance in the centre of Canifis. Bow before you talk to me. Equip a green gnome robe top, mithril plate legs and an iron two-handed sword.", "Canifis", CENTRE_OF_CANIFIS, new WorldPoint(3492, 3488, 0), DANCE, BOW, item(GREEN_ROBE_TOP), item(MITHRIL_PLATELEGS), item(IRON_2H_SWORD)),
|
||||
new EmoteClue("Dance in the King Black Dragon's lair. Beware of double agents! Equip a black dragonhide body, black dragonhide vambraces and a black dragon mask.", "King black dragon's lair", KING_BLACK_DRAGONS_LAIR, new WorldPoint(2271, 4680, 0), DOUBLE_AGENT_141, DANCE, item(BLACK_DHIDE_BODY), item(BLACK_DHIDE_VAMBRACES), item(BLACK_DRAGON_MASK)),
|
||||
new EmoteClue("Dance at the entrance to the Grand Exchange. Equip a pink skirt, pink robe top and a body tiara.", "Grand Exchange", SOUTH_OF_THE_GRAND_EXCHANGE, new WorldPoint(3165, 3467, 0), DANCE, item(PINK_SKIRT), item(PINK_ROBE_TOP), item(BODY_TIARA)),
|
||||
new EmoteClue("Goblin Salute in the Goblin Village. Beware of double agents! Equip a bandos godsword, a bandos cloak and a bandos platebody.", "Goblin Village", OUTSIDE_MUDKNUCKLES_HUT, new WorldPoint(2956, 3505, 0), DOUBLE_AGENT_141, GOBLIN_SALUTE, item(BANDOS_PLATEBODY), item(BANDOS_CLOAK), any("Bandos godsword", item(BANDOS_GODSWORD), item(BANDOS_GODSWORD_OR))),
|
||||
new EmoteClue("Headbang in the mine north of Al Kharid. Equip a desert shirt, leather gloves and leather boots.", "Al Kharid mine", AL_KHARID_SCORPION_MINE, new WorldPoint(3299, 3289, 0), HEADBANG, item(DESERT_SHIRT), item(LEATHER_GLOVES), item(LEATHER_BOOTS)),
|
||||
new EmoteClue("Headbang at the exam centre. Beware of double agents! Equip a mystic fire staff, a diamond bracelet and rune boots.", "Exam Centre", INSIDE_THE_DIGSITE_EXAM_CENTRE, new WorldPoint(3362, 3340, 0), DOUBLE_AGENT_108, HEADBANG, item(MYSTIC_FIRE_STAFF), item(DIAMOND_BRACELET), item(RUNE_BOOTS)),
|
||||
new EmoteClue("Headbang at the top of Slayer Tower. Equip a seercull, a combat bracelet and helm of Neitiznot.", "Slayer Tower", OUTSIDE_THE_SLAYER_TOWER_GARGOYLE_ROOM, new WorldPoint(3421, 3537, 2), HEADBANG, item(SEERCULL), range("Combat bracelet", COMBAT_BRACELET4, COMBAT_BRACELET), item(HELM_OF_NEITIZNOT)),
|
||||
new EmoteClue("Dance a jig by the entrance to the Fishing Guild. Equip an emerald ring, a sapphire amulet, and a bronze chain body.", "Fishing Guild", OUTSIDE_THE_FISHING_GUILD, new WorldPoint(2610, 3391, 0), JIG, item(EMERALD_RING), item(SAPPHIRE_AMULET), item(BRONZE_CHAINBODY)),
|
||||
new EmoteClue("Dance a jig under Shantay's Awning. Bow before you talk to me. Equip a pointed blue snail helmet, an air staff and a bronze square shield.", "Shantay Pass", SHANTAY_PASS, new WorldPoint(3304, 3124, 0), JIG, BOW, any("Bruise blue snelm (pointed)", item(BRUISE_BLUE_SNELM_3343)), item(STAFF_OF_AIR), item(BRONZE_SQ_SHIELD)),
|
||||
new EmoteClue("Do a jig in Varrock's rune store. Equip an air tiara and a staff of water.", "Varrock rune store", AUBURYS_SHOP_IN_VARROCK, new WorldPoint(3253, 3401, 0), JIG, item(AIR_TIARA), item(STAFF_OF_WATER)),
|
||||
new EmoteClue("Jump for joy at the beehives. Equip a desert shirt, green gnome robe bottoms and a steel axe.", "Catherby", CATHERBY_BEEHIVE_FIELD, new WorldPoint(2759, 3445, 0), JUMP_FOR_JOY, item(DESERT_SHIRT), item(GREEN_ROBE_BOTTOMS), item(STEEL_AXE)),
|
||||
new EmoteClue("Jump for joy in Yanille bank. Dance a jig before you talk to me. Equip a brown apron, adamantite medium helmet and snakeskin chaps.", "Yanille", OUTSIDE_YANILLE_BANK, new WorldPoint(2610, 3092, 0), JUMP_FOR_JOY, JIG, item(BROWN_APRON), item(ADAMANT_MED_HELM), item(SNAKESKIN_CHAPS)),
|
||||
new EmoteClue("Jump for joy in the TzHaar sword shop. Shrug before you talk to me. Equip a Steel longsword, Blue D'hide body and blue mystic gloves.", "Tzhaar weapon store", TZHAAR_WEAPONS_STORE, new WorldPoint(2477, 5146, 0), JUMP_FOR_JOY, SHRUG, item(STEEL_LONGSWORD), item(BLUE_DHIDE_BODY), item(MYSTIC_GLOVES)),
|
||||
new EmoteClue("Jump for joy in the Ancient Cavern. Equip a granite shield, splitbark body and any rune heraldic helm.", "Ancient cavern", ENTRANCE_OF_THE_CAVERN_UNDER_THE_WHIRLPOOL, new WorldPoint(1768, 5366, 1), JUMP_FOR_JOY, item(GRANITE_SHIELD), item(SPLITBARK_BODY), range("Any rune heraldic helm", RUNE_HELM_H1, RUNE_HELM_H5)),
|
||||
new EmoteClue("Jump for joy at the Neitiznot rune rock. Equip Rune boots, a proselyte hauberk and a dragonstone ring.", "Fremennik Isles", NEAR_A_RUNITE_ROCK_IN_THE_FREMENNIK_ISLES, new WorldPoint(2375, 3850, 0), JUMP_FOR_JOY, item(RUNE_BOOTS), item(PROSELYTE_HAUBERK), item(DRAGONSTONE_RING)),
|
||||
new EmoteClue("Jump for joy in the centre of Zul-Andra. Beware of double agents! Equip a dragon 2h sword, bandos boots and an obsidian cape.", "Zul-Andra", NEAR_THE_PIER_IN_ZULANDRA, new WorldPoint(2199, 3056, 0), DOUBLE_AGENT_141, JUMP_FOR_JOY, item(DRAGON_2H_SWORD), item(BANDOS_BOOTS), item(OBSIDIAN_CAPE)),
|
||||
new EmoteClue("Laugh by the fountain of heroes. Equip splitbark legs, dragon boots and a Rune longsword.", "Fountain of heroes", FOUNTAIN_OF_HEROES, new WorldPoint(2920, 9893, 0), LAUGH, item(SPLITBARK_LEGS), any("Dragon boots", item(DRAGON_BOOTS), item(DRAGON_BOOTS_G)), item(RUNE_LONGSWORD)),
|
||||
new EmoteClue("Laugh in Jokul's tent in the Mountain Camp. Beware of double agents! Equip a rune full helmet, blue dragonhide chaps and a fire battlestaff.", "Mountain Camp", MOUNTAIN_CAMP_GOAT_ENCLOSURE, new WorldPoint(2812, 3681, 0), DOUBLE_AGENT_108, LAUGH, item(RUNE_FULL_HELM), item(BLUE_DHIDE_CHAPS), item(FIRE_BATTLESTAFF)),
|
||||
new EmoteClue("Laugh at the crossroads south of the Sinclair Mansion. Equip a cowl, a blue wizard robe top and an iron scimitar.", "Sinclair Mansion", ROAD_JUNCTION_SOUTH_OF_SINCLAIR_MANSION, new WorldPoint(2741, 3536, 0), LAUGH, item(LEATHER_COWL), item(BLUE_WIZARD_ROBE), item(IRON_SCIMITAR)),
|
||||
new EmoteClue("Laugh in front of the gem store in Ardougne market. Equip a Castlewars bracelet, a dragonstone amulet and a ring of forging.", "Ardougne", NEAR_THE_GEM_STALL_IN_ARDOUGNE_MARKET, new WorldPoint(2666, 3304, 0), LAUGH, any("Castle wars bracelet", range(CASTLE_WARS_BRACELET3, CASTLE_WARS_BRACELET1)), item(DRAGONSTONE_AMULET), item(RING_OF_FORGING)),
|
||||
new EmoteClue("Panic in the Limestone Mine. Equip bronze platelegs, a steel pickaxe and a steel medium helmet.", "Limestone Mine", LIMESTONE_MINE, new WorldPoint(3372, 3498, 0), PANIC, item(BRONZE_PLATELEGS), item(STEEL_PICKAXE), item(STEEL_MED_HELM)),
|
||||
new EmoteClue("Panic by the mausoleum in Morytania. Wave before you speak to me. Equip a mithril plate skirt, a maple longbow and no boots.", "Morytania mausoleum, access via the experiments cave", MAUSOLEUM_OFF_THE_MORYTANIA_COAST, new WorldPoint(3504, 3576, 0), PANIC, WAVE, item(MITHRIL_PLATESKIRT), item(MAPLE_LONGBOW), emptySlot("No boots", BOOTS)),
|
||||
new EmoteClue("Panic on the Wilderness volcano bridge. Beware of double agents! Equip any headband and crozier.", "Wilderness volcano", VOLCANO_IN_THE_NORTHEASTERN_WILDERNESS, new WorldPoint(3368, 3935, 0), DOUBLE_AGENT_65, PANIC, any("Any headband", range(RED_HEADBAND, BROWN_HEADBAND), range(WHITE_HEADBAND, GREEN_HEADBAND)), any("Any crozier", item(ANCIENT_CROZIER), item(ARMADYL_CROZIER), item(BANDOS_CROZIER), range(SARADOMIN_CROZIER, ZAMORAK_CROZIER))),
|
||||
new EmoteClue("Panic by the pilot on White Wolf Mountain. Beware of double agents! Equip mithril platelegs, a ring of life and a rune axe.", "White Wolf Mountain", GNOME_GLIDER_ON_WHITE_WOLF_MOUNTAIN, new WorldPoint(2847, 3499, 0), DOUBLE_AGENT_108, PANIC, item(MITHRIL_PLATELEGS), item(RING_OF_LIFE), item(RUNE_AXE)),
|
||||
new EmoteClue("Panic by the big egg where no one dare goes and the ground is burnt. Beware of double agents! Equip a dragon med helm, a TokTz-Ket-Xil, a brine sabre, rune platebody and an uncharged amulet of glory.", "Lava dragon isle", SOUTHEAST_CORNER_OF_LAVA_DRAGON_ISLE, new WorldPoint(3227, 3831, 0), DOUBLE_AGENT_141, PANIC, item(DRAGON_MED_HELM), item(TOKTZKETXIL), item(BRINE_SABRE), item(RUNE_PLATEBODY), any("Uncharged Amulet of glory", item(AMULET_OF_GLORY))),
|
||||
new EmoteClue("Panic at the area flowers meet snow. Equip Blue D'hide vambraces, a dragon spear and a rune plateskirt.", "Trollweiss mountain", HALFWAY_DOWN_TROLLWEISS_MOUNTAIN, new WorldPoint(2776, 3781, 0), PANIC, item(BLUE_DHIDE_VAMBRACES), item(DRAGON_SPEAR), item(RUNE_PLATESKIRT), item(SLED_4084)),
|
||||
new EmoteClue("Do a push up at the bank of the Warrior's guild. Beware of double agents! Equip a dragon battleaxe, a dragon defender and a slayer helm of any kind.", "Warriors' guild", WARRIORS_GUILD_BANK_29047, new WorldPoint(2843, 3543, 0), DOUBLE_AGENT_141, PUSH_UP, item(DRAGON_BATTLEAXE), any("Dragon defender", item(DRAGON_DEFENDER), item(DRAGON_DEFENDER_T), item(DRAGON_DEFENDER_L)), any("Any slayer helmet", item(SLAYER_HELMET), item(BLACK_SLAYER_HELMET), item(GREEN_SLAYER_HELMET), item(PURPLE_SLAYER_HELMET), item(RED_SLAYER_HELMET), item(TURQUOISE_SLAYER_HELMET), item(SLAYER_HELMET_I), item(BLACK_SLAYER_HELMET_I), item(GREEN_SLAYER_HELMET_I), item(PURPLE_SLAYER_HELMET_I), item(RED_SLAYER_HELMET_I), item(TURQUOISE_SLAYER_HELMET_I), item(HYDRA_SLAYER_HELMET), item(HYDRA_SLAYER_HELMET_I), item(TWISTED_SLAYER_HELMET), item(TWISTED_SLAYER_HELMET_I))),
|
||||
new EmoteClue("Blow a raspberry in the bank of the Warriors' Guild. Beware of double agents! Equip a dragon battleaxe, a slayer helm of any kind and a dragon defender or avernic defender.", "Warriors' guild", WARRIORS_GUILD_BANK_29047, new WorldPoint(2843, 3543, 0), DOUBLE_AGENT_141, RASPBERRY, item(DRAGON_BATTLEAXE), any("Dragon defender or Avernic defender", item(DRAGON_DEFENDER), item(DRAGON_DEFENDER_T), item(DRAGON_DEFENDER_L), item(AVERNIC_DEFENDER), item(AVERNIC_DEFENDER_L)), any("Any slayer helmet", item(SLAYER_HELMET), item(BLACK_SLAYER_HELMET), item(GREEN_SLAYER_HELMET), item(PURPLE_SLAYER_HELMET), item(RED_SLAYER_HELMET), item(TURQUOISE_SLAYER_HELMET), item(SLAYER_HELMET_I), item(BLACK_SLAYER_HELMET_I), item(GREEN_SLAYER_HELMET_I), item(PURPLE_SLAYER_HELMET_I), item(RED_SLAYER_HELMET_I), item(TURQUOISE_SLAYER_HELMET_I), item(HYDRA_SLAYER_HELMET), item(HYDRA_SLAYER_HELMET_I), item(TWISTED_SLAYER_HELMET), item(TWISTED_SLAYER_HELMET_I))),
|
||||
new EmoteClue("Blow a raspberry at the monkey cage in Ardougne Zoo. Equip a studded leather body, bronze platelegs and a normal staff with no orb.", "Ardougne Zoo", NEAR_THE_PARROTS_IN_ARDOUGNE_ZOO, new WorldPoint(2607, 3282, 0), RASPBERRY, item(STUDDED_BODY), item(BRONZE_PLATELEGS), item(STAFF)),
|
||||
new EmoteClue("Blow raspberries outside the entrance to Keep Le Faye. Equip a coif, an iron platebody and leather gloves.", "Keep Le Faye", OUTSIDE_KEEP_LE_FAYE, new WorldPoint(2757, 3401, 0), RASPBERRY, item(COIF), item(IRON_PLATEBODY), item(LEATHER_GLOVES)),
|
||||
new EmoteClue("Blow a raspberry in the Fishing Guild bank. Beware of double agents! Equip an elemental shield, blue dragonhide chaps and a rune warhammer.", "Fishing Guild", FISHING_GUILD_BANK, new WorldPoint(2588, 3419, 0), DOUBLE_AGENT_108, RASPBERRY, item(ELEMENTAL_SHIELD), item(BLUE_DHIDE_CHAPS), item(RUNE_WARHAMMER)),
|
||||
new EmoteClue("Salute in the banana plantation. Beware of double agents! Equip a diamond ring, amulet of power, and nothing on your chest and legs.", "Karamja", WEST_SIDE_OF_THE_KARAMJA_BANANA_PLANTATION, new WorldPoint(2914, 3168, 0), DOUBLE_AGENT_108, SALUTE, item(DIAMOND_RING), item(AMULET_OF_POWER), emptySlot("Nothing on chest & legs", BODY, LEGS)),
|
||||
new EmoteClue("Salute in the Warriors' guild bank. Equip only a black salamander.", "Warriors' guild", WARRIORS_GUILD_BANK, new WorldPoint(2844, 3542, 0), SALUTE, item(BLACK_SALAMANDER), emptySlot("Nothing else", HEAD, CAPE, AMULET, BODY, SHIELD, LEGS, GLOVES, BOOTS, RING, AMMO)),
|
||||
new EmoteClue("Salute in the centre of the mess hall. Beware of double agents! Equip a rune halberd rune platebody, and an amulet of strength.", "Hosidius mess hall", HOSIDIUS_MESS, new WorldPoint(1646, 3631, 0), DOUBLE_AGENT_108, SALUTE, item(RUNE_HALBERD), item(RUNE_PLATEBODY), item(AMULET_OF_STRENGTH)),
|
||||
new EmoteClue("Shrug in the mine near Rimmington. Equip a gold necklace, a gold ring and a bronze spear.", "Rimmington mine", RIMMINGTON_MINE, new WorldPoint(2976, 3238, 0), SHRUG, item(GOLD_NECKLACE), item(GOLD_RING), item(BRONZE_SPEAR)),
|
||||
new EmoteClue("Shrug in Catherby bank. Yawn before you talk to me. Equip a maple longbow, green d'hide chaps and an iron med helm.", "Catherby", OUTSIDE_CATHERBY_BANK, new WorldPoint(2808, 3440, 0), SHRUG, YAWN, item(MAPLE_LONGBOW), item(GREEN_DHIDE_CHAPS), item(IRON_MED_HELM)),
|
||||
new EmoteClue("Shrug in the Zamorak temple found in the Eastern Wilderness. Beware of double agents! Equip rune platelegs, an iron platebody and blue dragonhide vambraces.", "Chaos temple", CHAOS_TEMPLE_IN_THE_SOUTHEASTERN_WILDERNESS, new WorldPoint(3239, 3611, 0), DOUBLE_AGENT_65, SHRUG, item(RUNE_PLATELEGS), item(IRON_PLATEBODY), item(BLUE_DHIDE_VAMBRACES)),
|
||||
new EmoteClue("Shrug in the Shayzien command tent. Equip a blue mystic robe bottom, a rune kiteshield and any bob shirt.", "Shayzien command tent", SHAYZIEN_WAR_TENT, new WorldPoint(1555, 3537, 0), SHRUG, item(MYSTIC_ROBE_BOTTOM), item(RUNE_KITESHIELD), range("Any bob shirt", BOBS_RED_SHIRT, BOBS_PURPLE_SHIRT)),
|
||||
new EmoteClue("Slap your head in the centre of the Kourend catacombs. Beware of double agents! Equip the arclight and the amulet of the damned.", "Kourend catacombs", CENTRE_OF_THE_CATACOMBS_OF_KOUREND, new WorldPoint(1663, 10045, 0), DOUBLE_AGENT_141, SLAP_HEAD, item(ARCLIGHT), any("Amulet of the damned", item(AMULET_OF_THE_DAMNED), item(AMULET_OF_THE_DAMNED_FULL))),
|
||||
new EmoteClue("Spin at the crossroads north of Rimmington. Equip a green gnome hat, cream gnome top and leather chaps.", "Rimmington", ROAD_JUNCTION_NORTH_OF_RIMMINGTON, new WorldPoint(2981, 3276, 0), SPIN, item(GREEN_HAT), item(CREAM_ROBE_TOP), item(LEATHER_CHAPS)),
|
||||
new EmoteClue("Spin in Draynor Manor by the fountain. Equip an iron platebody, studded leather chaps and a bronze full helmet.", "Draynor Manor", DRAYNOR_MANOR_BY_THE_FOUNTAIN, new WorldPoint(3088, 3336, 0), SPIN, item(IRON_PLATEBODY), item(STUDDED_CHAPS), item(BRONZE_FULL_HELM)),
|
||||
new EmoteClue("Spin in front of the Soul altar. Beware of double agents! Equip a dragon pickaxe, helm of neitiznot and a pair of rune boots.", "Soul altar", SOUL_ALTAR, new WorldPoint(1815, 3856, 0), DOUBLE_AGENT_141, SPIN, any("Dragon or Crystal pickaxe", item(DRAGON_PICKAXE), item(DRAGON_PICKAXE_12797), item(INFERNAL_PICKAXE), item(INFERNAL_PICKAXE_UNCHARGED), item(DRAGON_PICKAXE_OR), item(CRYSTAL_PICKAXE), item(CRYSTAL_PICKAXE_INACTIVE)), item(HELM_OF_NEITIZNOT), item(RUNE_BOOTS)),
|
||||
new EmoteClue("Spin in the Varrock Castle courtyard. Equip a black axe, a coif and a ruby ring.", "Varrock Castle", OUTSIDE_VARROCK_PALACE_COURTYARD, new WorldPoint(3213, 3463, 0), SPIN, item(BLACK_AXE), item(COIF), item(RUBY_RING)),
|
||||
new EmoteClue("Spin in West Ardougne Church. Equip a dragon spear and red dragonhide chaps.", "West Ardougne Church", CHAPEL_IN_WEST_ARDOUGNE, new WorldPoint(2530, 3290, 0), SPIN, item(DRAGON_SPEAR), item(RED_DHIDE_CHAPS)),
|
||||
new EmoteClue("Spin on the bridge by the Barbarian Village. Salute before you talk to me. Equip purple gloves, a steel kiteshield and a mithril full helmet.", "Barbarian Village", EAST_OF_THE_BARBARIAN_VILLAGE_BRIDGE, new WorldPoint(3105, 3420, 0), SPIN, SALUTE, item(PURPLE_GLOVES), item(STEEL_KITESHIELD), item(MITHRIL_FULL_HELM)),
|
||||
new EmoteClue("Stamp in the Enchanted valley west of the waterfall. Beware of double agents! Equip a dragon axe.", "Enchanted Valley (BKQ)", NORTHWESTERN_CORNER_OF_THE_ENCHANTED_VALLEY, new WorldPoint(3030, 4522, 0), DOUBLE_AGENT_141, STAMP, any("Dragon or Crystal axe", item(DRAGON_AXE), item(CRYSTAL_AXE), item(CRYSTAL_AXE_INACTIVE), item(INFERNAL_AXE), item(INFERNAL_AXE_UNCHARGED))),
|
||||
new EmoteClue("Think in middle of the wheat field by the Lumbridge mill. Equip a blue gnome robetop, a turquoise gnome robe bottom and an oak shortbow.", "Lumbridge mill", WHEAT_FIELD_NEAR_THE_LUMBRIDGE_WINDMILL, new WorldPoint(3159, 3298, 0), THINK, item(BLUE_ROBE_TOP), item(TURQUOISE_ROBE_BOTTOMS), item(OAK_SHORTBOW)),
|
||||
new EmoteClue("Think in the centre of the Observatory. Spin before you talk to me. Equip a mithril chain body, green dragonhide chaps and a ruby amulet.", "Observatory", OBSERVATORY, new WorldPoint(2439, 3161, 0), THINK, SPIN, item(MITHRIL_CHAINBODY), item(GREEN_DHIDE_CHAPS), item(RUBY_AMULET)),
|
||||
new EmoteClue("Wave along the south fence of the Lumber Yard. Equip a hard leather body, leather chaps and a bronze axe.", "Lumber Yard", NEAR_THE_SAWMILL_OPERATORS_BOOTH, new WorldPoint(3307, 3491, 0), WAVE, item(HARDLEATHER_BODY), item(LEATHER_CHAPS), item(BRONZE_AXE)),
|
||||
new EmoteClue("Wave in the Falador gem store. Equip a Mithril pickaxe, Black platebody and an Iron Kiteshield.", "Falador", NEAR_HERQUINS_SHOP_IN_FALADOR, new WorldPoint(2945, 3335, 0), WAVE, item(MITHRIL_PICKAXE), item(BLACK_PLATEBODY), item(IRON_KITESHIELD)),
|
||||
new EmoteClue("Wave on Mudskipper Point. Equip a black cape, leather chaps and a steel mace.", "Mudskipper Point", MUDSKIPPER_POINT, new WorldPoint(2989, 3110, 0), WAVE, item(BLACK_CAPE), item(LEATHER_CHAPS), item(STEEL_MACE)),
|
||||
new EmoteClue("Wave on the northern wall of Castle Drakan. Beware of double agents! Wear a dragon sq shield, splitbark body and any boater.", "Castle Drakan", NORTHERN_WALL_OF_CASTLE_DRAKAN, new WorldPoint(3562, 3379, 0), DOUBLE_AGENT_141, WAVE, any("Dragon sq shield", item(DRAGON_SQ_SHIELD), item(DRAGON_SQ_SHIELD_G)), item(SPLITBARK_BODY), any("Any boater", item(RED_BOATER), item(ORANGE_BOATER), item(GREEN_BOATER), item(BLUE_BOATER), item(BLACK_BOATER), item(PINK_BOATER), item(PURPLE_BOATER), item(WHITE_BOATER))),
|
||||
new EmoteClue("Yawn in the 7th room of Pyramid Plunder. Beware of double agents! Equip a pharaoh sceptre and a full set of menaphite robes.", "Pyramid Plunder", _7TH_CHAMBER_OF_JALSAVRAH, new WorldPoint(1944, 4427, 0), DOUBLE_AGENT_141, YAWN, any("Pharaoh's sceptre", item(PHARAOHS_SCEPTRE), item(PHARAOHS_SCEPTRE_1), item(PHARAOHS_SCEPTRE_2), item(PHARAOHS_SCEPTRE_3), item(PHARAOHS_SCEPTRE_4), item(PHARAOHS_SCEPTRE_5), item(PHARAOHS_SCEPTRE_6), item(PHARAOHS_SCEPTRE_7), item(PHARAOHS_SCEPTRE_8)), any("Full set of menaphite robes", all(item(MENAPHITE_PURPLE_HAT), item(MENAPHITE_PURPLE_TOP), range(MENAPHITE_PURPLE_ROBE, MENAPHITE_PURPLE_KILT)), all(item(MENAPHITE_RED_HAT), item(MENAPHITE_RED_TOP), range(MENAPHITE_RED_ROBE, MENAPHITE_RED_KILT)))),
|
||||
new EmoteClue("Yawn in the Varrock library. Equip a green gnome robe top, HAM robe bottom and an iron warhammer.", "Varrock Castle", VARROCK_PALACE_LIBRARY, new WorldPoint(3209, 3492, 0), YAWN, item(GREEN_ROBE_TOP), item(HAM_ROBE), item(IRON_WARHAMMER)),
|
||||
new EmoteClue("Yawn in Draynor Marketplace. Equip studded leather chaps, an iron kiteshield and a steel longsword.", "Draynor", DRAYNOR_VILLAGE_MARKET, new WorldPoint(3083, 3253, 0), YAWN, item(STUDDED_CHAPS), item(IRON_KITESHIELD), item(STEEL_LONGSWORD)),
|
||||
new EmoteClue("Yawn in the Castle Wars lobby. Shrug before you talk to me. Equip a ruby amulet, a mithril scimitar and a Wilderness cape.", "Castle Wars", CASTLE_WARS_BANK, new WorldPoint(2440, 3092, 0), YAWN, SHRUG, item(RUBY_AMULET), item(MITHRIL_SCIMITAR), range("Any team cape", TEAM1_CAPE, TEAM50_CAPE)),
|
||||
new EmoteClue("Yawn in the rogues' general store. Beware of double agents! Equip an adamant square shield, blue dragon vambraces and a rune pickaxe.", "Rogues general store", NOTERAZZOS_SHOP_IN_THE_WILDERNESS, new WorldPoint(3026, 3701, 0), DOUBLE_AGENT_65, YAWN, item(ADAMANT_SQ_SHIELD), item(BLUE_DHIDE_VAMBRACES), item(RUNE_PICKAXE)),
|
||||
new EmoteClue("Yawn at the top of Trollheim. Equip a lava battlestaff, black dragonhide vambraces and a mind shield.", "Trollheim Mountain", ON_TOP_OF_TROLLHEIM_MOUNTAIN, new WorldPoint(2887, 3676, 0), YAWN, any("Lava battlestaff", item(LAVA_BATTLESTAFF), item(LAVA_BATTLESTAFF_21198)), item(BLACK_DHIDE_VAMBRACES), item(MIND_SHIELD)),
|
||||
new EmoteClue("Yawn in the centre of Arceuus library. Nod your head before you talk to me. Equip blue dragonhide vambraces, adamant boots and an adamant dagger.", "Arceuus library", ENTRANCE_OF_THE_ARCEUUS_LIBRARY, new WorldPoint(1632, 3807, 0), YAWN, YES, item(BLUE_DHIDE_VAMBRACES), item(ADAMANT_BOOTS), item(ADAMANT_DAGGER)),
|
||||
new EmoteClue("Swing a bullroarer at the top of the Watchtower. Beware of double agents! Equip a dragon plateskirt, climbing boots and a dragon chainbody.", "Yanille Watchtower", TOP_FLOOR_OF_THE_YANILLE_WATCHTOWER, new WorldPoint(2930, 4717, 2), DOUBLE_AGENT_141, BULL_ROARER, any("Dragon plateskirt", item(DRAGON_PLATESKIRT), item(DRAGON_PLATESKIRT_G)), item(CLIMBING_BOOTS), any("Dragon chainbody", item(DRAGON_CHAINBODY_3140), item(DRAGON_CHAINBODY_G)), item(ItemID.BULL_ROARER)),
|
||||
new EmoteClue("Blow a raspberry at Gypsy Aris in her tent. Equip a gold ring and a gold necklace.", "Varrock", GYPSY_TENT_ENTRANCE, new WorldPoint(3203, 3424, 0), RASPBERRY, item(GOLD_RING), item(GOLD_NECKLACE)),
|
||||
new EmoteClue("Bow to Brugsen Bursen at the Grand Exchange.", "Grand Exchange", null, new WorldPoint(3164, 3477, 0), BOW),
|
||||
new EmoteClue("Cheer at Iffie Nitter. Equip a chef hat and a red cape.", "Varrock", FINE_CLOTHES_ENTRANCE, new WorldPoint(3205, 3416, 0), CHEER, item(CHEFS_HAT), item(RED_CAPE)),
|
||||
new EmoteClue("Clap at Bob's Brilliant Axes. Equip a bronze axe and leather boots.", "Lumbridge", BOB_AXES_ENTRANCE, new WorldPoint(3231, 3203, 0), CLAP, item(BRONZE_AXE), item(LEATHER_BOOTS)),
|
||||
new EmoteClue("Panic at Al Kharid mine.", "Al Kharid mine", null, new WorldPoint(3300, 3314, 0), PANIC),
|
||||
new EmoteClue("Spin at Flynn's Mace Shop.", "Falador", null, new WorldPoint(2950, 3387, 0), SPIN));
|
||||
|
||||
private static final String UNICODE_CHECK_MARK = "\u2713";
|
||||
private static final String UNICODE_BALLOT_X = "\u2717";
|
||||
|
||||
private final String text;
|
||||
private final String locationName;
|
||||
@Nullable
|
||||
private final STASHUnit stashUnit;
|
||||
private final WorldPoint location;
|
||||
private final Emote firstEmote;
|
||||
private final Emote secondEmote;
|
||||
private final ItemRequirement[] itemRequirements;
|
||||
|
||||
private EmoteClue(String text, String locationName, STASHUnit stashUnit, WorldPoint location, Emote firstEmote, @Nonnull ItemRequirement... itemRequirements)
|
||||
{
|
||||
this(text, locationName, stashUnit, location, firstEmote, null, itemRequirements);
|
||||
}
|
||||
|
||||
private EmoteClue(String text, String locationName, STASHUnit stashUnit, WorldPoint location, Enemy enemy, Emote firstEmote, @Nonnull ItemRequirement... itemRequirements)
|
||||
{
|
||||
this(text, locationName, stashUnit, location, firstEmote, null, itemRequirements);
|
||||
setEnemy(enemy);
|
||||
}
|
||||
|
||||
private EmoteClue(String text, String locationName, STASHUnit stashUnit, WorldPoint location, Emote firstEmote, Emote secondEmote, @Nonnull ItemRequirement... itemRequirements)
|
||||
{
|
||||
this.text = text;
|
||||
this.locationName = locationName;
|
||||
this.stashUnit = stashUnit;
|
||||
this.location = location;
|
||||
this.firstEmote = firstEmote;
|
||||
this.secondEmote = secondEmote;
|
||||
this.itemRequirements = itemRequirements;
|
||||
}
|
||||
|
||||
private EmoteClue(String text, String locationName, @Nullable STASHUnit stashUnit, WorldPoint location, Emote firstEmote, Emote secondEmote, @Nonnull Varbits firePit, @Nonnull ItemRequirement... itemRequirements)
|
||||
{
|
||||
this(text, locationName, stashUnit, location, firstEmote, secondEmote, itemRequirements);
|
||||
setRequiresLight(true);
|
||||
setHasFirePit(firePit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Emote Clue").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Emotes:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getFirstEmote().getName())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
if (getSecondEmote() != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getSecondEmote().getName())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
}
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Location:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getLocationName())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
if (itemRequirements.length > 0)
|
||||
{
|
||||
Client client = plugin.getClient();
|
||||
|
||||
if (stashUnit != null)
|
||||
{
|
||||
client.runScript(ScriptID.WATSON_STASH_UNIT_CHECK, stashUnit.getObjectId(), 0, 0, 0);
|
||||
int[] intStack = client.getIntStack();
|
||||
boolean stashUnitBuilt = intStack[0] == 1;
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("STASH Unit:")
|
||||
.right(stashUnitBuilt ? UNICODE_CHECK_MARK : UNICODE_BALLOT_X)
|
||||
.rightColor(stashUnitBuilt ? Color.GREEN : Color.RED)
|
||||
.build());
|
||||
}
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Equip:").build());
|
||||
|
||||
Item[] equipment = plugin.getEquippedItems();
|
||||
Item[] inventory = plugin.getInventoryItems();
|
||||
|
||||
// If equipment is null, the player is wearing nothing
|
||||
if (equipment == null)
|
||||
{
|
||||
equipment = new Item[0];
|
||||
}
|
||||
|
||||
// If inventory is null, the player has nothing in their inventory
|
||||
if (inventory == null)
|
||||
{
|
||||
inventory = new Item[0];
|
||||
}
|
||||
|
||||
Item[] combined = new Item[equipment.length + inventory.length];
|
||||
System.arraycopy(equipment, 0, combined, 0, equipment.length);
|
||||
System.arraycopy(inventory, 0, combined, equipment.length, inventory.length);
|
||||
|
||||
for (ItemRequirement requirement : itemRequirements)
|
||||
{
|
||||
boolean equipmentFulfilled = requirement.fulfilledBy(equipment);
|
||||
boolean combinedFulfilled = requirement.fulfilledBy(combined);
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(requirement.getCollectiveName(client))
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.right(combinedFulfilled ? UNICODE_CHECK_MARK : UNICODE_BALLOT_X)
|
||||
.rightColor(equipmentFulfilled ? Color.GREEN : (combinedFulfilled ? Color.ORANGE : Color.RED))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
LocalPoint localPoint = LocalPoint.fromWorld(plugin.getClient(), getLocation());
|
||||
|
||||
if (localPoint != null)
|
||||
{
|
||||
OverlayUtil.renderTileOverlay(plugin.getClient(), graphics, localPoint, plugin.getEmoteImage(), Color.ORANGE);
|
||||
}
|
||||
|
||||
if (stashUnit != null)
|
||||
{
|
||||
final WorldPoint[] worldPoints = stashUnit.getWorldPoints();
|
||||
|
||||
for (final WorldPoint worldPoint : worldPoints)
|
||||
{
|
||||
final LocalPoint stashUnitLocalPoint = LocalPoint.fromWorld(plugin.getClient(), worldPoint);
|
||||
|
||||
if (stashUnitLocalPoint != null)
|
||||
{
|
||||
final Polygon poly = Perspective.getCanvasTilePoly(plugin.getClient(), stashUnitLocalPoint);
|
||||
if (poly != null)
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, poly, Color.RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static EmoteClue forText(String text)
|
||||
{
|
||||
for (EmoteClue clue : CLUES)
|
||||
{
|
||||
if (clue.getText().equalsIgnoreCase(text))
|
||||
{
|
||||
return clue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Trevor <https://github.com/TrevorMartz>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum Enemy
|
||||
{
|
||||
//appears in hard clue emote steps in the wilderness
|
||||
DOUBLE_AGENT_65("Double Agent level 65"),
|
||||
//appears in hard clue emote steps not in the wilderness
|
||||
DOUBLE_AGENT_108("Double Agent level 108"),
|
||||
//appears for master clue emote steps all areas
|
||||
DOUBLE_AGENT_141("Double Agent level 141"),
|
||||
//appears for hard clue coordinate steps in the wilderness
|
||||
ZAMORAK_WIZARD("Zamorak Wizard"),
|
||||
//appears for hard clue coordinate steps not in the wilderness
|
||||
SARADOMIN_WIZARD("Saradomin Wizard"),
|
||||
//appears for elite clue coordinate steps all areas
|
||||
ARMADYLIAN_OR_BANDOSIAN_GUARD("Armadylian OR Bandosian Guard"),
|
||||
//appears for master clue coordinate and hot cold clues when single-way combat
|
||||
BRASSICAN_MAGE("Brassican Mage"),
|
||||
//appears for master clue coordinate and hot cold clues when multi-way combat
|
||||
ANCIENT_WIZARDS("Ancient Wizard Trio"),
|
||||
//There is a master hot cold step that overlaps the border of multi and single according to the wiki.
|
||||
BRASSICAN_OR_WIZARDS("Brassican Mage OR Ancient Wizards");
|
||||
|
||||
private final String text;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
@Getter
|
||||
public class FairyRingClue extends ClueScroll implements TextClueScroll, LocationClueScroll
|
||||
{
|
||||
private static final Set<FairyRingClue> CLUES = ImmutableSet.of(
|
||||
new FairyRingClue("A I R 2 3 3 1", new WorldPoint(2702, 3246, 0)),
|
||||
new FairyRingClue("A I Q 0 4 4 0", new WorldPoint(3000, 3110, 0)),
|
||||
new FairyRingClue("A L P 1 1 4 0", new WorldPoint(2504, 3633, 0)),
|
||||
new FairyRingClue("B L P 6 2 0 0", new WorldPoint(2439, 5132, 0)),
|
||||
new FairyRingClue("B J R 1 1 2 3", new WorldPoint(2648, 4729, 0)),
|
||||
new FairyRingClue("B I P 7 0 1 3", new WorldPoint(3407, 3330, 0)),
|
||||
new FairyRingClue("C I S 0 0 0 9", new WorldPoint(1630, 3868, 0)),
|
||||
new FairyRingClue("C K P 0 2 2 4", new WorldPoint(2073, 4846, 0)),
|
||||
new FairyRingClue("D I P 8 5 1 1", new WorldPoint(3041, 4770, 0)),
|
||||
new FairyRingClue("D K S 2 3 1 0", new WorldPoint(2747, 3720, 0))
|
||||
);
|
||||
|
||||
private String text;
|
||||
private WorldPoint location;
|
||||
|
||||
private FairyRingClue(String text, WorldPoint location)
|
||||
{
|
||||
this.text = text;
|
||||
this.location = location;
|
||||
setRequiresSpade(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Fairy Ring Clue").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Code:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getText().substring(0, 5))
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Travel to the fairy ring to see where to dig.")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
LocalPoint localLocation = LocalPoint.fromWorld(plugin.getClient(), getLocation());
|
||||
|
||||
if (localLocation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OverlayUtil.renderTileOverlay(plugin.getClient(), graphics, localLocation, plugin.getSpadeImage(), Color.ORANGE);
|
||||
}
|
||||
|
||||
public static FairyRingClue forText(String text)
|
||||
{
|
||||
for (FairyRingClue clue : CLUES)
|
||||
{
|
||||
if (clue.text.equalsIgnoreCase(text))
|
||||
{
|
||||
return clue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Twiglet1022 <https://github.com/Twiglet1022>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nonnull;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import static net.runelite.api.ItemID.*;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.AnyRequirementCollection;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.RangeItemRequirement;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.SingleItemRequirement;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
@Getter
|
||||
public class FaloTheBardClue extends ClueScroll implements TextClueScroll, NpcClueScroll
|
||||
{
|
||||
private static final List<FaloTheBardClue> CLUES = ImmutableList.of(
|
||||
new FaloTheBardClue("A blood red weapon, a strong curved sword, found on the island of primate lords.", item(DRAGON_SCIMITAR)),
|
||||
new FaloTheBardClue("A book that preaches of some great figure, lending strength, might and vigour.", any("Any god book (must be complete)", item(HOLY_BOOK), item(BOOK_OF_BALANCE), item(UNHOLY_BOOK), item(BOOK_OF_LAW), item(BOOK_OF_WAR), item(BOOK_OF_DARKNESS))),
|
||||
new FaloTheBardClue("A bow of elven craft was made, it shimmers bright, but will soon fade.", any("Crystal Bow", item(CRYSTAL_BOW), item(CRYSTAL_BOW_24123))),
|
||||
new FaloTheBardClue("A fiery axe of great inferno, when you use it, you'll wonder where the logs go.", item(INFERNAL_AXE)),
|
||||
new FaloTheBardClue("A mark used to increase one's grace, found atop a seer's place.", item(MARK_OF_GRACE)),
|
||||
new FaloTheBardClue("A molten beast with fiery breath, you acquire these with its death.", item(LAVA_DRAGON_BONES)),
|
||||
new FaloTheBardClue("A shiny helmet of flight, to obtain this with melee, struggle you might.", item(ARMADYL_HELMET)),
|
||||
// The wiki doesn't specify whether the trimmed dragon defender will work so I've assumed that it doesn't
|
||||
new FaloTheBardClue("A sword held in the other hand, red its colour, Cyclops strength you must withstand.", any("Dragon or Avernic Defender", item(DRAGON_DEFENDER), item(DRAGON_DEFENDER_L), item(AVERNIC_DEFENDER), item(AVERNIC_DEFENDER_L))),
|
||||
new FaloTheBardClue("A token used to kill mythical beasts, in hopes of a blade or just for an xp feast.", item(WARRIOR_GUILD_TOKEN)),
|
||||
new FaloTheBardClue("Green is my favourite, mature ale I do love, this takes your herblore above.", item(GREENMANS_ALEM)),
|
||||
new FaloTheBardClue("It can hold down a boat or crush a goat, this object, you see, is quite heavy.", item(BARRELCHEST_ANCHOR)),
|
||||
new FaloTheBardClue("It comes from the ground, underneath the snowy plain. Trolls aplenty, with what looks like a mane.", item(BASALT)),
|
||||
new FaloTheBardClue("No attack to wield, only strength is required, made of obsidian, but with no room for a shield.", item(TZHAARKETOM)),
|
||||
new FaloTheBardClue("Penance healers runners and more, obtaining this body often gives much deplore.", any("Fighter Torso", item(FIGHTER_TORSO), item(FIGHTER_TORSO_L))),
|
||||
new FaloTheBardClue("Strangely found in a chest, many believe these gloves are the best.", item(BARROWS_GLOVES)),
|
||||
new FaloTheBardClue("These gloves of white won't help you fight, but aid in cooking, they just might.", item(COOKING_GAUNTLETS)),
|
||||
new FaloTheBardClue("They come from some time ago, from a land unto the east. Fossilised they have become, this small and gentle beast.", item(NUMULITE)),
|
||||
new FaloTheBardClue("To slay a dragon you must first do, before this chest piece can be put on you.", item(RUNE_PLATEBODY)),
|
||||
new FaloTheBardClue("Vampyres are agile opponents, damaged best with a weapon of many components.", any("Rod of Ivandis or Ivandis/Blisterwood flail", range(ROD_OF_IVANDIS_10, ROD_OF_IVANDIS_1), item(IVANDIS_FLAIL), item(BLISTERWOOD_FLAIL)))
|
||||
);
|
||||
|
||||
private static final WorldPoint LOCATION = new WorldPoint(2689, 3550, 0);
|
||||
private static final String FALO_THE_BARD = "Falo the Bard";
|
||||
|
||||
private static SingleItemRequirement item(int itemId)
|
||||
{
|
||||
return new SingleItemRequirement(itemId);
|
||||
}
|
||||
|
||||
private static AnyRequirementCollection any(String name, ItemRequirement... requirements)
|
||||
{
|
||||
return new AnyRequirementCollection(name, requirements);
|
||||
}
|
||||
|
||||
private static RangeItemRequirement range(int startItemId, int endItemId)
|
||||
{
|
||||
return range(null, startItemId, endItemId);
|
||||
}
|
||||
|
||||
private static RangeItemRequirement range(String name, int startItemId, int endItemId)
|
||||
{
|
||||
return new RangeItemRequirement(name, startItemId, endItemId);
|
||||
}
|
||||
|
||||
private final String text;
|
||||
@Nonnull
|
||||
private final ItemRequirement[] itemRequirements;
|
||||
|
||||
private FaloTheBardClue(String text, @Nonnull ItemRequirement... itemRequirements)
|
||||
{
|
||||
this.text = text;
|
||||
this.itemRequirements = itemRequirements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Falo the Bard Clue").build());
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("NPC:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(FALO_THE_BARD)
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Item:").build());
|
||||
|
||||
Item[] inventory = plugin.getInventoryItems();
|
||||
|
||||
// If inventory is null, the player has nothing in their inventory
|
||||
if (inventory == null)
|
||||
{
|
||||
inventory = new Item[0];
|
||||
}
|
||||
|
||||
for (ItemRequirement requirement : itemRequirements)
|
||||
{
|
||||
boolean inventoryFulfilled = requirement.fulfilledBy(inventory);
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(requirement.getCollectiveName(plugin.getClient()))
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.right(inventoryFulfilled ? "\u2713" : "\u2717")
|
||||
.rightColor(inventoryFulfilled ? Color.GREEN : Color.RED)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
if (!LOCATION.isInScene(plugin.getClient()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (NPC npc : plugin.getNpcsToMark())
|
||||
{
|
||||
OverlayUtil.renderActorOverlayImage(graphics, npc, plugin.getClueScrollImage(), Color.ORANGE, IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNpcs()
|
||||
{
|
||||
return new String[] {FALO_THE_BARD};
|
||||
}
|
||||
|
||||
public static FaloTheBardClue forText(String text)
|
||||
{
|
||||
for (FaloTheBardClue clue : CLUES)
|
||||
{
|
||||
if (clue.text.equalsIgnoreCase(text))
|
||||
{
|
||||
return clue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Eadgars Ruse <https://github.com/Eadgars-Ruse>
|
||||
* Copyright (c) 2019, Jordan Atwood <nightfirecat@protonmail.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.cluescrolls.clues;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdLocation;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdSolver;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdTemperature;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdTemperatureChange;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
@EqualsAndHashCode(callSuper = false, exclude = { "hotColdSolver", "location" })
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class HotColdClue extends ClueScroll implements LocationClueScroll, LocationsClueScroll, TextClueScroll, NpcClueScroll
|
||||
{
|
||||
private static final HotColdClue BEGINNER_CLUE = new HotColdClue("Buried beneath the ground, who knows where it's found. Lucky for you, A man called Reldo may have a clue.",
|
||||
"Reldo",
|
||||
"Speak to Reldo to receive a strange device.",
|
||||
new WorldPoint(3211, 3494, 0));
|
||||
private static final HotColdClue MASTER_CLUE = new HotColdClue("Buried beneath the ground, who knows where it's found. Lucky for you, A man called Jorral may have a clue.",
|
||||
"Jorral",
|
||||
"Speak to Jorral to receive a strange device.",
|
||||
new WorldPoint(2436, 3347, 0));
|
||||
private static final HotColdClue MASTER_CLUE_LEAGUE = new HotColdClue("Buried beneath the ground, who knows where it's found. Lucky for you, A man called Watson may have a clue.",
|
||||
"Watson",
|
||||
"Speak to Watson to receive a strange device.",
|
||||
new WorldPoint(1645, 3572, 0));
|
||||
|
||||
private final String text;
|
||||
private final String npc;
|
||||
private final String solution;
|
||||
private final WorldPoint npcLocation;
|
||||
@Nullable
|
||||
private HotColdSolver hotColdSolver;
|
||||
private WorldPoint location;
|
||||
|
||||
public static HotColdClue forText(String text)
|
||||
{
|
||||
if (BEGINNER_CLUE.text.equalsIgnoreCase(text))
|
||||
{
|
||||
BEGINNER_CLUE.reset();
|
||||
return BEGINNER_CLUE;
|
||||
}
|
||||
else if (MASTER_CLUE.text.equalsIgnoreCase(text))
|
||||
{
|
||||
MASTER_CLUE.reset();
|
||||
return MASTER_CLUE;
|
||||
}
|
||||
else if (MASTER_CLUE_LEAGUE.text.equalsIgnoreCase(text))
|
||||
{
|
||||
MASTER_CLUE_LEAGUE.reset();
|
||||
return MASTER_CLUE_LEAGUE;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HotColdClue(String text, String npc, String solution, WorldPoint npcLocation)
|
||||
{
|
||||
this.text = text;
|
||||
this.npc = npc;
|
||||
this.solution = solution;
|
||||
this.npcLocation = npcLocation;
|
||||
setRequiresSpade(true);
|
||||
initializeSolver();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldPoint[] getLocations()
|
||||
{
|
||||
if (hotColdSolver == null)
|
||||
{
|
||||
return new WorldPoint[0];
|
||||
}
|
||||
|
||||
if (hotColdSolver.getLastWorldPoint() == null)
|
||||
{
|
||||
return new WorldPoint[] {npcLocation};
|
||||
}
|
||||
else
|
||||
{
|
||||
return hotColdSolver.getPossibleLocations().stream().map(HotColdLocation::getWorldPoint).toArray(WorldPoint[]::new);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
if (hotColdSolver == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
panelComponent.getChildren().add(TitleComponent.builder()
|
||||
.text("Hot/Cold Clue")
|
||||
.build());
|
||||
|
||||
// strange device has not been tested yet, show how to get it
|
||||
if (hotColdSolver.getLastWorldPoint() == null && location == null)
|
||||
{
|
||||
if (getNpc() != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("NPC:")
|
||||
.build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getNpc())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
}
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Solution:")
|
||||
.build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(getSolution())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
}
|
||||
// strange device has been tested, show possible locations for final dig spot
|
||||
else
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Possible locations:")
|
||||
.build());
|
||||
|
||||
final Map<HotColdArea, Integer> locationCounts = new EnumMap<>(HotColdArea.class);
|
||||
final Collection<HotColdLocation> digLocations = hotColdSolver.getPossibleLocations();
|
||||
|
||||
for (HotColdLocation hotColdLocation : digLocations)
|
||||
{
|
||||
HotColdArea hotColdArea = hotColdLocation.getHotColdArea();
|
||||
|
||||
if (locationCounts.containsKey(hotColdArea))
|
||||
{
|
||||
locationCounts.put(hotColdArea, locationCounts.get(hotColdArea) + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
locationCounts.put(hotColdArea, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (digLocations.size() > 10)
|
||||
{
|
||||
for (HotColdArea area : locationCounts.keySet())
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(area.getName())
|
||||
.right(Integer.toString(locationCounts.get(area)))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (HotColdArea area : locationCounts.keySet())
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(area.getName() + ':')
|
||||
.build());
|
||||
|
||||
for (HotColdLocation hotColdLocation : digLocations)
|
||||
{
|
||||
if (hotColdLocation.getHotColdArea() == area)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("- " + hotColdLocation.getArea())
|
||||
.leftColor(Color.LIGHT_GRAY)
|
||||
.build());
|
||||
|
||||
if (digLocations.size() <= 5 && hotColdLocation.getEnemy() != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(hotColdLocation.getEnemy().getText())
|
||||
.leftColor(Color.YELLOW)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
if (hotColdSolver == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// when final location has been found
|
||||
if (location != null)
|
||||
{
|
||||
LocalPoint localLocation = LocalPoint.fromWorld(plugin.getClient(), getLocation());
|
||||
|
||||
if (localLocation != null)
|
||||
{
|
||||
OverlayUtil.renderTileOverlay(plugin.getClient(), graphics, localLocation, plugin.getSpadeImage(), Color.ORANGE);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// when strange device hasn't been activated yet, show npc who gives you the strange device
|
||||
if (hotColdSolver.getLastWorldPoint() == null && plugin.getNpcsToMark() != null)
|
||||
{
|
||||
for (NPC npcToMark : plugin.getNpcsToMark())
|
||||
{
|
||||
OverlayUtil.renderActorOverlayImage(graphics, npcToMark, plugin.getClueScrollImage(), Color.ORANGE, IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
// once the number of possible dig locations is below 10, show the dig spots
|
||||
final Collection<HotColdLocation> digLocations = hotColdSolver.getPossibleLocations();
|
||||
if (digLocations.size() < 10)
|
||||
{
|
||||
// Mark potential dig locations
|
||||
for (HotColdLocation hotColdLocation : digLocations)
|
||||
{
|
||||
WorldPoint wp = hotColdLocation.getWorldPoint();
|
||||
LocalPoint localLocation = LocalPoint.fromWorld(plugin.getClient(), wp.getX(), wp.getY());
|
||||
|
||||
if (localLocation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OverlayUtil.renderTileOverlay(plugin.getClient(), graphics, localLocation, plugin.getSpadeImage(), Color.ORANGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean update(final String message, final ClueScrollPlugin plugin)
|
||||
{
|
||||
if (hotColdSolver == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Set<HotColdTemperature> temperatureSet;
|
||||
|
||||
if (this == BEGINNER_CLUE)
|
||||
{
|
||||
temperatureSet = HotColdTemperature.BEGINNER_HOT_COLD_TEMPERATURES;
|
||||
}
|
||||
else if (this == MASTER_CLUE || this == MASTER_CLUE_LEAGUE)
|
||||
{
|
||||
temperatureSet = HotColdTemperature.MASTER_HOT_COLD_TEMPERATURES;
|
||||
}
|
||||
else
|
||||
{
|
||||
temperatureSet = null;
|
||||
}
|
||||
|
||||
final HotColdTemperature temperature = HotColdTemperature.getFromTemperatureSet(temperatureSet, message);
|
||||
|
||||
if (temperature == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert from real to overworld
|
||||
final WorldPoint localWorld = ClueScrollPlugin.getMirrorPoint(plugin.getClient().getLocalPlayer().getWorldLocation(), true);
|
||||
|
||||
if (localWorld == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean master = this == MASTER_CLUE || this == MASTER_CLUE_LEAGUE;
|
||||
if ((this == BEGINNER_CLUE && temperature == HotColdTemperature.BEGINNER_VISIBLY_SHAKING)
|
||||
|| (master && temperature == HotColdTemperature.MASTER_VISIBLY_SHAKING))
|
||||
{
|
||||
markFinalSpot(localWorld);
|
||||
}
|
||||
else
|
||||
{
|
||||
location = null;
|
||||
}
|
||||
|
||||
final HotColdTemperatureChange temperatureChange = HotColdTemperatureChange.of(message);
|
||||
hotColdSolver.signal(localWorld, temperature, temperatureChange);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
location = null;
|
||||
initializeSolver();
|
||||
}
|
||||
|
||||
private void initializeSolver()
|
||||
{
|
||||
final boolean isBeginner;
|
||||
|
||||
if (this == BEGINNER_CLUE)
|
||||
{
|
||||
isBeginner = true;
|
||||
}
|
||||
else if (this == MASTER_CLUE || this == MASTER_CLUE_LEAGUE)
|
||||
{
|
||||
isBeginner = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("Hot cold solver could not be initialized, clue type is unknown; text: {}, npc: {}, solution: {}",
|
||||
text, npc, solution);
|
||||
hotColdSolver = null;
|
||||
return;
|
||||
}
|
||||
|
||||
final Set<HotColdLocation> locations = Arrays.stream(HotColdLocation.values())
|
||||
.filter(l -> l.isBeginnerClue() == isBeginner)
|
||||
.collect(Collectors.toSet());
|
||||
hotColdSolver = new HotColdSolver(locations);
|
||||
}
|
||||
|
||||
private void markFinalSpot(WorldPoint wp)
|
||||
{
|
||||
this.location = wp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNpcs()
|
||||
{
|
||||
return new String[] {npc};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Tomas Slusny <slusnucky@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.cluescrolls.clues;
|
||||
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
|
||||
public interface LocationClueScroll
|
||||
{
|
||||
WorldPoint getLocation();
|
||||
|
||||
default WorldPoint[] getLocations()
|
||||
{
|
||||
WorldPoint location = getLocation();
|
||||
return location == null ? new WorldPoint[0] : new WorldPoint[]{location};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Tomas Slusny <slusnucky@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.cluescrolls.clues;
|
||||
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
|
||||
public interface LocationsClueScroll
|
||||
{
|
||||
void reset();
|
||||
|
||||
WorldPoint[] getLocations();
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import static net.runelite.api.ItemID.*;
|
||||
import net.runelite.api.ObjectComposition;
|
||||
import static net.runelite.api.ObjectID.CRATE_18506;
|
||||
import static net.runelite.api.ObjectID.CRATE_2620;
|
||||
import static net.runelite.api.ObjectID.CRATE_354;
|
||||
import static net.runelite.api.ObjectID.CRATE_357;
|
||||
import static net.runelite.api.ObjectID.CRATE_6616;
|
||||
import net.runelite.api.TileObject;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_BORDER_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_FILL_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_HOVER_BORDER_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
@Getter
|
||||
public class MapClue extends ClueScroll implements ObjectClueScroll
|
||||
{
|
||||
public static final String CHAMPIONS_GUILD = "West of the Champions' Guild";
|
||||
public static final String VARROCK_EAST_MINE = "Outside Varrock East Mine";
|
||||
public static final String STANDING_STONES = "At the standing stones north of Falador";
|
||||
public static final String WIZARDS_TOWER_DIS = "On the south side of the Wizard's Tower (DIS)";
|
||||
public static final String SOUTH_OF_DRAYNOR_BANK = "South of Draynor Village Bank";
|
||||
|
||||
private static final Set<MapClue> CLUES = ImmutableSet.of(
|
||||
new MapClue(CLUE_SCROLL_EASY_12179, new WorldPoint(3300, 3291, 0), "Al Kharid mine"),
|
||||
new MapClue(CLUE_SCROLL_EASY_2713, new WorldPoint(3166, 3361, 0), CHAMPIONS_GUILD),
|
||||
new MapClue(CLUE_SCROLL_EASY_2716, new WorldPoint(3290, 3374, 0), VARROCK_EAST_MINE),
|
||||
new MapClue(CLUE_SCROLL_EASY_2719, new WorldPoint(3043, 3398, 0), STANDING_STONES),
|
||||
new MapClue(CLUE_SCROLL_EASY_3516, new WorldPoint(2612, 3482, 0), "Brother Galahad's house, West of McGrubor's Wood."),
|
||||
new MapClue(CLUE_SCROLL_EASY_3518, new WorldPoint(3110, 3152, 0), WIZARDS_TOWER_DIS),
|
||||
new MapClue(CLUE_SCROLL_EASY_7236, new WorldPoint(2970, 3415, 0), "North of Falador."),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_2827, new WorldPoint(3091, 3227, 0), SOUTH_OF_DRAYNOR_BANK),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_3596, new WorldPoint(2907, 3295, 0), "West of the Crafting Guild"),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_3598, new WorldPoint(2658, 3488, 0), CRATE_357, "The crate in McGrubor's Wood. Fairy ring ALS"),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_3599, new WorldPoint(2651, 3231, 0), "North of the Tower of Life. Fairy ring DJP"),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_3601, new WorldPoint(2565, 3248, 0), CRATE_354, "The crate west of the Clocktower."),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_3602, new WorldPoint(2924, 3210, 0), "Behind the Chemist's house in Rimmington."),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_7286, new WorldPoint(2536, 3865, 0), "Miscellania. Fairy ring CIP"),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_7288, new WorldPoint(3434, 3265, 0), "Mort Myre Swamp, west of Mort'ton. Fairy ring BIP"),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_7290, new WorldPoint(2454, 3230, 0), "At the entrance to the Ourania Cave."),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_7292, new WorldPoint(2578, 3597, 0), "South-east of the Lighthouse. Fairy ring ALP"),
|
||||
new MapClue(CLUE_SCROLL_MEDIUM_7294, new WorldPoint(2666, 3562, 0), "Between Seers' Village and Rellekka. South-west of Fairy ring CJR"),
|
||||
new MapClue(CLUE_SCROLL_HARD, new WorldPoint(3309, 3503, 0), CRATE_2620, "A crate in the Lumber Yard, north-east of Varrock."),
|
||||
new MapClue(CLUE_SCROLL_HARD_2729, new WorldPoint(3190, 3963, 0), "Behind the Magic axe hut in level 56 Wilderness."),
|
||||
new MapClue(CLUE_SCROLL_HARD_3520, new WorldPoint(2615, 3078, 0), "Yanille anvils, south of the bank."),
|
||||
new MapClue(CLUE_SCROLL_HARD_3522, new WorldPoint(2488, 3308, 0), "In the western section of West Ardougne."),
|
||||
new MapClue(CLUE_SCROLL_HARD_3524, new WorldPoint(2457, 3182, 0), CRATE_18506, "In a crate by the stairs to the Observatory Dungeon."),
|
||||
new MapClue(CLUE_SCROLL_HARD_3525, new WorldPoint(3026, 3628, 0), CRATE_354, "In a crate at the Dark Warriors' Fortress in level 14 Wilderness."),
|
||||
new MapClue(CLUE_SCROLL_HARD_7239, new WorldPoint(3021, 3912, 0), "South-east of the Wilderness Agility Course in level 50 Wilderness."),
|
||||
new MapClue(CLUE_SCROLL_HARD_7241, new WorldPoint(2722, 3338, 0), "South of the Legends' Guild. Fairy ring BLR"),
|
||||
new MapClue(CLUE_SCROLL_ELITE_12130, new WorldPoint(2449, 3130, 0), "South-west of Tree Gnome Village."),
|
||||
new MapClue(CLUE_SCROLL_ELITE_19782, new WorldPoint(2953, 9523, 1), "In the Mogre Camp, near Port Khazard. You require a Diving Apparatus and a Fishbowl Helmet"),
|
||||
new MapClue(CLUE_SCROLL_ELITE_19783, new WorldPoint(2202, 3062, 0), "Zul-Andra. Fairy ring BJS"),
|
||||
new MapClue(CLUE_SCROLL_ELITE_19784, new WorldPoint(1815, 3852, 0), "At the Soul Altar, north-east of the Arceuus essence mine."),
|
||||
new MapClue(CLUE_SCROLL_ELITE_19785, new WorldPoint(3538, 3208, 0), "East of Burgh de Rott."),
|
||||
new MapClue(CLUE_SCROLL_ELITE_19786, new WorldPoint(2703, 2716, 0), CRATE_6616, "The crate in south-western Ape Atoll")
|
||||
);
|
||||
|
||||
private final int itemId;
|
||||
private final WorldPoint location;
|
||||
private final int objectId;
|
||||
private final String description;
|
||||
|
||||
private MapClue(int itemId, WorldPoint location)
|
||||
{
|
||||
this(itemId, location, -1);
|
||||
}
|
||||
|
||||
private MapClue(int itemId, WorldPoint location, int objectId)
|
||||
{
|
||||
this(itemId, location, objectId, null);
|
||||
}
|
||||
|
||||
MapClue(int itemId, WorldPoint location, String description)
|
||||
{
|
||||
this(itemId, location, -1, description);
|
||||
}
|
||||
|
||||
private MapClue(int itemId, WorldPoint location, int objectId, String description)
|
||||
{
|
||||
this.itemId = itemId;
|
||||
this.location = location;
|
||||
this.objectId = objectId;
|
||||
this.description = description;
|
||||
setRequiresSpade(objectId == -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Map Clue").build());
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Click the clue scroll along the edge of your world map to see your destination.")
|
||||
.build());
|
||||
|
||||
if (objectId != -1)
|
||||
{
|
||||
ObjectComposition objectToClick = plugin.getClient().getObjectDefinition(getObjectId());
|
||||
|
||||
String objectName = "N/A";
|
||||
|
||||
if (objectToClick != null)
|
||||
{
|
||||
objectName = objectToClick.getName();
|
||||
}
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Travel to the destination and click the " + objectName + ".")
|
||||
.build());
|
||||
}
|
||||
else
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Travel to the destination and dig on the marked tile.")
|
||||
.build());
|
||||
}
|
||||
|
||||
if (description != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(description)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
LocalPoint localLocation = LocalPoint.fromWorld(plugin.getClient(), getLocation());
|
||||
|
||||
if (localLocation == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark game object
|
||||
if (objectId != -1)
|
||||
{
|
||||
net.runelite.api.Point mousePosition = plugin.getClient().getMouseCanvasPosition();
|
||||
|
||||
if (plugin.getObjectsToMark() != null)
|
||||
{
|
||||
for (TileObject gameObject : plugin.getObjectsToMark())
|
||||
{
|
||||
OverlayUtil.renderHoverableArea(graphics, gameObject.getClickbox(), mousePosition,
|
||||
CLICKBOX_FILL_COLOR, CLICKBOX_BORDER_COLOR, CLICKBOX_HOVER_BORDER_COLOR);
|
||||
|
||||
OverlayUtil.renderImageLocation(plugin.getClient(), graphics, gameObject.getLocalLocation(), plugin.getClueScrollImage(), IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mark tile
|
||||
else
|
||||
{
|
||||
OverlayUtil.renderTileOverlay(plugin.getClient(), graphics, localLocation, plugin.getSpadeImage(), Color.ORANGE);
|
||||
}
|
||||
}
|
||||
|
||||
public static MapClue forItemId(int itemId)
|
||||
{
|
||||
for (MapClue clue : CLUES)
|
||||
{
|
||||
if (clue.itemId == itemId)
|
||||
{
|
||||
return clue;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getObjectIds()
|
||||
{
|
||||
return new int[] {objectId};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@Getter
|
||||
public class MusicClue extends ClueScroll implements NpcClueScroll
|
||||
{
|
||||
private static final WorldPoint LOCATION = new WorldPoint(2990, 3384, 0);
|
||||
private static final String CECILIA = "Cecilia";
|
||||
private static final Pattern SONG_PATTERN = Pattern.compile("<col=ffffff>([A-Za-z !&',.]+)</col>");
|
||||
|
||||
private final String song;
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Music Clue").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("NPC:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(CECILIA)
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Location:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Falador Park")
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Song:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(song)
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
if (!LOCATION.isInScene(plugin.getClient()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (NPC npc : plugin.getNpcsToMark())
|
||||
{
|
||||
OverlayUtil.renderActorOverlayImage(graphics, npc, plugin.getClueScrollImage(), Color.ORANGE, IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNpcs()
|
||||
{
|
||||
return new String[] {CECILIA};
|
||||
}
|
||||
|
||||
public static MusicClue forText(String text)
|
||||
{
|
||||
final Matcher m = SONG_PATTERN.matcher(text);
|
||||
if (m.find())
|
||||
{
|
||||
final String song = m.group(1);
|
||||
return new MusicClue(song);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Jordan Atwood <jordan.atwood423@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.cluescrolls.clues;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a clue which should highlight objects of a given name rather than a specific ID and location, as some
|
||||
* clues will call for a general action which can be completed at any number of locations. The area in which this
|
||||
* highlighting should occur can be restricted by giving a non-null array of region IDs where only objects within those
|
||||
* regions will be highlighted.
|
||||
*/
|
||||
public interface NamedObjectClueScroll
|
||||
{
|
||||
String[] getObjectNames();
|
||||
|
||||
@Nullable
|
||||
int[] getObjectRegions();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
public interface NpcClueScroll
|
||||
{
|
||||
String[] getNpcs();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
public interface ObjectClueScroll extends LocationClueScroll
|
||||
{
|
||||
int[] getObjectIds();
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.runelite.api.EquipmentInventorySlot;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.TileObject;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_BORDER_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_FILL_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_HOVER_BORDER_COLOR;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.AnyRequirementCollection;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirements.*;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.item.SingleItemRequirement;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET;
|
||||
|
||||
@Getter
|
||||
public class SkillChallengeClue extends ClueScroll implements NpcClueScroll, NamedObjectClueScroll
|
||||
{
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
enum ChallengeType
|
||||
{
|
||||
CHARLIE("Charlie the Tramp", "Southern Entrance to Varrock"),
|
||||
SHERLOCK("Sherlock", "East of the Sorcerer's Tower in Seers' Village");
|
||||
|
||||
private String name;
|
||||
private String location;
|
||||
}
|
||||
|
||||
private static final AnyRequirementCollection ANY_PICKAXE = any("Any Pickaxe",
|
||||
item(ItemID.BRONZE_PICKAXE),
|
||||
item(ItemID.IRON_PICKAXE),
|
||||
item(ItemID.STEEL_PICKAXE),
|
||||
item(ItemID.BLACK_PICKAXE),
|
||||
item(ItemID.MITHRIL_PICKAXE),
|
||||
item(ItemID.ADAMANT_PICKAXE),
|
||||
item(ItemID.RUNE_PICKAXE),
|
||||
item(ItemID.DRAGON_PICKAXE),
|
||||
item(ItemID.DRAGON_PICKAXE_12797),
|
||||
item(ItemID.DRAGON_PICKAXE_OR),
|
||||
item(ItemID.INFERNAL_PICKAXE),
|
||||
item(ItemID.INFERNAL_PICKAXE_UNCHARGED),
|
||||
item(ItemID.GILDED_PICKAXE),
|
||||
item(ItemID._3RD_AGE_PICKAXE),
|
||||
item(ItemID.CRYSTAL_PICKAXE)
|
||||
);
|
||||
|
||||
private static final AnyRequirementCollection ANY_AXE = any("Any Axe",
|
||||
item(ItemID.BRONZE_AXE),
|
||||
item(ItemID.IRON_AXE),
|
||||
item(ItemID.STEEL_AXE),
|
||||
item(ItemID.BLACK_AXE),
|
||||
item(ItemID.MITHRIL_AXE),
|
||||
item(ItemID.ADAMANT_AXE),
|
||||
item(ItemID.RUNE_AXE),
|
||||
item(ItemID.DRAGON_AXE),
|
||||
item(ItemID.INFERNAL_AXE),
|
||||
item(ItemID.INFERNAL_AXE_UNCHARGED),
|
||||
item(ItemID.GILDED_AXE),
|
||||
item(ItemID._3RD_AGE_AXE),
|
||||
item(ItemID.CRYSTAL_AXE)
|
||||
);
|
||||
|
||||
private static final AnyRequirementCollection ANY_HARPOON = any("Harpoon",
|
||||
item(ItemID.HARPOON),
|
||||
item(ItemID.BARBTAIL_HARPOON),
|
||||
item(ItemID.DRAGON_HARPOON),
|
||||
item(ItemID.INFERNAL_HARPOON),
|
||||
item(ItemID.INFERNAL_HARPOON_UNCHARGED),
|
||||
item(ItemID.CRYSTAL_HARPOON)
|
||||
);
|
||||
|
||||
private static final Set<SkillChallengeClue> CLUES = ImmutableSet.of(
|
||||
// Charlie Tasks
|
||||
new SkillChallengeClue("Cook a Pike", "i need to cook charlie a pike.", "i need to take the cooked pike to charlie.", item(ItemID.PIKE), item(ItemID.RAW_PIKE)),
|
||||
new SkillChallengeClue("Cook a Trout", "i need to cook charlie a trout.", "i need to take the cooked trout to charlie.", item(ItemID.TROUT), item(ItemID.RAW_TROUT)),
|
||||
new SkillChallengeClue("Craft a Leather Body", "i need to craft charlie a leather body.", "i need to take the leather body i crafted to charlie.", item(ItemID.LEATHER_BODY), item(ItemID.LEATHER), item(ItemID.NEEDLE), item(ItemID.THREAD)),
|
||||
new SkillChallengeClue("Craft some Leather Chaps", "i need to craft charlie some leather chaps.", "i need to take the leather chaps i crafted to charlie.", item(ItemID.LEATHER_CHAPS), item(ItemID.LEATHER), item(ItemID.NEEDLE), item(ItemID.THREAD)),
|
||||
new SkillChallengeClue("Fish a Herring", "i need to fish charlie a herring.", "i need to take a raw herring to charlie.", item(ItemID.RAW_HERRING), any("Fishing rod", item(ItemID.FISHING_ROD), item(ItemID.PEARL_FISHING_ROD)), item(ItemID.FISHING_BAIT)),
|
||||
new SkillChallengeClue("Fish a Trout", "i need to fish charlie a trout.", "i need to take a raw trout to charlie.", item(ItemID.RAW_TROUT), any("Fly fishing rod", item(ItemID.FLY_FISHING_ROD), item(ItemID.PEARL_FLY_FISHING_ROD)), item(ItemID.FEATHER)),
|
||||
new SkillChallengeClue("Mine a piece of Iron Ore", "i need to mine charlie a piece of iron ore from an iron vein.", "i need to take the iron ore to charlie.", item(ItemID.IRON_ORE), ANY_PICKAXE),
|
||||
new SkillChallengeClue("Smith an Iron Dagger", "i need to smith charlie one iron dagger.", "i need to take the iron dagger i smithed to charlie.", item(ItemID.IRON_DAGGER), item(ItemID.IRON_BAR), item(ItemID.HAMMER)),
|
||||
// Elite Sherlock Tasks
|
||||
new SkillChallengeClue("Equip a Dragon Scimitar.", true, any("Any Dragon Scimitar", item(ItemID.DRAGON_SCIMITAR), item(ItemID.DRAGON_SCIMITAR_OR))),
|
||||
new SkillChallengeClue("Enchant some Dragonstone Jewellery.", "enchant a piece of dragonstone jewellery.",
|
||||
xOfItem(ItemID.COSMIC_RUNE, 1),
|
||||
any("Water Rune x15", xOfItem(ItemID.WATER_RUNE, 15), xOfItem(ItemID.MIST_RUNE, 15), xOfItem(ItemID.MUD_RUNE, 15), xOfItem(ItemID.STEAM_RUNE, 15), item(ItemID.STAFF_OF_WATER), item(ItemID.WATER_BATTLESTAFF), item(ItemID.MYSTIC_WATER_STAFF), item(ItemID.MUD_BATTLESTAFF), item(ItemID.MYSTIC_MUD_STAFF), item(ItemID.MIST_BATTLESTAFF), item(ItemID.MYSTIC_MIST_STAFF), item(ItemID.STEAM_BATTLESTAFF), item(ItemID.MYSTIC_STEAM_STAFF), item(ItemID.STEAM_BATTLESTAFF_12795), item(ItemID.MYSTIC_STEAM_STAFF_12796), item(ItemID.KODAI_WAND)),
|
||||
any("Earth Rune x15", xOfItem(ItemID.EARTH_RUNE, 15), xOfItem(ItemID.DUST_RUNE, 15), xOfItem(ItemID.MUD_RUNE, 15), xOfItem(ItemID.LAVA_RUNE, 15), item(ItemID.STAFF_OF_EARTH), item(ItemID.EARTH_BATTLESTAFF), item(ItemID.MYSTIC_EARTH_STAFF), item(ItemID.MUD_BATTLESTAFF), item(ItemID.MYSTIC_MUD_STAFF), item(ItemID.DUST_BATTLESTAFF), item(ItemID.MYSTIC_DUST_STAFF), item(ItemID.LAVA_BATTLESTAFF), item(ItemID.MYSTIC_LAVA_STAFF), item(ItemID.LAVA_BATTLESTAFF_21198), item(ItemID.MYSTIC_LAVA_STAFF_21200)),
|
||||
any("Unenchanted Dragonstone Jewellery", item(ItemID.DRAGONSTONE_RING), item(ItemID.DRAGON_NECKLACE), item(ItemID.DRAGONSTONE_BRACELET), item(ItemID.DRAGONSTONE_AMULET))),
|
||||
new SkillChallengeClue("Craft a nature rune.", item(ItemID.PURE_ESSENCE)),
|
||||
new SkillChallengeClue("Catch a mottled eel with aerial fishing in Lake Molch.", any("Fish chunks or King worms", item(ItemID.FISH_CHUNKS), item(ItemID.KING_WORM)), emptySlot("No Gloves", EquipmentInventorySlot.GLOVES), emptySlot("No Weapon", EquipmentInventorySlot.WEAPON), emptySlot("No Shield", EquipmentInventorySlot.SHIELD)),
|
||||
new SkillChallengeClue("Score a goal in skullball.", true, any("Ring of Charos", item(ItemID.RING_OF_CHAROS), item(ItemID.RING_OF_CHAROSA))),
|
||||
new SkillChallengeClue("Complete a lap of Ape atoll agility course.", true, any("Ninja Monkey Greegree", item(ItemID.NINJA_MONKEY_GREEGREE), item(ItemID.NINJA_MONKEY_GREEGREE_4025), item(ItemID.KRUK_MONKEY_GREEGREE))),
|
||||
new SkillChallengeClue("Create a super defence potion.", item(ItemID.CADANTINE_POTION_UNF), item(ItemID.WHITE_BERRIES)),
|
||||
new SkillChallengeClue("Steal from a chest in Ardougne Castle."),
|
||||
new SkillChallengeClue("Craft a green dragonhide body.", xOfItem(ItemID.GREEN_DRAGON_LEATHER, 3), item(ItemID.NEEDLE), item(ItemID.THREAD)),
|
||||
new SkillChallengeClue("String a yew longbow.", item(ItemID.YEW_LONGBOW_U), item(ItemID.BOW_STRING)),
|
||||
new SkillChallengeClue("Kill a Dust Devil.", "slay a dust devil.", true, any("Facemask or Slayer Helmet", item(ItemID.FACEMASK), item(ItemID.SLAYER_HELMET), item(ItemID. SLAYER_HELMET_I), item(ItemID. BLACK_SLAYER_HELMET), item(ItemID. BLACK_SLAYER_HELMET_I), item(ItemID. PURPLE_SLAYER_HELMET), item(ItemID. PURPLE_SLAYER_HELMET_I), item(ItemID. RED_SLAYER_HELMET), item(ItemID. RED_SLAYER_HELMET_I), item(ItemID.GREEN_SLAYER_HELMET), item(ItemID. GREEN_SLAYER_HELMET_I), item(ItemID. TURQUOISE_SLAYER_HELMET), item(ItemID. TURQUOISE_SLAYER_HELMET_I), item(ItemID. HYDRA_SLAYER_HELMET), item(ItemID. HYDRA_SLAYER_HELMET_I))),
|
||||
new SkillChallengeClue("Catch a black warlock.", item(ItemID.BUTTERFLY_JAR), any("Butterfly Net", item(ItemID.BUTTERFLY_NET), item(ItemID.MAGIC_BUTTERFLY_NET))),
|
||||
new SkillChallengeClue("Catch a red chinchompa.", item(ItemID.BOX_TRAP)),
|
||||
new SkillChallengeClue("Mine a mithril ore.", ANY_PICKAXE),
|
||||
new SkillChallengeClue("Smith a mithril 2h sword.", item(ItemID.HAMMER), xOfItem(ItemID.MITHRIL_BAR, 3)),
|
||||
new SkillChallengeClue("Catch a raw shark.", ANY_HARPOON),
|
||||
new SkillChallengeClue("Cut a yew log.", ANY_AXE),
|
||||
new SkillChallengeClue("Fix a magical lamp in Dorgesh-Kaan.", new String[] { "Broken lamp" }, new int[] { 10834, 10835 }, item(ItemID.LIGHT_ORB)),
|
||||
new SkillChallengeClue("Burn a yew log.", item(ItemID.YEW_LOGS), item(ItemID.TINDERBOX)),
|
||||
new SkillChallengeClue("Cook a swordfish", "cook a swordfish", item(ItemID.RAW_SWORDFISH)),
|
||||
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", "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)),
|
||||
new SkillChallengeClue("Teleport to a spirit tree you planted yourself."),
|
||||
new SkillChallengeClue("Create a Barrows teleport tablet.", item(ItemID.DARK_ESSENCE_BLOCK), xOfItem(ItemID.BLOOD_RUNE, 1), xOfItem(ItemID.LAW_RUNE, 2), xOfItem(ItemID.SOUL_RUNE, 2)),
|
||||
new SkillChallengeClue("Kill a Nechryael in the Slayer Tower.", "slay a nechryael in the slayer tower."),
|
||||
new SkillChallengeClue("Kill a Spiritual Mage while wearing something from their god.", "kill the spiritual, magic and godly whilst representing their own god."),
|
||||
new SkillChallengeClue("Create an unstrung dragonstone amulet at a furnace.", item(ItemID.GOLD_BAR), item(ItemID.DRAGONSTONE), item(ItemID.AMULET_MOULD)),
|
||||
new SkillChallengeClue("Burn a magic log.", item(ItemID.MAGIC_LOGS), item(ItemID.TINDERBOX)),
|
||||
new SkillChallengeClue("Burn a redwood log.", item(ItemID.REDWOOD_LOGS), item(ItemID.TINDERBOX)),
|
||||
new SkillChallengeClue("Complete a lap of Rellekka's Rooftop Agility Course", "complete a lap of the rellekka rooftop agility course whilst sporting the finest amount of grace.", true,
|
||||
all("A full Graceful set",
|
||||
any("", item(ItemID.GRACEFUL_HOOD), item(ItemID.GRACEFUL_HOOD_11851), item(ItemID.GRACEFUL_HOOD_13579), item(ItemID.GRACEFUL_HOOD_13580), item(ItemID.GRACEFUL_HOOD_13591), item(ItemID.GRACEFUL_HOOD_13592), item(ItemID.GRACEFUL_HOOD_13603), item(ItemID.GRACEFUL_HOOD_13604), item(ItemID.GRACEFUL_HOOD_13615), item(ItemID.GRACEFUL_HOOD_13616), item(ItemID.GRACEFUL_HOOD_13627), item(ItemID.GRACEFUL_HOOD_13628), item(ItemID.GRACEFUL_HOOD_13667), item(ItemID.GRACEFUL_HOOD_13668), item(ItemID.GRACEFUL_HOOD_21061), item(ItemID.GRACEFUL_HOOD_21063), item(ItemID.GRACEFUL_HOOD_24743), item(ItemID.GRACEFUL_HOOD_24745)),
|
||||
any("", item(ItemID.GRACEFUL_CAPE), item(ItemID.GRACEFUL_CAPE_11853), item(ItemID.GRACEFUL_CAPE_13581), item(ItemID.GRACEFUL_CAPE_13582), item(ItemID.GRACEFUL_CAPE_13593), item(ItemID.GRACEFUL_CAPE_13594), item(ItemID.GRACEFUL_CAPE_13605), item(ItemID.GRACEFUL_CAPE_13606), item(ItemID.GRACEFUL_CAPE_13617), item(ItemID.GRACEFUL_CAPE_13618), item(ItemID.GRACEFUL_CAPE_13629), item(ItemID.GRACEFUL_CAPE_13630), item(ItemID.GRACEFUL_CAPE_13669), item(ItemID.GRACEFUL_CAPE_13670), item(ItemID.GRACEFUL_CAPE_21064), item(ItemID.GRACEFUL_CAPE_21066), item(ItemID.GRACEFUL_CAPE_24746), item(ItemID.GRACEFUL_CAPE_24748), item(ItemID.AGILITY_CAPE), item(ItemID.AGILITY_CAPE_13340), item(ItemID.AGILITY_CAPET), item(ItemID.AGILITY_CAPET_13341), item(ItemID.MAX_CAPE), item(ItemID.MAX_CAPE_13342)),
|
||||
any("", item(ItemID.GRACEFUL_TOP), item(ItemID.GRACEFUL_TOP_11855), item(ItemID.GRACEFUL_TOP_13583), item(ItemID.GRACEFUL_TOP_13584), item(ItemID.GRACEFUL_TOP_13595), item(ItemID.GRACEFUL_TOP_13596), item(ItemID.GRACEFUL_TOP_13607), item(ItemID.GRACEFUL_TOP_13608), item(ItemID.GRACEFUL_TOP_13619), item(ItemID.GRACEFUL_TOP_13620), item(ItemID.GRACEFUL_TOP_13631), item(ItemID.GRACEFUL_TOP_13632), item(ItemID.GRACEFUL_TOP_13671), item(ItemID.GRACEFUL_TOP_13672), item(ItemID.GRACEFUL_TOP_21067), item(ItemID.GRACEFUL_TOP_21069), item(ItemID.GRACEFUL_TOP_24749), item(ItemID.GRACEFUL_TOP_24751)),
|
||||
any("", item(ItemID.GRACEFUL_LEGS), item(ItemID.GRACEFUL_LEGS_11857), item(ItemID.GRACEFUL_LEGS_13585), item(ItemID.GRACEFUL_LEGS_13586), item(ItemID.GRACEFUL_LEGS_13597), item(ItemID.GRACEFUL_LEGS_13598), item(ItemID.GRACEFUL_LEGS_13609), item(ItemID.GRACEFUL_LEGS_13610), item(ItemID.GRACEFUL_LEGS_13621), item(ItemID.GRACEFUL_LEGS_13622), item(ItemID.GRACEFUL_LEGS_13633), item(ItemID.GRACEFUL_LEGS_13634), item(ItemID.GRACEFUL_LEGS_13673), item(ItemID.GRACEFUL_LEGS_13674), item(ItemID.GRACEFUL_LEGS_21070), item(ItemID.GRACEFUL_LEGS_21072), item(ItemID.GRACEFUL_LEGS_24752), item(ItemID.GRACEFUL_LEGS_24754)),
|
||||
any("", item(ItemID.GRACEFUL_GLOVES), item(ItemID.GRACEFUL_GLOVES_11859), item(ItemID.GRACEFUL_GLOVES_13587), item(ItemID.GRACEFUL_GLOVES_13588), item(ItemID.GRACEFUL_GLOVES_13599), item(ItemID.GRACEFUL_GLOVES_13600), item(ItemID.GRACEFUL_GLOVES_13611), item(ItemID.GRACEFUL_GLOVES_13612), item(ItemID.GRACEFUL_GLOVES_13623), item(ItemID.GRACEFUL_GLOVES_13624), item(ItemID.GRACEFUL_GLOVES_13635), item(ItemID.GRACEFUL_GLOVES_13636), item(ItemID.GRACEFUL_GLOVES_13675), item(ItemID.GRACEFUL_GLOVES_13676), item(ItemID.GRACEFUL_GLOVES_21073), item(ItemID.GRACEFUL_GLOVES_21075), item(ItemID.GRACEFUL_GLOVES_24755), item(ItemID.GRACEFUL_GLOVES_24757)),
|
||||
any("", item(ItemID.GRACEFUL_BOOTS), item(ItemID.GRACEFUL_BOOTS_11861), item(ItemID.GRACEFUL_BOOTS_13589), item(ItemID.GRACEFUL_BOOTS_13590), item(ItemID.GRACEFUL_BOOTS_13601), item(ItemID.GRACEFUL_BOOTS_13602), item(ItemID.GRACEFUL_BOOTS_13613), item(ItemID.GRACEFUL_BOOTS_13614), item(ItemID.GRACEFUL_BOOTS_13625), item(ItemID.GRACEFUL_BOOTS_13626), item(ItemID.GRACEFUL_BOOTS_13637), item(ItemID.GRACEFUL_BOOTS_13638), item(ItemID.GRACEFUL_BOOTS_13677), item(ItemID.GRACEFUL_BOOTS_13678), item(ItemID.GRACEFUL_BOOTS_21076), item(ItemID.GRACEFUL_BOOTS_21078), item(ItemID.GRACEFUL_BOOTS_24758), item(ItemID.GRACEFUL_BOOTS_24760)))),
|
||||
new SkillChallengeClue("Mix an anti-venom potion.", item(ItemID.ANTIDOTE4_5952), xOfItem(ItemID.ZULRAHS_SCALES, 20)),
|
||||
new SkillChallengeClue("Mine a piece of Runite ore", "mine a piece of runite ore whilst sporting the finest mining gear.", true, ANY_PICKAXE, all("Prospector kit", item(ItemID.PROSPECTOR_HELMET), any("", item(ItemID.PROSPECTOR_JACKET), item(ItemID.VARROCK_ARMOUR_4)), item(ItemID.PROSPECTOR_LEGS), item(ItemID.PROSPECTOR_BOOTS))),
|
||||
new SkillChallengeClue("Steal a gem from the Ardougne market."),
|
||||
new SkillChallengeClue("Pickpocket an elf."),
|
||||
new SkillChallengeClue("Bind a blood rune at the blood altar.", item(ItemID.DARK_ESSENCE_FRAGMENTS)),
|
||||
new SkillChallengeClue("Create a ranging mix potion.", "mix a ranging mix potion.", item(ItemID.RANGING_POTION2), item(ItemID.CAVIAR)),
|
||||
new SkillChallengeClue("Fletch a rune dart.", item(ItemID.RUNE_DART_TIP), item(ItemID.FEATHER)),
|
||||
new SkillChallengeClue("Cremate a set of fiyr remains.", any("Magic or Redwood Pyre Logs", item(ItemID.MAGIC_PYRE_LOGS), item(ItemID.REDWOOD_PYRE_LOGS)), item(ItemID.TINDERBOX), item(ItemID.FIYR_REMAINS)),
|
||||
new SkillChallengeClue("Dissect a sacred eel.", item(ItemID.KNIFE), any("Fishing rod", item(ItemID.FISHING_ROD), item(ItemID.PEARL_FISHING_ROD)), item(ItemID.FISHING_BAIT)),
|
||||
new SkillChallengeClue("Kill a lizardman shaman."),
|
||||
new SkillChallengeClue("Catch an Anglerfish.", "angle for an anglerfish whilst sporting the finest fishing gear.", true, any("Fishing rod", item(ItemID.FISHING_ROD), item(ItemID.PEARL_FISHING_ROD)), item(ItemID.SANDWORMS), all("Angler's outfit", item(ItemID.ANGLER_HAT), item(ItemID.ANGLER_TOP), item(ItemID.ANGLER_WADERS), item(ItemID.ANGLER_BOOTS))),
|
||||
new SkillChallengeClue("Chop a redwood log.", "chop a redwood log whilst sporting the finest lumberjack gear.", true, ANY_AXE, all("Lumberjack outfit", item(ItemID.LUMBERJACK_HAT), item(ItemID.LUMBERJACK_TOP), item(ItemID.LUMBERJACK_LEGS), item(ItemID.LUMBERJACK_BOOTS))),
|
||||
new SkillChallengeClue("Craft a light orb in the Dorgesh-Kaan bank.", item(ItemID.CAVE_GOBLIN_WIRE), item(ItemID.EMPTY_LIGHT_ORB)),
|
||||
new SkillChallengeClue("Kill a reanimated Abyssal Demon.", "kill a reanimated abyssal.", xOfItem(ItemID.SOUL_RUNE, 4), xOfItem(ItemID.BLOOD_RUNE, 1), any("Nature Rune x4", xOfItem(ItemID.NATURE_RUNE, 4), item(ItemID.BRYOPHYTAS_STAFF)), range("Ensouled abyssal head", ItemID.ENSOULED_ABYSSAL_HEAD, ItemID.ENSOULED_ABYSSAL_HEAD_13508)),
|
||||
new SkillChallengeClue("Kill a Fiyr shade inside Mort'tons shade catacombs.", any("Any Silver Shade Key", item(ItemID.SILVER_KEY_RED), item(ItemID.SILVER_KEY_BROWN), item(ItemID.SILVER_KEY_CRIMSON), item(ItemID.SILVER_KEY_BLACK), item(ItemID.SILVER_KEY_PURPLE)))
|
||||
);
|
||||
|
||||
private final ChallengeType type;
|
||||
private final String challenge;
|
||||
private final String rawChallenge;
|
||||
private final String returnText;
|
||||
private final ItemRequirement[] itemRequirements;
|
||||
private final SingleItemRequirement returnItem;
|
||||
private final boolean requireEquip;
|
||||
private final String[] objectNames;
|
||||
private final int[] objectRegions;
|
||||
@Setter
|
||||
private boolean challengeCompleted;
|
||||
|
||||
// Charlie Tasks
|
||||
private SkillChallengeClue(String challenge, String rawChallenge, String returnText, SingleItemRequirement returnItem, ItemRequirement ... itemRequirements)
|
||||
{
|
||||
this.type = ChallengeType.CHARLIE;
|
||||
this.challenge = challenge;
|
||||
this.rawChallenge = rawChallenge;
|
||||
this.returnText = returnText;
|
||||
this.itemRequirements = itemRequirements;
|
||||
this.returnItem = returnItem;
|
||||
this.challengeCompleted = false;
|
||||
this.requireEquip = false;
|
||||
this.objectNames = new String[0];
|
||||
this.objectRegions = null;
|
||||
}
|
||||
|
||||
// Non-cryptic Sherlock Tasks
|
||||
private SkillChallengeClue(String challenge, ItemRequirement ... itemRequirements)
|
||||
{
|
||||
this(challenge, challenge.toLowerCase(), itemRequirements);
|
||||
}
|
||||
|
||||
// Non-cryptic Sherlock Tasks
|
||||
private SkillChallengeClue(String challenge, String[] objectNames, int[] objectRegions, ItemRequirement ... itemRequirements)
|
||||
{
|
||||
this(challenge, challenge.toLowerCase(), false, objectNames, objectRegions, itemRequirements);
|
||||
}
|
||||
|
||||
// Non-cryptic Sherlock Tasks
|
||||
private SkillChallengeClue(String challenge, boolean requireEquip, ItemRequirement ... itemRequirements)
|
||||
{
|
||||
this(challenge, challenge.toLowerCase(), requireEquip, new String[0], null, itemRequirements);
|
||||
}
|
||||
|
||||
// Sherlock Tasks
|
||||
private SkillChallengeClue(String challenge, String rawChallenge, ItemRequirement ... itemRequirements)
|
||||
{
|
||||
this(challenge, rawChallenge, false, new String[0], null, itemRequirements);
|
||||
}
|
||||
|
||||
// Sherlock Tasks
|
||||
private SkillChallengeClue(String challenge, String rawChallenge, boolean requireEquip, ItemRequirement ... itemRequirements)
|
||||
{
|
||||
this(challenge, rawChallenge, requireEquip, new String[0], null, itemRequirements);
|
||||
}
|
||||
|
||||
// Sherlock Tasks
|
||||
private SkillChallengeClue(String challenge, String rawChallenge, boolean requireEquip, String[] objectNames, int[] objectRegions, ItemRequirement ... itemRequirements)
|
||||
{
|
||||
this.type = ChallengeType.SHERLOCK;
|
||||
this.challenge = challenge;
|
||||
this.rawChallenge = rawChallenge;
|
||||
this.itemRequirements = itemRequirements;
|
||||
this.challengeCompleted = false;
|
||||
this.requireEquip = requireEquip;
|
||||
this.objectNames = objectNames;
|
||||
this.objectRegions = objectRegions;
|
||||
this.returnText = "<str>" + rawChallenge + "</str>";
|
||||
|
||||
this.returnItem = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Skill Challenge Clue").build());
|
||||
|
||||
if (!challengeCompleted)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Challenge:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(challenge)
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
if (itemRequirements.length > 0)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left(requireEquip ? "Equipment:" : "Items Required:").build());
|
||||
|
||||
for (LineComponent line : getRequirements(plugin, requireEquip, itemRequirements))
|
||||
{
|
||||
panelComponent.getChildren().add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("NPC:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(type.getName())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Location:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(type.getLocation())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
|
||||
if (returnItem != null)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Item:").build());
|
||||
for (LineComponent line : getRequirements(plugin, false, returnItem))
|
||||
{
|
||||
panelComponent.getChildren().add(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
// Mark NPC
|
||||
if (plugin.getNpcsToMark() != null)
|
||||
{
|
||||
for (NPC npc : plugin.getNpcsToMark())
|
||||
{
|
||||
OverlayUtil.renderActorOverlayImage(graphics, npc, plugin.getClueScrollImage(), Color.ORANGE, IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark objects
|
||||
if (!challengeCompleted && objectNames.length > 0 && plugin.getNamedObjectsToMark() != null)
|
||||
{
|
||||
final Point mousePosition = plugin.getClient().getMouseCanvasPosition();
|
||||
|
||||
for (final TileObject object : plugin.getNamedObjectsToMark())
|
||||
{
|
||||
if (plugin.getClient().getPlane() != object.getPlane())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OverlayUtil.renderHoverableArea(graphics, object.getClickbox(), mousePosition,
|
||||
CLICKBOX_FILL_COLOR, CLICKBOX_BORDER_COLOR, CLICKBOX_HOVER_BORDER_COLOR);
|
||||
|
||||
OverlayUtil.renderImageLocation(plugin.getClient(), graphics, object.getLocalLocation(), plugin.getClueScrollImage(), IMAGE_Z_OFFSET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<LineComponent> getRequirements(ClueScrollPlugin plugin, boolean requireEquipped, ItemRequirement ... requirements)
|
||||
{
|
||||
List<LineComponent> components = new ArrayList<>();
|
||||
|
||||
Item[] equipment = plugin.getEquippedItems();
|
||||
Item[] inventory = plugin.getInventoryItems();
|
||||
|
||||
// If equipment is null, the player is wearing nothing
|
||||
if (equipment == null)
|
||||
{
|
||||
equipment = new Item[0];
|
||||
}
|
||||
|
||||
// If inventory is null, the player has nothing in their inventory
|
||||
if (inventory == null)
|
||||
{
|
||||
inventory = new Item[0];
|
||||
}
|
||||
|
||||
Item[] combined = new Item[equipment.length + inventory.length];
|
||||
System.arraycopy(equipment, 0, combined, 0, equipment.length);
|
||||
System.arraycopy(inventory, 0, combined, equipment.length, inventory.length);
|
||||
|
||||
for (ItemRequirement requirement : requirements)
|
||||
{
|
||||
boolean equipmentFulfilled = requirement.fulfilledBy(equipment);
|
||||
boolean combinedFulfilled = requirement.fulfilledBy(combined);
|
||||
|
||||
components.add(LineComponent.builder()
|
||||
.left(requirement.getCollectiveName(plugin.getClient()))
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.right(combinedFulfilled ? "\u2713" : "\u2717")
|
||||
.rightColor(equipmentFulfilled || (combinedFulfilled && !requireEquipped) ? Color.GREEN : (combinedFulfilled ? Color.ORANGE : Color.RED))
|
||||
.build());
|
||||
}
|
||||
|
||||
return components;
|
||||
}
|
||||
|
||||
public static SkillChallengeClue forText(String text, String rawText)
|
||||
{
|
||||
for (SkillChallengeClue clue : CLUES)
|
||||
{
|
||||
if (rawText.equalsIgnoreCase(clue.returnText))
|
||||
{
|
||||
clue.setChallengeCompleted(true);
|
||||
return clue;
|
||||
}
|
||||
else if (text.equals(clue.rawChallenge))
|
||||
{
|
||||
clue.setChallengeCompleted(false);
|
||||
return clue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNpcs()
|
||||
{
|
||||
return new String[] {type.getName()};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
public interface TextClueScroll
|
||||
{
|
||||
String getText();
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Eadgars Ruse <https://github.com/Eadgars-Ruse>
|
||||
* 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.cluescrolls.clues;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import static net.runelite.api.ItemID.TORN_CLUE_SCROLL_PART_1;
|
||||
import static net.runelite.api.ItemID.TORN_CLUE_SCROLL_PART_2;
|
||||
import static net.runelite.api.ItemID.TORN_CLUE_SCROLL_PART_3;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR;
|
||||
import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
import net.runelite.client.util.Text;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class ThreeStepCrypticClue extends ClueScroll implements TextClueScroll, ObjectClueScroll, NpcClueScroll, LocationsClueScroll
|
||||
{
|
||||
public static ThreeStepCrypticClue forText(String plainText, String text)
|
||||
{
|
||||
final String[] split = text.split("<br>\\s*<br>");
|
||||
final List<Map.Entry<CrypticClue, Boolean>> steps = new ArrayList<>(split.length);
|
||||
|
||||
for (String part : split)
|
||||
{
|
||||
boolean isDone = part.contains("<str>");
|
||||
final String rawText = Text.sanitizeMultilineText(part);
|
||||
|
||||
for (CrypticClue clue : CrypticClue.CLUES)
|
||||
{
|
||||
if (!rawText.equalsIgnoreCase(clue.getText()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
steps.add(new AbstractMap.SimpleEntry<>(clue, isDone));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (steps.isEmpty() || steps.size() < 3)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ThreeStepCrypticClue(steps, plainText);
|
||||
}
|
||||
|
||||
private final List<Map.Entry<CrypticClue, Boolean>> clueSteps;
|
||||
private final String text;
|
||||
|
||||
@Override
|
||||
public void makeOverlayHint(PanelComponent panelComponent, ClueScrollPlugin plugin)
|
||||
{
|
||||
panelComponent.setPreferredSize(new Dimension(200, 0));
|
||||
|
||||
for (int i = 0; i < clueSteps.size(); i++)
|
||||
{
|
||||
final Map.Entry<CrypticClue, Boolean> e = clueSteps.get(i);
|
||||
|
||||
if (!e.getValue())
|
||||
{
|
||||
CrypticClue c = e.getKey();
|
||||
|
||||
panelComponent.getChildren().add(TitleComponent.builder().text("Cryptic Clue #" + (i + 1)).build());
|
||||
panelComponent.getChildren().add(LineComponent.builder().left("Solution:").build());
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(c.getSolution())
|
||||
.leftColor(TITLED_CONTENT_COLOR)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void makeWorldOverlayHint(Graphics2D graphics, ClueScrollPlugin plugin)
|
||||
{
|
||||
for (Map.Entry<CrypticClue, Boolean> e : clueSteps)
|
||||
{
|
||||
if (!e.getValue())
|
||||
{
|
||||
e.getKey().makeWorldOverlayHint(graphics, plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean update(int containerId, final ItemContainer itemContainer)
|
||||
{
|
||||
if (containerId == InventoryID.INVENTORY.getId())
|
||||
{
|
||||
return checkForPart(itemContainer, TORN_CLUE_SCROLL_PART_1, 0) ||
|
||||
checkForPart(itemContainer, TORN_CLUE_SCROLL_PART_2, 1) ||
|
||||
checkForPart(itemContainer, TORN_CLUE_SCROLL_PART_3, 2);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean checkForPart(final ItemContainer itemContainer, int clueScrollPart, int index)
|
||||
{
|
||||
// If we have the part then that step is done
|
||||
if (itemContainer.contains(clueScrollPart))
|
||||
{
|
||||
final Map.Entry<CrypticClue, Boolean> entry = clueSteps.get(index);
|
||||
|
||||
if (!entry.getValue())
|
||||
{
|
||||
entry.setValue(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
for (Map.Entry<CrypticClue, Boolean> clueStep : clueSteps)
|
||||
{
|
||||
clueStep.setValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldPoint getLocation()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldPoint[] getLocations()
|
||||
{
|
||||
return clueSteps.stream()
|
||||
.filter(s -> !s.getValue())
|
||||
.map(s -> s.getKey().getLocation())
|
||||
.toArray(WorldPoint[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getNpcs()
|
||||
{
|
||||
return clueSteps.stream()
|
||||
.filter(s -> !s.getValue())
|
||||
.map(s -> s.getKey().getNpc())
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getObjectIds()
|
||||
{
|
||||
return clueSteps.stream()
|
||||
.filter(s -> !s.getValue())
|
||||
.mapToInt(s -> s.getKey().getObjectId())
|
||||
.toArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues.emote;
|
||||
|
||||
import lombok.Getter;
|
||||
import static net.runelite.api.SpriteID.*;
|
||||
|
||||
@Getter
|
||||
public enum Emote
|
||||
{
|
||||
BULL_ROARER("Bull Roarer", -1),
|
||||
YES("Yes", EMOTE_YES),
|
||||
NO("No", EMOTE_NO),
|
||||
THINK("Think", EMOTE_THINK),
|
||||
BOW("Bow", EMOTE_BOW),
|
||||
ANGRY("Angry", EMOTE_ANGRY),
|
||||
CRY("Cry", EMOTE_CRY),
|
||||
LAUGH("Laugh", EMOTE_LAUGH),
|
||||
CHEER("Cheer", EMOTE_CHEER),
|
||||
WAVE("Wave", EMOTE_WAVE),
|
||||
BECKON("Beckon", EMOTE_BECKON),
|
||||
DANCE("Dance", EMOTE_DANCE),
|
||||
CLAP("Clap", EMOTE_CLAP),
|
||||
PANIC("Panic", EMOTE_PANIC),
|
||||
JIG("Jig", EMOTE_JIG),
|
||||
SPIN("Spin", EMOTE_SPIN),
|
||||
HEADBANG("Headbang", EMOTE_HEADBANG),
|
||||
JUMP_FOR_JOY("Jump for Joy", EMOTE_JUMP_FOR_JOY),
|
||||
RASPBERRY("Raspberry", EMOTE_RASPBERRY),
|
||||
YAWN("Yawn", EMOTE_YAWN),
|
||||
SALUTE("Salute", EMOTE_SALUTE),
|
||||
SHRUG("Shrug", EMOTE_SHRUG),
|
||||
BLOW_KISS("Blow Kiss", EMOTE_BLOW_KISS),
|
||||
GOBLIN_SALUTE("Goblin Salute", EMOTE_GOBLIN_SALUTE),
|
||||
SLAP_HEAD("Slap Head", EMOTE_SLAP_HEAD),
|
||||
STAMP("Stamp", EMOTE_STAMP),
|
||||
FLAP("Flap", EMOTE_FLAP),
|
||||
PUSH_UP("Push up", EMOTE_PUSH_UP);
|
||||
|
||||
private String name;
|
||||
private int spriteId;
|
||||
|
||||
Emote(String name, int spriteId)
|
||||
{
|
||||
this.name = name;
|
||||
this.spriteId = spriteId;
|
||||
}
|
||||
|
||||
public boolean hasSprite()
|
||||
{
|
||||
return spriteId != -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Abex
|
||||
* 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.cluescrolls.clues.emote;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.NullObjectID;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
|
||||
@Getter
|
||||
public enum STASHUnit
|
||||
{
|
||||
NEAR_A_SHED_IN_LUMBRIDGE_SWAMP(NullObjectID.NULL_28958, new WorldPoint(3201, 3171, 0)),
|
||||
ON_THE_BRIDGE_TO_THE_MISTHALIN_WIZARDS_TOWER(NullObjectID.NULL_28959, new WorldPoint(3115, 3194, 0)),
|
||||
DRAYNOR_VILLAGE_MARKET(NullObjectID.NULL_28960, new WorldPoint(3083, 3254, 0)),
|
||||
LIMESTONE_MINE(NullObjectID.NULL_28961, new WorldPoint(3373, 3498, 0)),
|
||||
OUTSIDE_THE_LEGENDS_GUILD_GATES(NullObjectID.NULL_28962, new WorldPoint(2735, 3350, 0)),
|
||||
MUDSKIPPER_POINT(NullObjectID.NULL_28963, new WorldPoint(2988, 3111, 0)),
|
||||
NEAR_THE_ENTRANA_FERRY_IN_PORT_SARIM(NullObjectID.NULL_28964, new WorldPoint(3050, 3237, 0)),
|
||||
AL_KHARID_SCORPION_MINE(NullObjectID.NULL_28965, new WorldPoint(3303, 3289, 0)),
|
||||
DRAYNOR_MANOR_BY_THE_FOUNTAIN(NullObjectID.NULL_28966, new WorldPoint(3089, 3331, 0)),
|
||||
WHEAT_FIELD_NEAR_THE_LUMBRIDGE_WINDMILL(NullObjectID.NULL_28967, new WorldPoint(3163, 3297, 0)),
|
||||
CROSSROADS_NORTH_OF_DRAYNOR_VILLAGE(NullObjectID.NULL_28968, new WorldPoint(3111, 3289, 0)),
|
||||
RIMMINGTON_MINE(NullObjectID.NULL_28969, new WorldPoint(2976, 3239, 0)),
|
||||
VARROCK_PALACE_LIBRARY(NullObjectID.NULL_28970, new WorldPoint(3214, 3490, 0)),
|
||||
UPSTAIRS_IN_THE_ARDOUGNE_WINDMILL(NullObjectID.NULL_28971, new WorldPoint(2635, 3386, 2)),
|
||||
OUTSIDE_THE_FALADOR_PARTY_ROOM(NullObjectID.NULL_28972, new WorldPoint(3043, 3371, 0)),
|
||||
TAVERLEY_STONE_CIRCLE(NullObjectID.NULL_28973, new WorldPoint(2924, 3477, 0)),
|
||||
CATHERBY_BEEHIVE_FIELD(NullObjectID.NULL_28974, new WorldPoint(2764, 3438, 0)),
|
||||
NEAR_THE_PARROTS_IN_ARDOUGNE_ZOO(NullObjectID.NULL_28975, new WorldPoint(2608, 3284, 0)),
|
||||
ROAD_JUNCTION_NORTH_OF_RIMMINGTON(NullObjectID.NULL_28976, new WorldPoint(2981, 3278, 0)),
|
||||
OUTSIDE_THE_FISHING_GUILD(NullObjectID.NULL_28977, new WorldPoint(2608, 3393, 0)),
|
||||
OUTSIDE_KEEP_LE_FAYE(NullObjectID.NULL_28978, new WorldPoint(2756, 3399, 0)),
|
||||
ROAD_JUNCTION_SOUTH_OF_SINCLAIR_MANSION(NullObjectID.NULL_28979, new WorldPoint(2735, 3534, 0)),
|
||||
OUTSIDE_THE_DIGSITE_EXAM_CENTRE(NullObjectID.NULL_28980, new WorldPoint(3353, 3343, 0)),
|
||||
NEAR_THE_SAWMILL_OPERATORS_BOOTH(NullObjectID.NULL_28981, new WorldPoint(3298, 3490, 0)),
|
||||
MUBARIZS_ROOM_AT_THE_DUEL_ARENA(NullObjectID.NULL_28982, new WorldPoint(3316, 3242, 0)),
|
||||
OUTSIDE_VARROCK_PALACE_COURTYARD(NullObjectID.NULL_28983, new WorldPoint(3211, 3456, 0)),
|
||||
NEAR_HERQUINS_SHOP_IN_FALADOR(NullObjectID.NULL_28984, new WorldPoint(2941, 3339, 0)),
|
||||
SOUTH_OF_THE_GRAND_EXCHANGE(NullObjectID.NULL_28985, new WorldPoint(3159, 3464, 0)),
|
||||
AUBURYS_SHOP_IN_VARROCK(NullObjectID.NULL_28986, new WorldPoint(3252, 3404, 0)),
|
||||
CENTRE_OF_CANIFIS(NullObjectID.NULL_28987, new WorldPoint(3491, 3489, 0)),
|
||||
MAUSOLEUM_OFF_THE_MORYTANIA_COAST(NullObjectID.NULL_28988, new WorldPoint(3500, 3575, 0)),
|
||||
EAST_OF_THE_BARBARIAN_VILLAGE_BRIDGE(NullObjectID.NULL_28989, new WorldPoint(3110, 3422, 0)),
|
||||
SOUTH_OF_THE_SHRINE_IN_TAI_BWO_WANNAI_VILLAGE(NullObjectID.NULL_28990, new WorldPoint(2802, 3081, 0)),
|
||||
CASTLE_WARS_BANK(NullObjectID.NULL_28991, new WorldPoint(2444, 3093, 0)),
|
||||
BARBARIAN_OUTPOST_OBSTACLE_COURSE(NullObjectID.NULL_28992, new WorldPoint(2541, 3550, 0)),
|
||||
GNOME_STRONGHOLD_BALANCING_ROPE(NullObjectID.NULL_28993, new WorldPoint(2473, 3418, 2)),
|
||||
OUTSIDE_YANILLE_BANK(NullObjectID.NULL_28994, new WorldPoint(2603, 3091, 0)),
|
||||
OBSERVATORY(NullObjectID.NULL_28995, new WorldPoint(2439, 3166, 0)),
|
||||
OGRE_CAGE_IN_KING_LATHAS_TRAINING_CAMP(NullObjectID.NULL_28996, new WorldPoint(2533, 3377, 0)),
|
||||
DIGSITE(NullObjectID.NULL_28997, new WorldPoint(3370, 3420, 0)),
|
||||
HICKTONS_ARCHERY_EMPORIUM(NullObjectID.NULL_28998, new WorldPoint(2825, 3441, 0)),
|
||||
SHANTAY_PASS(NullObjectID.NULL_28999, new WorldPoint(3308, 3125, 0)),
|
||||
LUMBRIDGE_SWAMP_CAVES(NullObjectID.NULL_29000, new WorldPoint(3222, 9584, 0), new WorldPoint(3167, 9570, 0)),
|
||||
OUTSIDE_CATHERBY_BANK(NullObjectID.NULL_29001, new WorldPoint(2807, 3437, 0)),
|
||||
OUTSIDE_THE_SEERS_VILLAGE_COURTHOUSE(NullObjectID.NULL_29002, new WorldPoint(2731, 3475, 0)),
|
||||
OUTSIDE_HARRYS_FISHING_SHOP_IN_CATHERBY(NullObjectID.NULL_29003, new WorldPoint(2837, 3436, 0)),
|
||||
TZHAAR_WEAPONS_STORE(NullObjectID.NULL_29004, new WorldPoint(2479, 5146, 0)),
|
||||
NORTH_OF_EVIL_DAVES_HOUSE_IN_EDGEVILLE(NullObjectID.NULL_29005, new WorldPoint(3077, 3503, 0)),
|
||||
WEST_OF_THE_SHAYZIEN_COMBAT_RING(NullObjectID.NULL_29006, new WorldPoint(1534, 3591, 0)),
|
||||
ENTRANCE_OF_THE_ARCEUUS_LIBRARY(NullObjectID.NULL_29007, new WorldPoint(1642, 3809, 0)),
|
||||
OUTSIDE_DRAYNOR_VILLAGE_JAIL(NullObjectID.NULL_29008, new WorldPoint(3130, 3250, 0)),
|
||||
CHAOS_TEMPLE_IN_THE_SOUTHEASTERN_WILDERNESS(NullObjectID.NULL_29009, new WorldPoint(3245, 3609, 0)),
|
||||
FISHING_GUILD_BANK(NullObjectID.NULL_29010, new WorldPoint(2593, 3409, 0)),
|
||||
TOP_FLOOR_OF_THE_LIGHTHOUSE(NullObjectID.NULL_29011, new WorldPoint(2512, 3640, 2)),
|
||||
OUTSIDE_THE_GREAT_PYRAMID_OF_SOPHANEM(NullObjectID.NULL_29012, new WorldPoint(3291, 2780, 0)),
|
||||
NOTERAZZOS_SHOP_IN_THE_WILDERNESS(NullObjectID.NULL_29013, new WorldPoint(3027, 3699, 0)),
|
||||
WEST_SIDE_OF_THE_KARAMJA_BANANA_PLANTATION(NullObjectID.NULL_29014, new WorldPoint(2909, 3169, 0)),
|
||||
MOUNTAIN_CAMP_GOAT_ENCLOSURE(NullObjectID.NULL_29015, new WorldPoint(2810, 3677, 0)),
|
||||
GNOME_GLIDER_ON_WHITE_WOLF_MOUNTAIN(NullObjectID.NULL_29016, new WorldPoint(2849, 3496, 0)),
|
||||
SHILO_VILLAGE_BANK(NullObjectID.NULL_29017, new WorldPoint(2853, 2952, 0)),
|
||||
INSIDE_THE_DIGSITE_EXAM_CENTRE(NullObjectID.NULL_29018, new WorldPoint(3356, 3333, 0)),
|
||||
NORTHEAST_CORNER_OF_THE_KHARAZI_JUNGLE(NullObjectID.NULL_29019, new WorldPoint(2952, 2932, 0)),
|
||||
VOLCANO_IN_THE_NORTHEASTERN_WILDERNESS(NullObjectID.NULL_29020, new WorldPoint(3368, 3930, 0)),
|
||||
IN_THE_MIDDLE_OF_JIGGIG(NullObjectID.NULL_29021, new WorldPoint(2478, 3048, 0)),
|
||||
AGILITY_PYRAMID(NullObjectID.NULL_29022, new WorldPoint(3357, 2830, 0)),
|
||||
HOSIDIUS_MESS(NullObjectID.NULL_29023, new WorldPoint(1646, 3632, 0)),
|
||||
CHAPEL_IN_WEST_ARDOUGNE(NullObjectID.NULL_29024, new WorldPoint(2527, 3294, 0)),
|
||||
NEAR_A_RUNITE_ROCK_IN_THE_FREMENNIK_ISLES(NullObjectID.NULL_29025, new WorldPoint(2374, 3847, 0)),
|
||||
NEAR_A_LADDER_IN_THE_WILDERNESS_LAVA_MAZE(NullObjectID.NULL_29026, new WorldPoint(3069, 3862, 0)),
|
||||
ENTRANCE_OF_THE_CAVE_OF_DAMIS(NullObjectID.NULL_29027, new WorldPoint(2629, 5070, 0)),
|
||||
WARRIORS_GUILD_BANK(NullObjectID.NULL_29028, new WorldPoint(2844, 3537, 0)),
|
||||
SOUTHEAST_CORNER_OF_THE_MONASTERY(NullObjectID.NULL_29029, new WorldPoint(3056, 3482, 0)),
|
||||
SOUTHEAST_CORNER_OF_THE_FISHING_PLATFORM(NullObjectID.NULL_29030, new WorldPoint(2787, 3277, 0)),
|
||||
OUTSIDE_THE_SLAYER_TOWER_GARGOYLE_ROOM(NullObjectID.NULL_29031, new WorldPoint(3423, 3534, 2)),
|
||||
ON_TOP_OF_TROLLHEIM_MOUNTAIN(NullObjectID.NULL_29032, new WorldPoint(2886, 3676, 0)),
|
||||
FOUNTAIN_OF_HEROES(NullObjectID.NULL_29033, new WorldPoint(2916, 9891, 0)),
|
||||
ENTRANCE_OF_THE_CAVERN_UNDER_THE_WHIRLPOOL(NullObjectID.NULL_29034, new WorldPoint(1764, 5367, 1), new WorldPoint(1636, 5367, 1)),
|
||||
HALFWAY_DOWN_TROLLWEISS_MOUNTAIN(NullObjectID.NULL_29035, new WorldPoint(2782, 3787, 0)),
|
||||
SHAYZIEN_WAR_TENT(NullObjectID.NULL_29036, new WorldPoint(1550, 3541, 0)),
|
||||
OUTSIDE_THE_LEGENDS_GUILD_DOOR(NullObjectID.NULL_29037, new WorldPoint(2727, 3371, 0)),
|
||||
NEAR_THE_GEM_STALL_IN_ARDOUGNE_MARKET(NullObjectID.NULL_29038, new WorldPoint(2672, 3302, 0)),
|
||||
OUTSIDE_THE_BAR_BY_THE_FIGHT_ARENA(NullObjectID.NULL_29039, new WorldPoint(2571, 3150, 0)),
|
||||
SOUTHEAST_CORNER_OF_LAVA_DRAGON_ISLE(NullObjectID.NULL_29040, new WorldPoint(3228, 3830, 0)),
|
||||
NEAR_THE_PIER_IN_ZULANDRA(NullObjectID.NULL_29041, new WorldPoint(2203, 3059, 0)),
|
||||
BARROWS_CHEST(NullObjectID.NULL_29042, new WorldPoint(3547, 9690, 0)),
|
||||
WELL_OF_VOYAGE(NullObjectID.NULL_29043, new WorldPoint(2006, 4709, 1)),
|
||||
NORTHERN_WALL_OF_CASTLE_DRAKAN(NullObjectID.NULL_29044, new WorldPoint(3563, 3379, 0)),
|
||||
_7TH_CHAMBER_OF_JALSAVRAH(NullObjectID.NULL_29045, new WorldPoint(1951, 4431, 0)),
|
||||
SOUL_ALTAR(NullObjectID.NULL_29046, new WorldPoint(1810, 3855, 0)),
|
||||
WARRIORS_GUILD_BANK_29047(NullObjectID.NULL_29047, new WorldPoint(2845, 3545, 0)),
|
||||
ENTRANA_CHAPEL(NullObjectID.NULL_29048, new WorldPoint(2851, 3355, 0)),
|
||||
TZHAAR_GEM_STORE(NullObjectID.NULL_29049, new WorldPoint(2466, 5150, 0)),
|
||||
TENT_IN_LORD_IORWERTHS_ENCAMPMENT(NullObjectID.NULL_29050, new WorldPoint(2198, 3257, 0)),
|
||||
OUTSIDE_MUDKNUCKLES_HUT(NullObjectID.NULL_29051, new WorldPoint(2959, 3502, 0)),
|
||||
CENTRE_OF_THE_CATACOMBS_OF_KOUREND(NullObjectID.NULL_29052, new WorldPoint(1661, 10045, 0)),
|
||||
KING_BLACK_DRAGONS_LAIR(NullObjectID.NULL_29053, new WorldPoint(2286, 4680, 0)),
|
||||
OUTSIDE_KRIL_TSUTSAROTHS_ROOM(NullObjectID.NULL_29054, new WorldPoint(2931, 5337, 2)),
|
||||
BY_THE_BEAR_CAGE_IN_VARROCK_PALACE_GARDENS(NullObjectID.NULL_29055, new WorldPoint(3232, 3494, 0)),
|
||||
OUTSIDE_THE_WILDERNESS_AXE_HUT(NullObjectID.NULL_29056, new WorldPoint(3186, 3958, 0)),
|
||||
TOP_FLOOR_OF_THE_YANILLE_WATCHTOWER(NullObjectID.NULL_29057, new WorldPoint(2930, 4718, 2)),
|
||||
DEATH_ALTAR(NullObjectID.NULL_29058, new WorldPoint(2210, 4842, 0)),
|
||||
BEHIND_MISS_SCHISM_IN_DRAYNOR_VILLAGE(NullObjectID.NULL_29059, new WorldPoint(3095, 3254, 0)),
|
||||
NORTHWESTERN_CORNER_OF_THE_ENCHANTED_VALLEY(NullObjectID.NULL_29060, new WorldPoint(3022, 4517, 0)),
|
||||
NORTH_OF_MOUNT_KARUULM(NullObjectID.NULL_34647, new WorldPoint(1308, 3840, 0)),
|
||||
GYPSY_TENT_ENTRANCE(NullObjectID.NULL_34736, new WorldPoint(3206, 3422, 0)),
|
||||
FINE_CLOTHES_ENTRANCE(NullObjectID.NULL_34737, new WorldPoint(3209, 3416, 0)),
|
||||
BOB_AXES_ENTRANCE(NullObjectID.NULL_34738, new WorldPoint(3233, 3200, 0)),
|
||||
CRYSTALLINE_MAPLE_TREES(NullObjectID.NULL_34953, new WorldPoint(2213, 3427, 0));
|
||||
|
||||
private final int objectId;
|
||||
private final WorldPoint[] worldPoints;
|
||||
|
||||
STASHUnit(int objectId, WorldPoint... worldPoints)
|
||||
{
|
||||
this.objectId = objectId;
|
||||
this.worldPoints = worldPoints;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.plugins.cluescrolls.clues.hotcold;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum HotColdArea
|
||||
{
|
||||
ASGARNIA("Asgarnia"),
|
||||
DESERT("Desert"),
|
||||
FELDIP_HILLS("Feldip Hills"),
|
||||
FREMENNIK_PROVINCE("Fremennik Province"),
|
||||
KANDARIN("Kandarin"),
|
||||
KARAMJA("Karamja"),
|
||||
MISTHALIN("Misthalin"),
|
||||
MORYTANIA("Morytania"),
|
||||
WESTERN_PROVINCE("Western Province"),
|
||||
WILDERNESS("Wilderness"),
|
||||
ZEAH("Zeah");
|
||||
|
||||
private final String name;
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Eadgars Ruse <https://github.com/Eadgars-Ruse>
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* Copyright (c) 2019, Jordan Atwood <nightfirecat@protonmail.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.cluescrolls.clues.hotcold;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.client.plugins.cluescrolls.clues.Enemy;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.Enemy.*;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.ASGARNIA;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.DESERT;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.FELDIP_HILLS;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.FREMENNIK_PROVINCE;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.KANDARIN;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.KARAMJA;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.MISTHALIN;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.MORYTANIA;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.WESTERN_PROVINCE;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.WILDERNESS;
|
||||
import static net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdArea.ZEAH;
|
||||
|
||||
// The locations contains all hot/cold points and their descriptions according to the wiki
|
||||
// these central points were obtained by checking wiki location pictures against a coordinate map
|
||||
// some central points points may be slightly off-center
|
||||
// calculations are done considering the 9x9 grid around the central point where the strange device shakes
|
||||
// because the calculations consider the 9x9 grid, slightly off-center points should still be found by the calculations
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum HotColdLocation
|
||||
{
|
||||
ASGARNIA_WARRIORS(new WorldPoint(2860, 3562, 0), ASGARNIA, "North of the Warriors' Guild in Burthorpe.", BRASSICAN_MAGE),
|
||||
ASGARNIA_JATIX(new WorldPoint(2915, 3425, 0), ASGARNIA, "East of Jatix's Herblore Shop in Taverley.", BRASSICAN_MAGE),
|
||||
ASGARNIA_BARB(new WorldPoint(3033, 3438, 0), ASGARNIA, "West of Barbarian Village.", BRASSICAN_MAGE),
|
||||
ASGARNIA_MIAZRQA(new WorldPoint(2972, 3486, 0), ASGARNIA, "North of Miazrqa's tower, outside Goblin Village.", BRASSICAN_MAGE),
|
||||
ASGARNIA_COW(new WorldPoint(3031, 3304, 0), ASGARNIA, "In the cow pen north of Sarah's Farming Shop.", ANCIENT_WIZARDS),
|
||||
ASGARNIA_PARTY_ROOM(new WorldPoint(3030, 3364, 0), ASGARNIA, "Outside the Falador Party Room.", BRASSICAN_MAGE),
|
||||
ASGARNIA_CRAFT_GUILD(new WorldPoint(2917, 3295, 0), ASGARNIA, "Outside the Crafting Guild cow pen.", BRASSICAN_MAGE),
|
||||
ASGARNIA_RIMMINGTON(new WorldPoint(2976, 3239, 0), ASGARNIA, "In the centre of the Rimmington mine.", BRASSICAN_MAGE),
|
||||
ASGARNIA_MUDSKIPPER(new WorldPoint(2987, 3110, 0), ASGARNIA, "Mudskipper Point, near the starfish in the south-west corner.", BRASSICAN_MAGE),
|
||||
ASGARNIA_TROLL(new WorldPoint(2910, 3616, 0), ASGARNIA, "The Troll arena, where the player fights Dad during the Troll Stronghold quest. Bring climbing boots if travelling from Burthorpe.", BRASSICAN_MAGE),
|
||||
DESERT_GENIE(new WorldPoint(3359, 2912, 0), DESERT, "West of Nardah genie cave.", BRASSICAN_MAGE),
|
||||
DESERT_ALKHARID_MINE(new WorldPoint(3279, 3263, 0), DESERT, "West of Al Kharid mine.", BRASSICAN_MAGE),
|
||||
DESERT_MENAPHOS_GATE(new WorldPoint(3223, 2820, 0), DESERT, "North of Menaphos gate.", BRASSICAN_MAGE),
|
||||
DESERT_BEDABIN_CAMP(new WorldPoint(3161, 3047, 0), DESERT, "Bedabin Camp, near the north tent.", BRASSICAN_MAGE),
|
||||
DESERT_UZER(new WorldPoint(3432, 3105, 0), DESERT, "West of Uzer.", BRASSICAN_MAGE),
|
||||
DESERT_POLLNIVNEACH(new WorldPoint(3288, 2976, 0), DESERT, "West of Pollnivneach.", BRASSICAN_MAGE),
|
||||
DESERT_MTA(new WorldPoint(3347, 3295, 0), DESERT, "Next to Mage Training Arena.", BRASSICAN_MAGE),
|
||||
DESERT_SHANTY(new WorldPoint(3292, 3107, 0), DESERT, "South-west of Shantay Pass.", BRASSICAN_MAGE),
|
||||
DRAYNOR_MANOR_MUSHROOMS(new WorldPoint(3096, 3379, 0), MISTHALIN, "Patch of mushrooms just northwest of Draynor Manor"),
|
||||
DRAYNOR_WHEAT_FIELD(new WorldPoint(3120, 3282, 0), MISTHALIN, "Inside the wheat field next to Draynor Village"),
|
||||
FELDIP_HILLS_JIGGIG(new WorldPoint(2409, 3053, 0), FELDIP_HILLS, "West of Jiggig, east of the fairy ring bkp.", BRASSICAN_MAGE),
|
||||
FELDIP_HILLS_SW(new WorldPoint(2586, 2897, 0), FELDIP_HILLS, "West of the southeasternmost lake in Feldip Hills.", BRASSICAN_MAGE),
|
||||
FELDIP_HILLS_GNOME_GLITER(new WorldPoint(2555, 2972, 0), FELDIP_HILLS, "East of the gnome glider (Lemantolly Undri).", BRASSICAN_MAGE),
|
||||
FELDIP_HILLS_RANTZ(new WorldPoint(2611, 2950, 0), FELDIP_HILLS, "South of Rantz, west of the empty glass bottles.", BRASSICAN_MAGE),
|
||||
FELDIP_HILLS_SOUTH(new WorldPoint(2486, 3007, 0), FELDIP_HILLS, "South of Jiggig.", BRASSICAN_MAGE),
|
||||
FELDIP_HILLS_RED_CHIN(new WorldPoint(2530, 2901, 0), FELDIP_HILLS, "Outside the red chinchompa hunting ground entrance, south of the Hunting expert's hut.", BRASSICAN_MAGE),
|
||||
FELDIP_HILLS_SE(new WorldPoint(2569, 2918, 0), FELDIP_HILLS, "South-east of the ∩-shaped lake, near the Hunter icon.", BRASSICAN_MAGE),
|
||||
FELDIP_HILLS_CW_BALLOON(new WorldPoint(2451, 3112, 0), FELDIP_HILLS, "Directly west of the Castle Wars balloon.", BRASSICAN_MAGE),
|
||||
FREMENNIK_PROVINCE_MTN_CAMP(new WorldPoint(2800, 3669, 0), FREMENNIK_PROVINCE, "At the Mountain Camp.", BRASSICAN_MAGE),
|
||||
FREMENNIK_PROVINCE_RELLEKKA_HUNTER(new WorldPoint(2720, 3784, 0), FREMENNIK_PROVINCE, "At the Rellekka Hunter area, near the Hunter icon.", BRASSICAN_MAGE),
|
||||
FREMENNIK_PROVINCE_KELGADRIM_ENTRANCE(new WorldPoint(2711, 3689, 0), FREMENNIK_PROVINCE, "West of the Keldagrim entrance mine.", BRASSICAN_MAGE),
|
||||
FREMENNIK_PROVINCE_SW(new WorldPoint(2604, 3648, 0), FREMENNIK_PROVINCE, "Outside the fence in the south-western corner of Rellekka.", BRASSICAN_MAGE),
|
||||
FREMENNIK_PROVINCE_LIGHTHOUSE(new WorldPoint(2585, 3601, 0), FREMENNIK_PROVINCE, "South-east of the Lighthouse.", BRASSICAN_MAGE),
|
||||
FREMENNIK_PROVINCE_ETCETERIA_CASTLE(new WorldPoint(2617, 3862, 0), FREMENNIK_PROVINCE, "South-east of Etceteria's castle.", BRASSICAN_MAGE),
|
||||
FREMENNIK_PROVINCE_MISC_COURTYARD(new WorldPoint(2527, 3868, 0), FREMENNIK_PROVINCE, "Outside Miscellania's courtyard.", BRASSICAN_MAGE),
|
||||
FREMENNIK_PROVINCE_FREMMY_ISLES_MINE(new WorldPoint(2374, 3850, 0), FREMENNIK_PROVINCE, "Central Fremennik Isles mine.", ANCIENT_WIZARDS),
|
||||
FREMENNIK_PROVINCE_WEST_ISLES_MINE(new WorldPoint(2313, 3850, 0), FREMENNIK_PROVINCE, "West Fremennik Isles mine.", ANCIENT_WIZARDS),
|
||||
FREMENNIK_PROVINCE_WEST_JATIZSO_ENTRANCE(new WorldPoint(2393, 3812, 0), FREMENNIK_PROVINCE, "West of the Jatizso mine entrance.", BRASSICAN_MAGE),
|
||||
FREMENNIK_PROVINCE_PIRATES_COVE(new WorldPoint(2210, 3814, 0), FREMENNIK_PROVINCE, "Pirates' Cove", ANCIENT_WIZARDS),
|
||||
FREMENNIK_PROVINCE_ASTRAL_ALTER(new WorldPoint(2149, 3865, 0), FREMENNIK_PROVINCE, "Astral altar", ANCIENT_WIZARDS),
|
||||
FREMENNIK_PROVINCE_LUNAR_VILLAGE(new WorldPoint(2084, 3916, 0), FREMENNIK_PROVINCE, "Lunar Isle, inside the village.", ANCIENT_WIZARDS),
|
||||
FREMENNIK_PROVINCE_LUNAR_NORTH(new WorldPoint(2106, 3949, 0), FREMENNIK_PROVINCE, "Lunar Isle, north of the village.", ANCIENT_WIZARDS),
|
||||
ICE_MOUNTAIN(new WorldPoint(3007, 3475, 0), MISTHALIN, "Atop Ice Mountain"),
|
||||
KANDARIN_SINCLAR_MANSION(new WorldPoint(2730, 3588, 0), KANDARIN, "North-west of the Sinclair Mansion, near the log balance shortcut.", BRASSICAN_MAGE),
|
||||
KANDARIN_CATHERBY(new WorldPoint(2774, 3436, 0), KANDARIN, "Catherby, between the bank and the beehives, near small rock formation.", BRASSICAN_MAGE),
|
||||
KANDARIN_GRAND_TREE(new WorldPoint(2448, 3503, 0), KANDARIN, "Grand Tree, just east of the terrorchick gnome enclosure.", BRASSICAN_MAGE),
|
||||
KANDARIN_SEERS(new WorldPoint(2735, 3486, 0), KANDARIN, "Between the Seers' Village bank and Camelot.", BRASSICAN_MAGE),
|
||||
KANDARIN_MCGRUBORS_WOOD(new WorldPoint(2653, 3485, 0), KANDARIN, "McGrubor's Wood", BRASSICAN_MAGE),
|
||||
KANDARIN_FISHING_BUILD(new WorldPoint(2590, 3369, 0), KANDARIN, "South of Fishing Guild", BRASSICAN_MAGE),
|
||||
KANDARIN_WITCHHAVEN(new WorldPoint(2707, 3306, 0), KANDARIN, "Outside Witchaven, west of Jeb, Holgart, and Caroline.", BRASSICAN_MAGE),
|
||||
KANDARIN_NECRO_TOWER(new WorldPoint(2667, 3241, 0), KANDARIN, "Ground floor inside the Necromancer Tower. Easily accessed by using fairy ring code djp.", ANCIENT_WIZARDS),
|
||||
KANDARIN_FIGHT_ARENA(new WorldPoint(2587, 3135, 0), KANDARIN, "South of the Fight Arena, north-west of the Nightmare Zone.", BRASSICAN_MAGE),
|
||||
KANDARIN_TREE_GNOME_VILLAGE(new WorldPoint(2530, 3164, 0), KANDARIN, "Tree Gnome Village, near the general store icon.", BRASSICAN_MAGE),
|
||||
KANDARIN_GRAVE_OF_SCORPIUS(new WorldPoint(2467, 3227, 0), KANDARIN, "Grave of Scorpius", BRASSICAN_MAGE),
|
||||
KANDARIN_KHAZARD_BATTLEFIELD(new WorldPoint(2522, 3252, 0), KANDARIN, "Khazard Battlefield, south of Tracker gnome 2.", BRASSICAN_MAGE),
|
||||
KANDARIN_WEST_ARDY(new WorldPoint(2535, 3322, 0), KANDARIN, "West Ardougne, near the staircase outside the Civic Office.", BRASSICAN_MAGE),
|
||||
KANDARIN_SW_TREE_GNOME_STRONGHOLD(new WorldPoint(2411, 3429, 0), KANDARIN, "South-west Tree Gnome Stronghold", BRASSICAN_MAGE),
|
||||
KANDARIN_OUTPOST(new WorldPoint(2457, 3362, 0), KANDARIN, "South of the Tree Gnome Stronghold, north-east of the Outpost.", BRASSICAN_MAGE),
|
||||
KANDARIN_BAXTORIAN_FALLS(new WorldPoint(2530, 3477, 0), KANDARIN, "South-east of Almera's house on Baxtorian Falls.", BRASSICAN_MAGE),
|
||||
KANDARIN_BA_AGILITY_COURSE(new WorldPoint(2540, 3548, 0), KANDARIN, "Inside the Barbarian Agility Course. Completion of Alfred Grimhand's Barcrawl is required.", BRASSICAN_MAGE),
|
||||
KARAMJA_MUSA_POINT(new WorldPoint(2913, 3169, 0), KARAMJA, "Musa Point, banana plantation.", BRASSICAN_MAGE),
|
||||
KARAMJA_BRIMHAVEN_FRUIT_TREE(new WorldPoint(2782, 3215, 0), KARAMJA, "Brimhaven, east of the fruit tree patch.", BRASSICAN_MAGE),
|
||||
KARAMJA_WEST_BRIMHAVEN(new WorldPoint(2718, 3167, 0), KARAMJA, "West of Brimhaven.", BRASSICAN_MAGE),
|
||||
KARAMJA_GLIDER(new WorldPoint(2966, 2975, 0), KARAMJA, "West of the gnome glider.", BRASSICAN_MAGE),
|
||||
KARAMJA_KHARAZI_NE(new WorldPoint(2904, 2925, 0), KARAMJA, "North-eastern part of Kharazi Jungle.", BRASSICAN_MAGE),
|
||||
KARAMJA_KHARAZI_SW(new WorldPoint(2786, 2899, 0), KARAMJA, "South-western part of Kharazi Jungle.", BRASSICAN_MAGE),
|
||||
KARAMJA_CRASH_ISLAND(new WorldPoint(2909, 2737, 0), KARAMJA, "Northern part of Crash Island.", BRASSICAN_MAGE),
|
||||
LUMBRIDGE_COW_FIELD( new WorldPoint(3174, 3336, 0), MISTHALIN, "Cow field north of Lumbridge"),
|
||||
MISTHALIN_VARROCK_STONE_CIRCLE(new WorldPoint(3225, 3356, 0), MISTHALIN, "South of the stone circle near Varrock's entrance.", BRASSICAN_MAGE),
|
||||
MISTHALIN_LUMBRIDGE(new WorldPoint(3234, 3169, 0), MISTHALIN, "Just north-west of the Lumbridge Fishing tutor.", BRASSICAN_MAGE),
|
||||
MISTHALIN_LUMBRIDGE_2(new WorldPoint(3169, 3279, 0), MISTHALIN, "North of the pond between Lumbridge and Draynor Village.", BRASSICAN_MAGE),
|
||||
MISTHALIN_GERTUDES(new WorldPoint(3154, 3421, 0), MISTHALIN, "North-east of Gertrude's house west of Varrock.", BRASSICAN_MAGE),
|
||||
MISTHALIN_DRAYNOR_BANK(new WorldPoint(3098, 3234, 0), MISTHALIN, "South of Draynor Village bank.", BRASSICAN_MAGE),
|
||||
MISTHALIN_LUMBER_YARD(new WorldPoint(3301, 3484, 0), MISTHALIN, "South of Lumber Yard, east of Assistant Serf.", BRASSICAN_MAGE),
|
||||
MORYTANIA_BURGH_DE_ROTT(new WorldPoint(3546, 3252, 0), MORYTANIA, "In the north-east area of Burgh de Rott, by the reverse-L-shaped ruins.", BRASSICAN_MAGE),
|
||||
MORYTANIA_DARKMEYER(new WorldPoint(3604, 3326, 0), MORYTANIA, "Southwestern part of Darkmeyer.", BRASSICAN_MAGE),
|
||||
MORYTANIA_PORT_PHASMATYS(new WorldPoint(3611, 3485, 0), MORYTANIA, "West of Port Phasmatys, south-east of fairy ring.", BRASSICAN_MAGE),
|
||||
MORYTANIA_HOLLOWS(new WorldPoint(3499, 3421, 0), MORYTANIA, "Inside The Hollows, south of the bridge which was repaired in a quest.", BRASSICAN_MAGE),
|
||||
MORYTANIA_SWAMP(new WorldPoint(3418, 3372, 0), MORYTANIA, "Inside the Mort Myre Swamp, north-west of the Nature Grotto.", BRASSICAN_MAGE),
|
||||
MORYTANIA_HAUNTED_MINE(new WorldPoint(3444, 3255, 0), MORYTANIA, "At Haunted Mine quest start.", BRASSICAN_MAGE),
|
||||
MORYTANIA_MAUSOLEUM(new WorldPoint(3499, 3539, 0), MORYTANIA, "South of the Mausoleum.", BRASSICAN_MAGE),
|
||||
MORYTANIA_MOS_LES_HARMLESS(new WorldPoint(3740, 3041, 0), MORYTANIA, "Northern area of Mos Le'Harmless, between the lakes.", BRASSICAN_MAGE),
|
||||
MORYTANIA_MOS_LES_HARMLESS_BAR(new WorldPoint(3666, 2972, 0), MORYTANIA, "Near Mos Le'Harmless southern bar.", BRASSICAN_MAGE),
|
||||
MORYTANIA_DRAGONTOOTH_NORTH(new WorldPoint(3811, 3569, 0), MORYTANIA, "Northern part of Dragontooth Island.", BRASSICAN_MAGE),
|
||||
MORYTANIA_DRAGONTOOTH_SOUTH(new WorldPoint(3803, 3532, 0), MORYTANIA, "Southern part of Dragontooth Island.", BRASSICAN_MAGE),
|
||||
MORYTANIA_SLEPE_TENTS(new WorldPoint(3769, 3383, 0), MORYTANIA, "North-east of Slepe, near the tents.", BRASSICAN_MAGE),
|
||||
NORTHEAST_OF_AL_KHARID_MINE(new WorldPoint(3332, 3313, 0), MISTHALIN, "Northeast of Al Kharid Mine"),
|
||||
WESTERN_PROVINCE_EAGLES_PEAK(new WorldPoint(2297, 3529, 0), WESTERN_PROVINCE, "North-west of Eagles' Peak.", BRASSICAN_MAGE),
|
||||
WESTERN_PROVINCE_PISCATORIS(new WorldPoint(2334, 3685, 0), WESTERN_PROVINCE, "Piscatoris Fishing Colony", ANCIENT_WIZARDS),
|
||||
WESTERN_PROVINCE_PISCATORIS_HUNTER_AREA(new WorldPoint(2359, 3564, 0), WESTERN_PROVINCE, "Eastern part of Piscatoris Hunter area, south-west of the Falconry.", BRASSICAN_MAGE),
|
||||
WESTERN_PROVINCE_ARANDAR(new WorldPoint(2370, 3319, 0), WESTERN_PROVINCE, "South-west of the crystal gate to Arandar.", ANCIENT_WIZARDS),
|
||||
WESTERN_PROVINCE_ELF_CAMP_EAST(new WorldPoint(2268, 3242, 0), WESTERN_PROVINCE, "East of Iorwerth Camp.", BRASSICAN_MAGE),
|
||||
WESTERN_PROVINCE_ELF_CAMP_NW(new WorldPoint(2174, 3280, 0), WESTERN_PROVINCE, "North-west of Iorwerth Camp.", BRASSICAN_MAGE),
|
||||
WESTERN_PROVINCE_LLETYA(new WorldPoint(2337, 3166, 0), WESTERN_PROVINCE, "In Lletya.", BRASSICAN_MAGE),
|
||||
WESTERN_PROVINCE_TYRAS(new WorldPoint(2206, 3158, 0), WESTERN_PROVINCE, "Near Tyras Camp.", BRASSICAN_MAGE),
|
||||
WESTERN_PROVINCE_ZULANDRA(new WorldPoint(2196, 3057, 0), WESTERN_PROVINCE, "The northern house at Zul-Andra.", BRASSICAN_MAGE),
|
||||
WILDERNESS_5(new WorldPoint(3173, 3556, 0), WILDERNESS, "North of the Grand Exchange, level 5 Wilderness.", ANCIENT_WIZARDS),
|
||||
WILDERNESS_12(new WorldPoint(3036, 3612, 0), WILDERNESS, "South-east of the Dark Warriors' Fortress, level 12 Wilderness.", ANCIENT_WIZARDS),
|
||||
WILDERNESS_20(new WorldPoint(3222, 3679, 0), WILDERNESS, "East of the Corporeal Beast's lair, level 20 Wilderness.", ANCIENT_WIZARDS),
|
||||
WILDERNESS_27(new WorldPoint(3174, 3736, 0), WILDERNESS, "Inside the Ruins north of the Graveyard of Shadows, level 27 Wilderness.", BRASSICAN_MAGE),
|
||||
WILDERNESS_28(new WorldPoint(3377, 3737, 0), WILDERNESS, "East of Venenatis' nest, level 28 Wilderness.", BRASSICAN_MAGE),
|
||||
WILDERNESS_32(new WorldPoint(3311, 3773, 0), WILDERNESS, "North of Venenatis' nest, level 32 Wilderness.", ANCIENT_WIZARDS),
|
||||
WILDERNESS_35(new WorldPoint(3152, 3796, 0), WILDERNESS, "East of the Wilderness canoe exit, level 35 Wilderness.", BRASSICAN_OR_WIZARDS),
|
||||
WILDERNESS_37(new WorldPoint(2974, 3814, 0), WILDERNESS, "South-east of the Chaos Temple, level 37 Wilderness.", BRASSICAN_MAGE),
|
||||
WILDERNESS_38(new WorldPoint(3293, 3813, 0), WILDERNESS, "South of Callisto, level 38 Wilderness.", ANCIENT_WIZARDS),
|
||||
WILDERNESS_49(new WorldPoint(3136, 3914, 0), WILDERNESS, "South-west of the Deserted Keep, level 49 Wilderness.", BRASSICAN_MAGE),
|
||||
WILDERNESS_54(new WorldPoint(2981, 3944, 0), WILDERNESS, "West of the Wilderness Agility Course, level 54 Wilderness.", BRASSICAN_MAGE),
|
||||
ZEAH_BLASTMINE_BANK(new WorldPoint(1504, 3859, 0), ZEAH, "Next to the bank in the Lovakengj blast mine.", BRASSICAN_MAGE),
|
||||
ZEAH_BLASTMINE_NORTH(new WorldPoint(1488, 3881, 0), ZEAH, "Northern part of the Lovakengj blast mine.", BRASSICAN_MAGE),
|
||||
ZEAH_LOVAKITE_FURNACE(new WorldPoint(1507, 3819, 0), ZEAH, "Next to the lovakite furnace in Lovakengj.", ANCIENT_WIZARDS),
|
||||
ZEAH_LOVAKENGJ_MINE(new WorldPoint(1477, 3778, 0), ZEAH, "Next to mithril rock in the Lovakengj mine.", ANCIENT_WIZARDS),
|
||||
ZEAH_SULPHR_MINE(new WorldPoint(1428, 3869, 0), ZEAH, "Western entrance in the Lovakengj sulphur mine.", BRASSICAN_MAGE),
|
||||
ZEAH_SHAYZIEN_BANK(new WorldPoint(1517, 3603, 0), ZEAH, "South-east of the bank in Shayzien.", ANCIENT_WIZARDS),
|
||||
ZEAH_OVERPASS(new WorldPoint(1467, 3714, 0), ZEAH, "Overpass between Lovakengj and Shayzien.", BRASSICAN_MAGE),
|
||||
ZEAH_LIZARDMAN(new WorldPoint(1490, 3698, 0), ZEAH, "Within Lizardman Canyon, east of the ladder. Requires 5% favour with Shayzien.", ANCIENT_WIZARDS),
|
||||
ZEAH_COMBAT_RING(new WorldPoint(1559, 3582, 0), ZEAH, "Shayzien, south-east of the Combat Ring.", BRASSICAN_MAGE),
|
||||
ZEAH_SHAYZIEN_BANK_2(new WorldPoint(1491, 3623, 0), ZEAH, "North-west of the bank in Shayzien.", ANCIENT_WIZARDS),
|
||||
ZEAH_LIBRARY(new WorldPoint(1603, 3843, 0), ZEAH, "North-west of the Arceuus Library.", BRASSICAN_MAGE),
|
||||
ZEAH_HOUSECHURCH(new WorldPoint(1682, 3792, 0), ZEAH, "By the entrance to the Arceuus church.", ANCIENT_WIZARDS),
|
||||
ZEAH_DARK_ALTAR(new WorldPoint(1698, 3881, 0), ZEAH, "West of the Dark Altar.", BRASSICAN_MAGE),
|
||||
ZEAH_ARCEUUS_HOUSE(new WorldPoint(1710, 3700, 0), ZEAH, "By the southern entrance to Arceuus.", ANCIENT_WIZARDS),
|
||||
ZEAH_ESSENCE_MINE(new WorldPoint(1762, 3852, 0), ZEAH, "By the Arceuus essence mine.", BRASSICAN_MAGE),
|
||||
ZEAH_ESSENCE_MINE_NE(new WorldPoint(1773, 3867, 0), ZEAH, "North-east of the Arceuus essence mine.", BRASSICAN_MAGE),
|
||||
ZEAH_PISCARILUS_MINE(new WorldPoint(1768, 3705, 0), ZEAH, "South of the Piscarilius mine.", ANCIENT_WIZARDS),
|
||||
ZEAH_GOLDEN_FIELD_TAVERN(new WorldPoint(1718, 3643, 0), ZEAH, "South of the gravestone in Kingstown.", BRASSICAN_MAGE),
|
||||
ZEAH_MESS_HALL(new WorldPoint(1656, 3621, 0), ZEAH, "East of the Mess hall.", ANCIENT_WIZARDS),
|
||||
ZEAH_WATSONS_HOUSE(new WorldPoint(1653, 3573, 0), ZEAH, "East of Watson's house.", ANCIENT_WIZARDS),
|
||||
ZEAH_VANNAHS_FARM_STORE(new WorldPoint(1807, 3523, 0), ZEAH, "North of Tithe Farm, next to the pond.", BRASSICAN_MAGE),
|
||||
ZEAH_FARMING_GUILD_W(new WorldPoint(1208, 3736, 0), ZEAH, "West of the Farming Guild.", BRASSICAN_MAGE),
|
||||
ZEAH_DAIRY_COW(new WorldPoint(1324, 3722, 0), ZEAH, "North-east of the Kebos Lowlands, east of the dairy cow.", BRASSICAN_MAGE),
|
||||
ZEAH_CRIMSON_SWIFTS(new WorldPoint(1187, 3580, 0), ZEAH, "South-west of the Kebos Swamp, below the crimson swifts.", BRASSICAN_MAGE);
|
||||
|
||||
private final boolean beginnerClue;
|
||||
private final WorldPoint worldPoint;
|
||||
private final HotColdArea hotColdArea;
|
||||
private final String area;
|
||||
private final Enemy enemy;
|
||||
|
||||
HotColdLocation(WorldPoint worldPoint, HotColdArea hotColdArea, String areaDescription, Enemy enemy)
|
||||
{
|
||||
this(false, worldPoint, hotColdArea, areaDescription, enemy);
|
||||
}
|
||||
|
||||
HotColdLocation(WorldPoint worldPoint, HotColdArea hotColdArea, String areaDescription)
|
||||
{
|
||||
//only master clues have enemies, so if no enemy it is a beginner clue
|
||||
this(true, worldPoint, hotColdArea, areaDescription, null);
|
||||
}
|
||||
|
||||
public Rectangle getRect()
|
||||
{
|
||||
final int digRadius = beginnerClue ? HotColdTemperature.BEGINNER_VISIBLY_SHAKING.getMaxDistance() :
|
||||
HotColdTemperature.MASTER_VISIBLY_SHAKING.getMaxDistance();
|
||||
return new Rectangle(worldPoint.getX() - digRadius, worldPoint.getY() - digRadius, digRadius * 2 + 1, digRadius * 2 + 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Eadgars Ruse <https://github.com/Eadgars-Ruse>
|
||||
* Copyright (c) 2019, Jordan Atwood <nightfirecat@protonmail.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.cluescrolls.clues.hotcold;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
|
||||
/**
|
||||
* Solution finder for hot-cold style puzzles.
|
||||
* <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
|
||||
* 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.
|
||||
*/
|
||||
@Getter
|
||||
public class HotColdSolver
|
||||
{
|
||||
private final Set<HotColdLocation> possibleLocations;
|
||||
@Nullable
|
||||
private WorldPoint lastWorldPoint;
|
||||
|
||||
public HotColdSolver(Set<HotColdLocation> possibleLocations)
|
||||
{
|
||||
this.possibleLocations = possibleLocations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a hot-cold update given a {@link WorldPoint} where a check occurred and the resulting temperature and
|
||||
* temperature change discovered at that point. This will filter the set of possible locations which can be the
|
||||
* solution.
|
||||
*
|
||||
* @param worldPoint The point where a hot-cold check occurred
|
||||
* @param temperature The temperature of the checked point
|
||||
* @param temperatureChange The change of temperature of the checked point compared to the previously-checked point
|
||||
* @return A set of {@link HotColdLocation}s which are still possible after the filtering occurs. This return value
|
||||
* is the same as would be returned by {@code getPossibleLocations()}.
|
||||
*/
|
||||
public Set<HotColdLocation> signal(@Nonnull final WorldPoint worldPoint, @Nonnull final HotColdTemperature temperature, @Nullable final HotColdTemperatureChange temperatureChange)
|
||||
{
|
||||
// when the strange device reads a temperature, that means that the center of the final dig location
|
||||
// is a range of squares away from the player's current location (Chebyshev AKA Chess-board distance)
|
||||
int maxSquaresAway = temperature.getMaxDistance();
|
||||
int minSquaresAway = temperature.getMinDistance();
|
||||
|
||||
// maxDistanceArea encompasses all of the points that are within the max possible distance from the player
|
||||
final Rectangle maxDistanceArea = new Rectangle(
|
||||
worldPoint.getX() - maxSquaresAway,
|
||||
worldPoint.getY() - maxSquaresAway,
|
||||
2 * maxSquaresAway + 1,
|
||||
2 * maxSquaresAway + 1);
|
||||
// minDistanceArea encompasses all of the points that are within the min possible distance from the player
|
||||
final Rectangle minDistanceArea = new Rectangle(
|
||||
worldPoint.getX() - minSquaresAway,
|
||||
worldPoint.getY() - minSquaresAway,
|
||||
2 * minSquaresAway + 1,
|
||||
2 * minSquaresAway + 1);
|
||||
|
||||
// eliminate from consideration dig spots that lie entirely within the min range or entirely outside of the max range
|
||||
possibleLocations.removeIf(entry -> minDistanceArea.contains(entry.getRect()) || !maxDistanceArea.intersects(entry.getRect()));
|
||||
|
||||
// if a previous world point has been recorded, we can consider the warmer/colder result from the strange device
|
||||
if (lastWorldPoint != null && temperatureChange != null)
|
||||
{
|
||||
switch (temperatureChange)
|
||||
{
|
||||
case COLDER:
|
||||
// eliminate spots that are absolutely warmer
|
||||
possibleLocations.removeIf(entry -> isFirstPointCloserRect(worldPoint, lastWorldPoint, entry.getRect()));
|
||||
break;
|
||||
case WARMER:
|
||||
// eliminate spots that are absolutely colder
|
||||
possibleLocations.removeIf(entry -> isFirstPointCloserRect(lastWorldPoint, worldPoint, entry.getRect()));
|
||||
break;
|
||||
case SAME:
|
||||
// eliminate spots which are absolutely colder or warmer (as they would not yield a SAME temperature change)
|
||||
possibleLocations.removeIf(entry ->
|
||||
isFirstPointCloserRect(worldPoint, lastWorldPoint, entry.getRect())
|
||||
|| isFirstPointCloserRect(lastWorldPoint, worldPoint, entry.getRect()));
|
||||
}
|
||||
}
|
||||
|
||||
lastWorldPoint = worldPoint;
|
||||
return getPossibleLocations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the first point passed is closer to each corner of the given rectangle than the second point.
|
||||
*
|
||||
* @param firstPoint First point to test. Return result will be relating to this point's location.
|
||||
* @param secondPoint Second point to test
|
||||
* @param rect Rectangle, whose corner points will be compared to the first and second points passed
|
||||
* @return {@code true} if {@code firstPoint} is closer to each of {@code rect}'s four corner points than
|
||||
* {@code secondPoint}, {@code false} otherwise.
|
||||
* @see WorldPoint#distanceTo2D
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean isFirstPointCloserRect(final WorldPoint firstPoint, final WorldPoint secondPoint, final Rectangle rect)
|
||||
{
|
||||
final WorldPoint nePoint = new WorldPoint((rect.x + rect.width), (rect.y + rect.height), 0);
|
||||
|
||||
if (!isFirstPointCloser(firstPoint, secondPoint, nePoint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final WorldPoint sePoint = new WorldPoint((rect.x + rect.width), rect.y, 0);
|
||||
|
||||
if (!isFirstPointCloser(firstPoint, secondPoint, sePoint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final WorldPoint nwPoint = new WorldPoint(rect.x, (rect.y + rect.height), 0);
|
||||
|
||||
if (!isFirstPointCloser(firstPoint, secondPoint, nwPoint))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final WorldPoint swPoint = new WorldPoint(rect.x, rect.y, 0);
|
||||
return (isFirstPointCloser(firstPoint, secondPoint, swPoint));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the first point passed is closer to the given point of comparison than the second point.
|
||||
*
|
||||
* @param firstPoint First point to test. Return result will be relating to this point's location.
|
||||
* @param secondPoint Second point to test
|
||||
* @param worldPoint Point to compare to the first and second points passed
|
||||
* @return {@code true} if {@code firstPoint} is closer to {@code worldPoint} than {@code secondPoint},
|
||||
* {@code false} otherwise.
|
||||
* @see WorldPoint#distanceTo2D
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static boolean isFirstPointCloser(final WorldPoint firstPoint, final WorldPoint secondPoint, final WorldPoint worldPoint)
|
||||
{
|
||||
return firstPoint.distanceTo2D(worldPoint) < secondPoint.distanceTo2D(worldPoint);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Jordan Atwood <nightfirecat@protonmail.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.cluescrolls.clues.hotcold;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.util.Text;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum HotColdTemperature
|
||||
{
|
||||
ICE_COLD("ice cold", 500, 5000),
|
||||
VERY_COLD("very cold", 200, 499),
|
||||
COLD("cold", 150, 199),
|
||||
WARM("warm", 100, 149),
|
||||
HOT("hot", 70, 99),
|
||||
VERY_HOT("very hot", 30, 69),
|
||||
BEGINNER_INCREDIBLY_HOT("incredibly hot", 4, 29),
|
||||
BEGINNER_VISIBLY_SHAKING("visibly shaking", 0, 3),
|
||||
MASTER_INCREDIBLY_HOT("incredibly hot", 5, 29),
|
||||
MASTER_VISIBLY_SHAKING("visibly shaking", 0, 4);
|
||||
|
||||
public static final Set<HotColdTemperature> BEGINNER_HOT_COLD_TEMPERATURES = Sets.immutableEnumSet(
|
||||
ICE_COLD,
|
||||
VERY_COLD,
|
||||
COLD,
|
||||
WARM,
|
||||
HOT,
|
||||
VERY_HOT,
|
||||
BEGINNER_INCREDIBLY_HOT,
|
||||
BEGINNER_VISIBLY_SHAKING
|
||||
);
|
||||
public static final Set<HotColdTemperature> MASTER_HOT_COLD_TEMPERATURES = Sets.immutableEnumSet(
|
||||
ICE_COLD,
|
||||
VERY_COLD,
|
||||
COLD,
|
||||
WARM,
|
||||
HOT,
|
||||
VERY_HOT,
|
||||
MASTER_INCREDIBLY_HOT,
|
||||
MASTER_VISIBLY_SHAKING
|
||||
);
|
||||
|
||||
private final String text;
|
||||
private final int minDistance;
|
||||
private final int maxDistance;
|
||||
|
||||
private static final String DEVICE_USED_START_TEXT = "The device is ";
|
||||
|
||||
/**
|
||||
* Gets the temperature from a set of temperatures corresponding to the passed string.
|
||||
*
|
||||
* @param temperatureSet A set of temperature values to select from
|
||||
* @param message A string containing a temperature value
|
||||
* @return The corresponding enum from the given temperature set.
|
||||
* <p>
|
||||
* Note that in cases where two temperature values in the given set are equally likely to be the given
|
||||
* temperature (say, two temperatures with identical text values), the behavior is undefined.
|
||||
*/
|
||||
@Nullable
|
||||
public static HotColdTemperature getFromTemperatureSet(final Set<HotColdTemperature> temperatureSet, final String message)
|
||||
{
|
||||
if (!message.startsWith(DEVICE_USED_START_TEXT) || temperatureSet == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final String messageStart = Text.fromCSV(message).get(0);
|
||||
final List<HotColdTemperature> possibleTemperatures = new ArrayList<>();
|
||||
|
||||
for (final HotColdTemperature temperature : temperatureSet)
|
||||
{
|
||||
if (messageStart.contains(temperature.getText()))
|
||||
{
|
||||
possibleTemperatures.add(temperature);
|
||||
}
|
||||
}
|
||||
|
||||
return possibleTemperatures.stream()
|
||||
// For messages such as "The device is very cold", this will choose the Enum with text of greatest length so
|
||||
// that VERY_COLD would be selected over COLD, though both Enums have matching text for this message.
|
||||
.max(Comparator.comparingInt(x -> (x.getText()).length()))
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Jordan Atwood <nightfirecat@protonmail.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.cluescrolls.clues.hotcold;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public enum HotColdTemperatureChange
|
||||
{
|
||||
WARMER("and warmer than"),
|
||||
SAME("and the same temperature as"),
|
||||
COLDER("but colder than");
|
||||
|
||||
private final String text;
|
||||
|
||||
public static HotColdTemperatureChange of(final String message)
|
||||
{
|
||||
if (!message.endsWith(" last time."))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (final HotColdTemperatureChange change : values())
|
||||
{
|
||||
if (message.contains(change.text))
|
||||
{
|
||||
return change;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues.item;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Item;
|
||||
|
||||
public class AllRequirementsCollection implements ItemRequirement
|
||||
{
|
||||
private String name;
|
||||
private ItemRequirement[] requirements;
|
||||
|
||||
public AllRequirementsCollection(String name, ItemRequirement... requirements)
|
||||
{
|
||||
this.name = name;
|
||||
this.requirements = requirements;
|
||||
}
|
||||
|
||||
public AllRequirementsCollection(ItemRequirement... requirements)
|
||||
{
|
||||
this("N/A", requirements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(int itemId)
|
||||
{
|
||||
for (ItemRequirement requirement : requirements)
|
||||
{
|
||||
if (requirement.fulfilledBy(itemId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(Item[] items)
|
||||
{
|
||||
for (ItemRequirement requirement : requirements)
|
||||
{
|
||||
if (!requirement.fulfilledBy(items))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollectiveName(Client client)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues.item;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Item;
|
||||
|
||||
public class AnyRequirementCollection implements ItemRequirement
|
||||
{
|
||||
private final String name;
|
||||
private final ItemRequirement[] requirements;
|
||||
|
||||
public AnyRequirementCollection(String name, ItemRequirement... requirements)
|
||||
{
|
||||
this.name = name;
|
||||
this.requirements = requirements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(int itemId)
|
||||
{
|
||||
for (ItemRequirement requirement : requirements)
|
||||
{
|
||||
if (requirement.fulfilledBy(itemId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(Item[] items)
|
||||
{
|
||||
for (ItemRequirement requirement : requirements)
|
||||
{
|
||||
if (requirement.fulfilledBy(items))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollectiveName(Client client)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues.item;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Item;
|
||||
|
||||
public interface ItemRequirement
|
||||
{
|
||||
boolean fulfilledBy(int itemId);
|
||||
|
||||
boolean fulfilledBy(Item[] items);
|
||||
|
||||
String getCollectiveName(Client client);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues.item;
|
||||
|
||||
import net.runelite.api.EquipmentInventorySlot;
|
||||
|
||||
public class ItemRequirements
|
||||
{
|
||||
public static SingleItemRequirement item(int itemId)
|
||||
{
|
||||
return new SingleItemRequirement(itemId);
|
||||
}
|
||||
|
||||
public static RangeItemRequirement range(int startItemId, int endItemId)
|
||||
{
|
||||
return range(null, startItemId, endItemId);
|
||||
}
|
||||
|
||||
public static RangeItemRequirement range(String name, int startItemId, int endItemId)
|
||||
{
|
||||
return new RangeItemRequirement(name, startItemId, endItemId);
|
||||
}
|
||||
|
||||
public static AnyRequirementCollection any(String name, ItemRequirement... requirements)
|
||||
{
|
||||
return new AnyRequirementCollection(name, requirements);
|
||||
}
|
||||
|
||||
public static AllRequirementsCollection all(ItemRequirement... requirements)
|
||||
{
|
||||
return new AllRequirementsCollection(requirements);
|
||||
}
|
||||
|
||||
public static AllRequirementsCollection all(String name, ItemRequirement... requirements)
|
||||
{
|
||||
return new AllRequirementsCollection(name, requirements);
|
||||
}
|
||||
|
||||
public static SlotLimitationRequirement emptySlot(String description, EquipmentInventorySlot... slots)
|
||||
{
|
||||
return new SlotLimitationRequirement(description, slots);
|
||||
}
|
||||
|
||||
public static MultipleOfItemRequirement xOfItem(int itemId, int quantity)
|
||||
{
|
||||
return new MultipleOfItemRequirement(itemId, quantity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.cluescrolls.clues.item;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemComposition;
|
||||
|
||||
public class MultipleOfItemRequirement implements ItemRequirement
|
||||
{
|
||||
private final int itemId;
|
||||
private final int quantity;
|
||||
|
||||
public MultipleOfItemRequirement(int itemId, int quantity)
|
||||
{
|
||||
this.itemId = itemId;
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(int itemId)
|
||||
{
|
||||
return itemId == this.itemId && this.quantity == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(Item[] items)
|
||||
{
|
||||
int quantityFound = 0;
|
||||
for (Item item : items)
|
||||
{
|
||||
if (item.getId() == itemId)
|
||||
{
|
||||
quantityFound += item.getQuantity();
|
||||
if (quantityFound >= quantity)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollectiveName(Client client)
|
||||
{
|
||||
ItemComposition definition = client.getItemDefinition(itemId);
|
||||
|
||||
if (definition == null)
|
||||
{
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
return definition.getName() + " x" + this.quantity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues.item;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Item;
|
||||
|
||||
public class RangeItemRequirement implements ItemRequirement
|
||||
{
|
||||
private final String name;
|
||||
private final int startItemId;
|
||||
private final int endItemId;
|
||||
|
||||
public RangeItemRequirement(String name, int startItemId, int endItemId)
|
||||
{
|
||||
this.name = name;
|
||||
this.startItemId = startItemId;
|
||||
this.endItemId = endItemId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(int itemId)
|
||||
{
|
||||
return itemId >= startItemId && itemId <= endItemId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(Item[] items)
|
||||
{
|
||||
for (Item item : items)
|
||||
{
|
||||
if (item.getId() >= startItemId && item.getId() <= endItemId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollectiveName(Client client)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues.item;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemComposition;
|
||||
|
||||
public class SingleItemRequirement implements ItemRequirement
|
||||
{
|
||||
private final int itemId;
|
||||
|
||||
public SingleItemRequirement(int itemId)
|
||||
{
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(int itemId)
|
||||
{
|
||||
return this.itemId == itemId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(Item[] items)
|
||||
{
|
||||
for (Item item : items)
|
||||
{
|
||||
if (item.getId() == itemId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollectiveName(Client client)
|
||||
{
|
||||
ItemComposition definition = client.getItemDefinition(itemId);
|
||||
|
||||
if (definition == null)
|
||||
{
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
return definition.getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.cluescrolls.clues.item;
|
||||
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.EquipmentInventorySlot;
|
||||
import net.runelite.api.Item;
|
||||
|
||||
public class SlotLimitationRequirement implements ItemRequirement
|
||||
{
|
||||
private final String description;
|
||||
private final EquipmentInventorySlot[] slots;
|
||||
|
||||
public SlotLimitationRequirement(String description, EquipmentInventorySlot... slots)
|
||||
{
|
||||
this.description = description;
|
||||
this.slots = slots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(int itemId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fulfilledBy(Item[] items)
|
||||
{
|
||||
for (EquipmentInventorySlot slot : slots)
|
||||
{
|
||||
if (slot.getSlotIdx() >= items.length)
|
||||
{
|
||||
continue; //We can't check the slot, because there is nothing in it, the array hasn't been resized
|
||||
}
|
||||
|
||||
if (items[slot.getSlotIdx()].getId() != -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCollectiveName(Client client)
|
||||
{
|
||||
return description;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.plugins.gpu;
|
||||
|
||||
import com.jogamp.opengl.GL4;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
class GLUtil
|
||||
{
|
||||
private static final int ERR_LEN = 1024;
|
||||
|
||||
private static final int[] buf = new int[1];
|
||||
private static final float[] fbuf = new float[1];
|
||||
|
||||
static int glGetInteger(GL4 gl, int pname)
|
||||
{
|
||||
gl.glGetIntegerv(pname, buf, 0);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static float glGetFloat(GL4 gl, int pname)
|
||||
{
|
||||
gl.glGetFloatv(pname, fbuf, 0);
|
||||
return fbuf[0];
|
||||
}
|
||||
|
||||
static int glGetShader(GL4 gl, int shader, int pname)
|
||||
{
|
||||
gl.glGetShaderiv(shader, pname, buf, 0);
|
||||
assert buf[0] > -1;
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static int glGetProgram(GL4 gl, int program, int pname)
|
||||
{
|
||||
gl.glGetProgramiv(program, pname, buf, 0);
|
||||
assert buf[0] > -1;
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static String glGetShaderInfoLog(GL4 gl, int shader)
|
||||
{
|
||||
byte[] err = new byte[ERR_LEN];
|
||||
gl.glGetShaderInfoLog(shader, ERR_LEN, buf, 0, err, 0);
|
||||
return new String(err, 0, buf[0], StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
static String glGetProgramInfoLog(GL4 gl, int program)
|
||||
{
|
||||
byte[] err = new byte[ERR_LEN];
|
||||
gl.glGetProgramInfoLog(program, ERR_LEN, buf, 0, err, 0);
|
||||
return new String(err, 0, buf[0], StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
static int glGenVertexArrays(GL4 gl)
|
||||
{
|
||||
gl.glGenVertexArrays(1, buf, 0);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static void glDeleteVertexArrays(GL4 gl, int vertexArray)
|
||||
{
|
||||
buf[0] = vertexArray;
|
||||
gl.glDeleteVertexArrays(1, buf, 0);
|
||||
}
|
||||
|
||||
static int glGenBuffers(GL4 gl)
|
||||
{
|
||||
gl.glGenBuffers(1, buf, 0);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static void glDeleteBuffer(GL4 gl, int buffer)
|
||||
{
|
||||
buf[0] = buffer;
|
||||
gl.glDeleteBuffers(1, buf, 0);
|
||||
}
|
||||
|
||||
static int glGenTexture(GL4 gl)
|
||||
{
|
||||
gl.glGenTextures(1, buf, 0);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static void glDeleteTexture(GL4 gl, int texture)
|
||||
{
|
||||
buf[0] = texture;
|
||||
gl.glDeleteTextures(1, buf, 0);
|
||||
}
|
||||
|
||||
static int glGenFrameBuffer(GL4 gl)
|
||||
{
|
||||
gl.glGenFramebuffers(1, buf, 0);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static void glDeleteFrameBuffer(GL4 gl, int frameBuffer)
|
||||
{
|
||||
buf[0] = frameBuffer;
|
||||
gl.glDeleteFramebuffers(1, buf, 0);
|
||||
}
|
||||
|
||||
static int glGenRenderbuffer(GL4 gl)
|
||||
{
|
||||
gl.glGenRenderbuffers(1, buf, 0);
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
static void glDeleteRenderbuffers(GL4 gl, int renderBuffer)
|
||||
{
|
||||
buf[0] = renderBuffer;
|
||||
gl.glDeleteRenderbuffers(1, buf, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.plugins.gpu;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
class GpuFloatBuffer
|
||||
{
|
||||
private FloatBuffer buffer = allocateDirect(65536);
|
||||
|
||||
void put(float texture, float u, float v, float pad)
|
||||
{
|
||||
buffer.put(texture).put(u).put(v).put(pad);
|
||||
}
|
||||
|
||||
void flip()
|
||||
{
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
void ensureCapacity(int size)
|
||||
{
|
||||
int capacity = buffer.capacity();
|
||||
final int position = buffer.position();
|
||||
if ((capacity - position) < size)
|
||||
{
|
||||
do
|
||||
{
|
||||
capacity *= 2;
|
||||
}
|
||||
while ((capacity - position) < size);
|
||||
|
||||
FloatBuffer newB = allocateDirect(capacity);
|
||||
buffer.flip();
|
||||
newB.put(buffer);
|
||||
buffer = newB;
|
||||
}
|
||||
}
|
||||
|
||||
FloatBuffer getBuffer()
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static FloatBuffer allocateDirect(int size)
|
||||
{
|
||||
return ByteBuffer.allocateDirect(size * Float.BYTES)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.asFloatBuffer();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.plugins.gpu;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
class GpuIntBuffer
|
||||
{
|
||||
private IntBuffer buffer = allocateDirect(65536);
|
||||
|
||||
void put(int x, int y, int z)
|
||||
{
|
||||
buffer.put(x).put(y).put(z);
|
||||
}
|
||||
|
||||
void put(int x, int y, int z, int c)
|
||||
{
|
||||
buffer.put(x).put(y).put(z).put(c);
|
||||
}
|
||||
|
||||
void flip()
|
||||
{
|
||||
buffer.flip();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
void ensureCapacity(int size)
|
||||
{
|
||||
int capacity = buffer.capacity();
|
||||
final int position = buffer.position();
|
||||
if ((capacity - position) < size)
|
||||
{
|
||||
do
|
||||
{
|
||||
capacity *= 2;
|
||||
}
|
||||
while ((capacity - position) < size);
|
||||
|
||||
IntBuffer newB = allocateDirect(capacity);
|
||||
buffer.flip();
|
||||
newB.put(buffer);
|
||||
buffer = newB;
|
||||
}
|
||||
}
|
||||
|
||||
IntBuffer getBuffer()
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static IntBuffer allocateDirect(int size)
|
||||
{
|
||||
return ByteBuffer.allocateDirect(size * Integer.BYTES)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.asIntBuffer();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.plugins.gpu;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.Range;
|
||||
import static net.runelite.client.plugins.gpu.GpuPlugin.MAX_FOG_DEPTH;
|
||||
import net.runelite.client.plugins.gpu.config.AntiAliasingMode;
|
||||
import static net.runelite.client.plugins.gpu.GpuPlugin.MAX_DISTANCE;
|
||||
import net.runelite.client.plugins.gpu.config.ColorBlindMode;
|
||||
import net.runelite.client.plugins.gpu.config.UIScalingMode;
|
||||
|
||||
@ConfigGroup("gpu")
|
||||
public interface GpuPluginConfig extends Config
|
||||
{
|
||||
@Range(
|
||||
max = MAX_DISTANCE
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "drawDistance",
|
||||
name = "Draw Distance",
|
||||
description = "Draw distance",
|
||||
position = 1
|
||||
)
|
||||
default int drawDistance()
|
||||
{
|
||||
return 25;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "smoothBanding",
|
||||
name = "Remove Color Banding",
|
||||
description = "Smooths out the color banding that is present in the CPU renderer",
|
||||
position = 2
|
||||
)
|
||||
default boolean smoothBanding()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "antiAliasingMode",
|
||||
name = "Anti Aliasing",
|
||||
description = "Configures the anti-aliasing mode",
|
||||
position = 3
|
||||
)
|
||||
default AntiAliasingMode antiAliasingMode()
|
||||
{
|
||||
return AntiAliasingMode.DISABLED;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "uiScalingMode",
|
||||
name = "UI scaling mode",
|
||||
description = "Sampling function to use for the UI in stretched mode",
|
||||
position = 4
|
||||
)
|
||||
default UIScalingMode uiScalingMode()
|
||||
{
|
||||
return UIScalingMode.LINEAR;
|
||||
}
|
||||
|
||||
@Range(
|
||||
max = MAX_FOG_DEPTH
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "fogDepth",
|
||||
name = "Fog depth",
|
||||
description = "Distance from the scene edge the fog starts",
|
||||
position = 5
|
||||
)
|
||||
default int fogDepth()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "useComputeShaders",
|
||||
name = "Compute Shaders",
|
||||
description = "Offloads face sorting to GPU, enabling extended draw distance. Requires plugin restart.",
|
||||
warning = "This feature requires OpenGL 4.3 to use. Please check that your GPU supports this.\nRestart the plugin for changes to take effect.",
|
||||
position = 6
|
||||
)
|
||||
default boolean useComputeShaders()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Range(
|
||||
min = 0,
|
||||
max = 16
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "anisotropicFilteringLevel",
|
||||
name = "Anisotropic Filtering",
|
||||
description = "Configures the anisotropic filtering level.",
|
||||
position = 7
|
||||
)
|
||||
default int anisotropicFilteringLevel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "colorBlindMode",
|
||||
name = "Colorblindness Correction",
|
||||
description = "Adjusts colors to account for colorblindness",
|
||||
position = 8
|
||||
)
|
||||
default ColorBlindMode colorBlindMode()
|
||||
{
|
||||
return ColorBlindMode.NONE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
* 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.plugins.gpu;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Constants;
|
||||
import net.runelite.api.DecorativeObject;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.GroundObject;
|
||||
import net.runelite.api.Model;
|
||||
import net.runelite.api.Perspective;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.Renderable;
|
||||
import net.runelite.api.Scene;
|
||||
import net.runelite.api.SceneTileModel;
|
||||
import net.runelite.api.SceneTilePaint;
|
||||
import net.runelite.api.Tile;
|
||||
import net.runelite.api.WallObject;
|
||||
|
||||
@Singleton
|
||||
@Slf4j
|
||||
class SceneUploader
|
||||
{
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
int sceneId = (int) (System.currentTimeMillis() / 1000L);
|
||||
private int offset;
|
||||
private int uvoffset;
|
||||
|
||||
void upload(Scene scene, GpuIntBuffer vertexbuffer, GpuFloatBuffer uvBuffer)
|
||||
{
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
|
||||
++sceneId;
|
||||
offset = 0;
|
||||
uvoffset = 0;
|
||||
vertexbuffer.clear();
|
||||
uvBuffer.clear();
|
||||
|
||||
for (int z = 0; z < Constants.MAX_Z; ++z)
|
||||
{
|
||||
for (int x = 0; x < Constants.SCENE_SIZE; ++x)
|
||||
{
|
||||
for (int y = 0; y < Constants.SCENE_SIZE; ++y)
|
||||
{
|
||||
Tile tile = scene.getTiles()[z][x][y];
|
||||
if (tile != null)
|
||||
{
|
||||
upload(tile, vertexbuffer, uvBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.stop();
|
||||
log.debug("Scene upload time: {}", stopwatch);
|
||||
}
|
||||
|
||||
private void upload(Tile tile, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer)
|
||||
{
|
||||
Tile bridge = tile.getBridge();
|
||||
if (bridge != null)
|
||||
{
|
||||
upload(bridge, vertexBuffer, uvBuffer);
|
||||
}
|
||||
|
||||
SceneTilePaint sceneTilePaint = tile.getSceneTilePaint();
|
||||
if (sceneTilePaint != null)
|
||||
{
|
||||
sceneTilePaint.setBufferOffset(offset);
|
||||
if (sceneTilePaint.getTexture() != -1)
|
||||
{
|
||||
sceneTilePaint.setUvBufferOffset(uvoffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
sceneTilePaint.setUvBufferOffset(-1);
|
||||
}
|
||||
Point tilePoint = tile.getSceneLocation();
|
||||
int len = upload(sceneTilePaint,
|
||||
tile.getRenderLevel(), tilePoint.getX(), tilePoint.getY(),
|
||||
vertexBuffer, uvBuffer,
|
||||
0, 0, false);
|
||||
sceneTilePaint.setBufferLen(len);
|
||||
offset += len;
|
||||
if (sceneTilePaint.getTexture() != -1)
|
||||
{
|
||||
uvoffset += len;
|
||||
}
|
||||
}
|
||||
|
||||
SceneTileModel sceneTileModel = tile.getSceneTileModel();
|
||||
if (sceneTileModel != null)
|
||||
{
|
||||
sceneTileModel.setBufferOffset(offset);
|
||||
if (sceneTileModel.getTriangleTextureId() != null)
|
||||
{
|
||||
sceneTileModel.setUvBufferOffset(uvoffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
sceneTileModel.setUvBufferOffset(-1);
|
||||
}
|
||||
Point tilePoint = tile.getSceneLocation();
|
||||
int len = upload(sceneTileModel,
|
||||
tilePoint.getX(), tilePoint.getY(),
|
||||
vertexBuffer, uvBuffer,
|
||||
0, 0, false);
|
||||
sceneTileModel.setBufferLen(len);
|
||||
offset += len;
|
||||
if (sceneTileModel.getTriangleTextureId() != null)
|
||||
{
|
||||
uvoffset += len;
|
||||
}
|
||||
}
|
||||
|
||||
WallObject wallObject = tile.getWallObject();
|
||||
if (wallObject != null)
|
||||
{
|
||||
Renderable renderable1 = wallObject.getRenderable();
|
||||
if (renderable1 instanceof Model)
|
||||
{
|
||||
uploadModel((Model) renderable1, vertexBuffer, uvBuffer);
|
||||
}
|
||||
|
||||
Renderable renderable2 = wallObject.getRenderable2();
|
||||
if (renderable2 instanceof Model)
|
||||
{
|
||||
uploadModel((Model) renderable2, vertexBuffer, uvBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
GroundObject groundObject = tile.getGroundObject();
|
||||
if (groundObject != null)
|
||||
{
|
||||
Renderable renderable = groundObject.getRenderable();
|
||||
if (renderable instanceof Model)
|
||||
{
|
||||
uploadModel((Model) renderable, vertexBuffer, uvBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
DecorativeObject decorativeObject = tile.getDecorativeObject();
|
||||
if (decorativeObject != null)
|
||||
{
|
||||
Renderable renderable = decorativeObject.getRenderable();
|
||||
if (renderable instanceof Model)
|
||||
{
|
||||
uploadModel((Model) renderable, vertexBuffer, uvBuffer);
|
||||
}
|
||||
|
||||
Renderable renderable2 = decorativeObject.getRenderable2();
|
||||
if (renderable2 instanceof Model)
|
||||
{
|
||||
uploadModel((Model) renderable2, vertexBuffer, uvBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
GameObject[] gameObjects = tile.getGameObjects();
|
||||
for (GameObject gameObject : gameObjects)
|
||||
{
|
||||
if (gameObject == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Renderable renderable = gameObject.getRenderable();
|
||||
if (renderable instanceof Model)
|
||||
{
|
||||
uploadModel((Model) gameObject.getRenderable(), vertexBuffer, uvBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int upload(SceneTilePaint tile, int tileZ, int tileX, int tileY, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer,
|
||||
int offsetX, int offsetY, boolean padUvs)
|
||||
{
|
||||
final int[][][] tileHeights = client.getTileHeights();
|
||||
|
||||
final int localX = offsetX;
|
||||
final int localY = offsetY;
|
||||
|
||||
int swHeight = tileHeights[tileZ][tileX][tileY];
|
||||
int seHeight = tileHeights[tileZ][tileX + 1][tileY];
|
||||
int neHeight = tileHeights[tileZ][tileX + 1][tileY + 1];
|
||||
int nwHeight = tileHeights[tileZ][tileX][tileY + 1];
|
||||
|
||||
final int neColor = tile.getNeColor();
|
||||
final int nwColor = tile.getNwColor();
|
||||
final int seColor = tile.getSeColor();
|
||||
final int swColor = tile.getSwColor();
|
||||
|
||||
if (neColor == 12345678)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
vertexBuffer.ensureCapacity(24);
|
||||
uvBuffer.ensureCapacity(24);
|
||||
|
||||
// 0,0
|
||||
int vertexDx = localX;
|
||||
int vertexDy = localY;
|
||||
int vertexDz = swHeight;
|
||||
final int c1 = swColor;
|
||||
|
||||
// 1,0
|
||||
int vertexCx = localX + Perspective.LOCAL_TILE_SIZE;
|
||||
int vertexCy = localY;
|
||||
int vertexCz = seHeight;
|
||||
final int c2 = seColor;
|
||||
|
||||
// 1,1
|
||||
int vertexAx = localX + Perspective.LOCAL_TILE_SIZE;
|
||||
int vertexAy = localY + Perspective.LOCAL_TILE_SIZE;
|
||||
int vertexAz = neHeight;
|
||||
final int c3 = neColor;
|
||||
|
||||
// 0,1
|
||||
int vertexBx = localX;
|
||||
int vertexBy = localY + Perspective.LOCAL_TILE_SIZE;
|
||||
int vertexBz = nwHeight;
|
||||
final int c4 = nwColor;
|
||||
|
||||
vertexBuffer.put(vertexAx, vertexAz, vertexAy, c3);
|
||||
vertexBuffer.put(vertexBx, vertexBz, vertexBy, c4);
|
||||
vertexBuffer.put(vertexCx, vertexCz, vertexCy, c2);
|
||||
|
||||
vertexBuffer.put(vertexDx, vertexDz, vertexDy, c1);
|
||||
vertexBuffer.put(vertexCx, vertexCz, vertexCy, c2);
|
||||
vertexBuffer.put(vertexBx, vertexBz, vertexBy, c4);
|
||||
|
||||
if (padUvs || tile.getTexture() != -1)
|
||||
{
|
||||
float tex = tile.getTexture() + 1f;
|
||||
uvBuffer.put(tex, 1.0f, 1.0f, 0f);
|
||||
uvBuffer.put(tex, 0.0f, 1.0f, 0f);
|
||||
uvBuffer.put(tex, 1.0f, 0.0f, 0f);
|
||||
|
||||
uvBuffer.put(tex, 0.0f, 0.0f, 0f);
|
||||
uvBuffer.put(tex, 1.0f, 0.0f, 0f);
|
||||
uvBuffer.put(tex, 0.0f, 1.0f, 0f);
|
||||
}
|
||||
|
||||
return 6;
|
||||
}
|
||||
|
||||
int upload(SceneTileModel sceneTileModel, int tileX, int tileY, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer,
|
||||
int offsetX, int offsetY, boolean padUvs)
|
||||
{
|
||||
final int[] faceX = sceneTileModel.getFaceX();
|
||||
final int[] faceY = sceneTileModel.getFaceY();
|
||||
final int[] faceZ = sceneTileModel.getFaceZ();
|
||||
|
||||
final int[] vertexX = sceneTileModel.getVertexX();
|
||||
final int[] vertexY = sceneTileModel.getVertexY();
|
||||
final int[] vertexZ = sceneTileModel.getVertexZ();
|
||||
|
||||
final int[] triangleColorA = sceneTileModel.getTriangleColorA();
|
||||
final int[] triangleColorB = sceneTileModel.getTriangleColorB();
|
||||
final int[] triangleColorC = sceneTileModel.getTriangleColorC();
|
||||
|
||||
final int[] triangleTextures = sceneTileModel.getTriangleTextureId();
|
||||
|
||||
final int faceCount = faceX.length;
|
||||
|
||||
vertexBuffer.ensureCapacity(faceCount * 12);
|
||||
uvBuffer.ensureCapacity(faceCount * 12);
|
||||
|
||||
int baseX = Perspective.LOCAL_TILE_SIZE * tileX;
|
||||
int baseY = Perspective.LOCAL_TILE_SIZE * tileY;
|
||||
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < faceCount; ++i)
|
||||
{
|
||||
final int triangleA = faceX[i];
|
||||
final int triangleB = faceY[i];
|
||||
final int triangleC = faceZ[i];
|
||||
|
||||
final int colorA = triangleColorA[i];
|
||||
final int colorB = triangleColorB[i];
|
||||
final int colorC = triangleColorC[i];
|
||||
|
||||
if (colorA == 12345678)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
cnt += 3;
|
||||
|
||||
// vertexes are stored in scene local, convert to tile local
|
||||
int vertexXA = vertexX[triangleA] - baseX;
|
||||
int vertexZA = vertexZ[triangleA] - baseY;
|
||||
|
||||
int vertexXB = vertexX[triangleB] - baseX;
|
||||
int vertexZB = vertexZ[triangleB] - baseY;
|
||||
|
||||
int vertexXC = vertexX[triangleC] - baseX;
|
||||
int vertexZC = vertexZ[triangleC] - baseY;
|
||||
|
||||
vertexBuffer.put(vertexXA + offsetX, vertexY[triangleA], vertexZA + offsetY, colorA);
|
||||
vertexBuffer.put(vertexXB + offsetX, vertexY[triangleB], vertexZB + offsetY, colorB);
|
||||
vertexBuffer.put(vertexXC + offsetX, vertexY[triangleC], vertexZC + offsetY, colorC);
|
||||
|
||||
if (padUvs || triangleTextures != null)
|
||||
{
|
||||
if (triangleTextures != null && triangleTextures[i] != -1)
|
||||
{
|
||||
float tex = triangleTextures[i] + 1f;
|
||||
uvBuffer.put(tex, vertexXA / 128f, vertexZA / 128f, 0f);
|
||||
uvBuffer.put(tex, vertexXB / 128f, vertexZB / 128f, 0f);
|
||||
uvBuffer.put(tex, vertexXC / 128f, vertexZC / 128f, 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
uvBuffer.put(0, 0, 0, 0f);
|
||||
uvBuffer.put(0, 0, 0, 0f);
|
||||
uvBuffer.put(0, 0, 0, 0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
private void uploadModel(Model model, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer)
|
||||
{
|
||||
if (model.getSceneId() == sceneId)
|
||||
{
|
||||
return; // model has already been uploaded
|
||||
}
|
||||
|
||||
model.setBufferOffset(offset);
|
||||
if (model.getFaceTextures() != null)
|
||||
{
|
||||
model.setUvBufferOffset(uvoffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
model.setUvBufferOffset(-1);
|
||||
}
|
||||
model.setSceneId(sceneId);
|
||||
|
||||
final int triangleCount = model.getTrianglesCount();
|
||||
|
||||
vertexBuffer.ensureCapacity(triangleCount * 12);
|
||||
uvBuffer.ensureCapacity(triangleCount * 12);
|
||||
|
||||
final int[] vertexX = model.getVerticesX();
|
||||
final int[] vertexY = model.getVerticesY();
|
||||
final int[] vertexZ = model.getVerticesZ();
|
||||
|
||||
final int[] trianglesX = model.getTrianglesX();
|
||||
final int[] trianglesY = model.getTrianglesY();
|
||||
final int[] trianglesZ = model.getTrianglesZ();
|
||||
|
||||
final int[] color1s = model.getFaceColors1();
|
||||
final int[] color2s = model.getFaceColors2();
|
||||
final int[] color3s = model.getFaceColors3();
|
||||
|
||||
final byte[] transparencies = model.getTriangleTransparencies();
|
||||
final short[] faceTextures = model.getFaceTextures();
|
||||
final byte[] facePriorities = model.getFaceRenderPriorities();
|
||||
|
||||
float[][] u = model.getFaceTextureUCoordinates();
|
||||
float[][] v = model.getFaceTextureVCoordinates();
|
||||
|
||||
int len = 0;
|
||||
for (int face = 0; face < triangleCount; ++face)
|
||||
{
|
||||
int color1 = color1s[face];
|
||||
int color2 = color2s[face];
|
||||
int color3 = color3s[face];
|
||||
|
||||
if (color3 == -1)
|
||||
{
|
||||
color2 = color3 = color1;
|
||||
}
|
||||
else if (color3 == -2)
|
||||
{
|
||||
vertexBuffer.put(0, 0, 0, 0);
|
||||
vertexBuffer.put(0, 0, 0, 0);
|
||||
vertexBuffer.put(0, 0, 0, 0);
|
||||
|
||||
if (faceTextures != null)
|
||||
{
|
||||
uvBuffer.put(0, 0, 0, 0);
|
||||
uvBuffer.put(0, 0, 0, 0);
|
||||
uvBuffer.put(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
len += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
int packAlphaPriority = packAlphaPriority(faceTextures, transparencies, facePriorities, face);
|
||||
|
||||
int triangleA = trianglesX[face];
|
||||
int triangleB = trianglesY[face];
|
||||
int triangleC = trianglesZ[face];
|
||||
|
||||
vertexBuffer.put(vertexX[triangleA], vertexY[triangleA], vertexZ[triangleA], packAlphaPriority | color1);
|
||||
vertexBuffer.put(vertexX[triangleB], vertexY[triangleB], vertexZ[triangleB], packAlphaPriority | color2);
|
||||
vertexBuffer.put(vertexX[triangleC], vertexY[triangleC], vertexZ[triangleC], packAlphaPriority | color3);
|
||||
|
||||
if (faceTextures != null)
|
||||
{
|
||||
pushUvForFace(faceTextures, u, v, face, uvBuffer);
|
||||
}
|
||||
|
||||
len += 3;
|
||||
}
|
||||
|
||||
offset += len;
|
||||
if (model.getFaceTextures() != null)
|
||||
{
|
||||
uvoffset += len;
|
||||
}
|
||||
}
|
||||
|
||||
int pushFace(Model model, int face, boolean padUvs, GpuIntBuffer vertexBuffer, GpuFloatBuffer uvBuffer,
|
||||
int xOffset, int yOffset, int zOffset, int orientation)
|
||||
{
|
||||
final int[] vertexX = model.getVerticesX();
|
||||
final int[] vertexY = model.getVerticesY();
|
||||
final int[] vertexZ = model.getVerticesZ();
|
||||
|
||||
final int[] trianglesX = model.getTrianglesX();
|
||||
final int[] trianglesY = model.getTrianglesY();
|
||||
final int[] trianglesZ = model.getTrianglesZ();
|
||||
|
||||
final int[] color1s = model.getFaceColors1();
|
||||
final int[] color2s = model.getFaceColors2();
|
||||
final int[] color3s = model.getFaceColors3();
|
||||
|
||||
final byte[] transparencies = model.getTriangleTransparencies();
|
||||
final short[] faceTextures = model.getFaceTextures();
|
||||
final byte[] facePriorities = model.getFaceRenderPriorities();
|
||||
|
||||
int triangleA = trianglesX[face];
|
||||
int triangleB = trianglesY[face];
|
||||
int triangleC = trianglesZ[face];
|
||||
|
||||
int color1 = color1s[face];
|
||||
int color2 = color2s[face];
|
||||
int color3 = color3s[face];
|
||||
|
||||
int packedAlphaPriority = packAlphaPriority(faceTextures, transparencies, facePriorities, face);
|
||||
|
||||
int sin = 0, cos = 0;
|
||||
if (orientation != 0)
|
||||
{
|
||||
sin = Perspective.SINE[orientation];
|
||||
cos = Perspective.COSINE[orientation];
|
||||
}
|
||||
|
||||
if (color3 == -1)
|
||||
{
|
||||
color2 = color3 = color1;
|
||||
}
|
||||
else if (color3 == -2)
|
||||
{
|
||||
vertexBuffer.put(0, 0, 0, 0);
|
||||
vertexBuffer.put(0, 0, 0, 0);
|
||||
vertexBuffer.put(0, 0, 0, 0);
|
||||
|
||||
if (padUvs || faceTextures != null)
|
||||
{
|
||||
uvBuffer.put(0, 0, 0, 0f);
|
||||
uvBuffer.put(0, 0, 0, 0f);
|
||||
uvBuffer.put(0, 0, 0, 0f);
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
int a, b, c;
|
||||
|
||||
a = vertexX[triangleA];
|
||||
b = vertexY[triangleA];
|
||||
c = vertexZ[triangleA];
|
||||
|
||||
if (orientation != 0)
|
||||
{
|
||||
int x = c * sin + a * cos >> 16;
|
||||
int z = c * cos - a * sin >> 16;
|
||||
|
||||
a = x;
|
||||
c = z;
|
||||
}
|
||||
|
||||
a += xOffset;
|
||||
b += yOffset;
|
||||
c += zOffset;
|
||||
|
||||
vertexBuffer.put(a, b, c, packedAlphaPriority | color1);
|
||||
|
||||
a = vertexX[triangleB];
|
||||
b = vertexY[triangleB];
|
||||
c = vertexZ[triangleB];
|
||||
|
||||
if (orientation != 0)
|
||||
{
|
||||
int x = c * sin + a * cos >> 16;
|
||||
int z = c * cos - a * sin >> 16;
|
||||
|
||||
a = x;
|
||||
c = z;
|
||||
}
|
||||
|
||||
a += xOffset;
|
||||
b += yOffset;
|
||||
c += zOffset;
|
||||
|
||||
vertexBuffer.put(a, b, c, packedAlphaPriority | color2);
|
||||
|
||||
a = vertexX[triangleC];
|
||||
b = vertexY[triangleC];
|
||||
c = vertexZ[triangleC];
|
||||
|
||||
if (orientation != 0)
|
||||
{
|
||||
int x = c * sin + a * cos >> 16;
|
||||
int z = c * cos - a * sin >> 16;
|
||||
|
||||
a = x;
|
||||
c = z;
|
||||
}
|
||||
|
||||
a += xOffset;
|
||||
b += yOffset;
|
||||
c += zOffset;
|
||||
|
||||
vertexBuffer.put(a, b, c, packedAlphaPriority | color3);
|
||||
|
||||
float[][] u = model.getFaceTextureUCoordinates();
|
||||
float[][] v = model.getFaceTextureVCoordinates();
|
||||
if (padUvs || faceTextures != null)
|
||||
{
|
||||
pushUvForFace(faceTextures, u, v, face, uvBuffer);
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
private static int packAlphaPriority(short[] faceTextures, byte[] faceTransparencies, byte[] facePriorities, int face)
|
||||
{
|
||||
int alpha = 0;
|
||||
if (faceTransparencies != null && (faceTextures == null || faceTextures[face] == -1))
|
||||
{
|
||||
alpha = (faceTransparencies[face] & 0xFF) << 24;
|
||||
}
|
||||
int priority = 0;
|
||||
if (facePriorities != null)
|
||||
{
|
||||
priority = (facePriorities[face] & 0xff) << 16;
|
||||
}
|
||||
return alpha | priority;
|
||||
}
|
||||
|
||||
private static void pushUvForFace(short[] faceTextures, float[][] u, float[][] v, int face, GpuFloatBuffer uvBuffer)
|
||||
{
|
||||
float[] uf, vf;
|
||||
if (faceTextures != null && u != null && v != null && (uf = u[face]) != null && (vf = v[face]) != null)
|
||||
{
|
||||
float texture = faceTextures[face] + 1f;
|
||||
uvBuffer.put(texture, uf[0], vf[0], 0f);
|
||||
uvBuffer.put(texture, uf[1], vf[1], 0f);
|
||||
uvBuffer.put(texture, uf[2], vf[2], 0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
uvBuffer.put(0, 0, 0, 0);
|
||||
uvBuffer.put(0, 0, 0, 0);
|
||||
uvBuffer.put(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.gpu;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.jogamp.opengl.GL4;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.runelite.client.plugins.gpu.template.Template;
|
||||
|
||||
public class Shader
|
||||
{
|
||||
@VisibleForTesting
|
||||
final List<Unit> units = new ArrayList<>();
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@VisibleForTesting
|
||||
static class Unit
|
||||
{
|
||||
@Getter
|
||||
private final int type;
|
||||
|
||||
@Getter
|
||||
private final String filename;
|
||||
}
|
||||
|
||||
public Shader add(int type, String name)
|
||||
{
|
||||
units.add(new Unit(type, name));
|
||||
return this;
|
||||
}
|
||||
|
||||
public int compile(GL4 gl, Template template) throws ShaderException
|
||||
{
|
||||
int program = gl.glCreateProgram();
|
||||
int[] shaders = new int[units.size()];
|
||||
int i = 0;
|
||||
boolean ok = false;
|
||||
try
|
||||
{
|
||||
while (i < shaders.length)
|
||||
{
|
||||
Unit unit = units.get(i);
|
||||
int shader = gl.glCreateShader(unit.type);
|
||||
if (shader == 0)
|
||||
{
|
||||
throw new ShaderException("Unable to create shader of type " + unit.type);
|
||||
}
|
||||
|
||||
String source = template.load(unit.filename);
|
||||
gl.glShaderSource(shader, 1, new String[]{source}, null);
|
||||
gl.glCompileShader(shader);
|
||||
|
||||
if (GLUtil.glGetShader(gl, shader, gl.GL_COMPILE_STATUS) != gl.GL_TRUE)
|
||||
{
|
||||
String err = GLUtil.glGetShaderInfoLog(gl, shader);
|
||||
gl.glDeleteShader(shader);
|
||||
throw new ShaderException(err);
|
||||
}
|
||||
gl.glAttachShader(program, shader);
|
||||
shaders[i++] = shader;
|
||||
}
|
||||
|
||||
gl.glLinkProgram(program);
|
||||
|
||||
if (GLUtil.glGetProgram(gl, program, gl.GL_LINK_STATUS) == gl.GL_FALSE)
|
||||
{
|
||||
String err = GLUtil.glGetProgramInfoLog(gl, program);
|
||||
throw new ShaderException(err);
|
||||
}
|
||||
|
||||
gl.glValidateProgram(program);
|
||||
|
||||
if (GLUtil.glGetProgram(gl, program, gl.GL_VALIDATE_STATUS) == gl.GL_FALSE)
|
||||
{
|
||||
String err = GLUtil.glGetProgramInfoLog(gl, program);
|
||||
throw new ShaderException(err);
|
||||
}
|
||||
|
||||
ok = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
while (i > 0)
|
||||
{
|
||||
int shader = shaders[--i];
|
||||
gl.glDetachShader(program, shader);
|
||||
gl.glDeleteShader(shader);
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
gl.glDeleteProgram(program);
|
||||
}
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.plugins.gpu;
|
||||
|
||||
class ShaderException extends Exception
|
||||
{
|
||||
ShaderException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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.plugins.gpu;
|
||||
|
||||
import com.jogamp.opengl.GL4;
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Texture;
|
||||
import net.runelite.api.TextureProvider;
|
||||
|
||||
@Singleton
|
||||
@Slf4j
|
||||
class TextureManager
|
||||
{
|
||||
private static final float PERC_64 = 1f / 64f;
|
||||
private static final float PERC_128 = 1f / 128f;
|
||||
|
||||
private static final int TEXTURE_SIZE = 128;
|
||||
|
||||
int initTextureArray(TextureProvider textureProvider, GL4 gl)
|
||||
{
|
||||
if (!allTexturesLoaded(textureProvider))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
Texture[] textures = textureProvider.getTextures();
|
||||
|
||||
int textureArrayId = GLUtil.glGenTexture(gl);
|
||||
gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, textureArrayId);
|
||||
gl.glTexStorage3D(gl.GL_TEXTURE_2D_ARRAY, 8, gl.GL_RGBA8, TEXTURE_SIZE, TEXTURE_SIZE, textures.length);
|
||||
|
||||
gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST);
|
||||
gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MAG_FILTER, gl.GL_NEAREST);
|
||||
|
||||
gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE);
|
||||
|
||||
// Set brightness to 1.0d to upload unmodified textures to GPU
|
||||
double save = textureProvider.getBrightness();
|
||||
textureProvider.setBrightness(1.0d);
|
||||
|
||||
updateTextures(textureProvider, gl, textureArrayId);
|
||||
|
||||
textureProvider.setBrightness(save);
|
||||
|
||||
gl.glActiveTexture(gl.GL_TEXTURE1);
|
||||
gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, textureArrayId);
|
||||
gl.glGenerateMipmap(gl.GL_TEXTURE_2D_ARRAY);
|
||||
gl.glActiveTexture(gl.GL_TEXTURE0);
|
||||
|
||||
return textureArrayId;
|
||||
}
|
||||
|
||||
void setAnisotropicFilteringLevel(int textureArrayId, int level, GL4 gl)
|
||||
{
|
||||
gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, textureArrayId);
|
||||
|
||||
//level = 0 means no mipmaps and no anisotropic filtering
|
||||
if (level == 0)
|
||||
{
|
||||
gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST);
|
||||
}
|
||||
//level = 1 means with mipmaps but without anisotropic filtering GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT defaults to 1.0 which is off
|
||||
//level > 1 enables anisotropic filtering. It's up to the vendor what the values mean
|
||||
//Even if anisotropic filtering isn't supported, mipmaps will be enabled with any level >= 1
|
||||
else
|
||||
{
|
||||
// Set on GL_NEAREST_MIPMAP_LINEAR (bilinear filtering with mipmaps) since the pixel nature of the game means that nearest filtering
|
||||
// looks best for objects up close but allows linear filtering to resolve possible aliasing and noise with mipmaps from far away objects.
|
||||
gl.glTexParameteri(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MIN_FILTER, gl.GL_NEAREST_MIPMAP_LINEAR);
|
||||
}
|
||||
|
||||
if (gl.isExtensionAvailable("GL_EXT_texture_filter_anisotropic"))
|
||||
{
|
||||
final float maxSamples = GLUtil.glGetFloat(gl, gl.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT);
|
||||
//Clamp from 1 to max GL says it supports.
|
||||
final float anisoLevel = Math.max(1, Math.min(maxSamples, level));
|
||||
gl.glTexParameterf(gl.GL_TEXTURE_2D_ARRAY, gl.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoLevel);
|
||||
}
|
||||
}
|
||||
|
||||
void freeTextureArray(GL4 gl, int textureArrayId)
|
||||
{
|
||||
GLUtil.glDeleteTexture(gl, textureArrayId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all textures have been loaded and cached yet.
|
||||
*
|
||||
* @param textureProvider
|
||||
* @return
|
||||
*/
|
||||
private boolean allTexturesLoaded(TextureProvider textureProvider)
|
||||
{
|
||||
Texture[] textures = textureProvider.getTextures();
|
||||
if (textures == null || textures.length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int textureId = 0; textureId < textures.length; textureId++)
|
||||
{
|
||||
Texture texture = textures[textureId];
|
||||
if (texture != null)
|
||||
{
|
||||
int[] pixels = textureProvider.load(textureId);
|
||||
if (pixels == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateTextures(TextureProvider textureProvider, GL4 gl, int textureArrayId)
|
||||
{
|
||||
Texture[] textures = textureProvider.getTextures();
|
||||
|
||||
gl.glBindTexture(gl.GL_TEXTURE_2D_ARRAY, textureArrayId);
|
||||
|
||||
int cnt = 0;
|
||||
for (int textureId = 0; textureId < textures.length; textureId++)
|
||||
{
|
||||
Texture texture = textures[textureId];
|
||||
if (texture != null)
|
||||
{
|
||||
int[] srcPixels = textureProvider.load(textureId);
|
||||
if (srcPixels == null)
|
||||
{
|
||||
log.warn("No pixels for texture {}!", textureId);
|
||||
continue; // this can't happen
|
||||
}
|
||||
|
||||
++cnt;
|
||||
|
||||
if (srcPixels.length != TEXTURE_SIZE * TEXTURE_SIZE)
|
||||
{
|
||||
// The texture storage is 128x128 bytes, and will only work correctly with the
|
||||
// 128x128 textures from high detail mode
|
||||
log.warn("Texture size for {} is {}!", textureId, srcPixels.length);
|
||||
continue;
|
||||
}
|
||||
|
||||
byte[] pixels = convertPixels(srcPixels, TEXTURE_SIZE, TEXTURE_SIZE, TEXTURE_SIZE, TEXTURE_SIZE);
|
||||
ByteBuffer pixelBuffer = ByteBuffer.wrap(pixels);
|
||||
gl.glTexSubImage3D(gl.GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureId, TEXTURE_SIZE, TEXTURE_SIZE,
|
||||
1, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pixelBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("Uploaded textures {}", cnt);
|
||||
}
|
||||
|
||||
private static byte[] convertPixels(int[] srcPixels, int width, int height, int textureWidth, int textureHeight)
|
||||
{
|
||||
byte[] pixels = new byte[textureWidth * textureHeight * 4];
|
||||
|
||||
int pixelIdx = 0;
|
||||
int srcPixelIdx = 0;
|
||||
|
||||
int offset = (textureWidth - width) * 4;
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
int rgb = srcPixels[srcPixelIdx++];
|
||||
if (rgb != 0)
|
||||
{
|
||||
pixels[pixelIdx++] = (byte) (rgb >> 16);
|
||||
pixels[pixelIdx++] = (byte) (rgb >> 8);
|
||||
pixels[pixelIdx++] = (byte) rgb;
|
||||
pixels[pixelIdx++] = (byte) -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixelIdx += 4;
|
||||
}
|
||||
}
|
||||
pixelIdx += offset;
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animate the given texture
|
||||
*
|
||||
* @param texture
|
||||
* @param diff Number of elapsed client ticks since last animation
|
||||
*/
|
||||
void animate(Texture texture, int diff)
|
||||
{
|
||||
final int[] pixels = texture.getPixels();
|
||||
if (pixels == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final int animationSpeed = texture.getAnimationSpeed();
|
||||
final float uvdiff = pixels.length == 4096 ? PERC_64 : PERC_128;
|
||||
|
||||
float u = texture.getU();
|
||||
float v = texture.getV();
|
||||
|
||||
int offset = animationSpeed * diff;
|
||||
float d = (float) offset * uvdiff;
|
||||
|
||||
switch (texture.getAnimationDirection())
|
||||
{
|
||||
case 1:
|
||||
v -= d;
|
||||
if (v < 0f)
|
||||
{
|
||||
v += 1f;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
v += d;
|
||||
if (v > 1f)
|
||||
{
|
||||
v -= 1f;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
u -= d;
|
||||
if (u < 0f)
|
||||
{
|
||||
u += 1f;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
u += d;
|
||||
if (u > 1f)
|
||||
{
|
||||
u -= 1f;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
texture.setU(u);
|
||||
texture.setV(v);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
|
||||
* 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.gpu.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum AntiAliasingMode
|
||||
{
|
||||
DISABLED("Disabled", 0),
|
||||
MSAA_2("MSAA x2", 2),
|
||||
MSAA_4("MSAA x4", 4),
|
||||
MSAA_8("MSAA x8", 8),
|
||||
MSAA_16("MSAA x16", 16);
|
||||
|
||||
private final String name;
|
||||
private final int samples;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Ben Poulson <https://github.com/benpoulson>
|
||||
* 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.gpu.config;
|
||||
|
||||
public enum ColorBlindMode
|
||||
{
|
||||
NONE,
|
||||
PROTANOPE,
|
||||
DEUTERANOPE,
|
||||
TRITANOPE;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2019 logarrhytmic <https://github.com/logarrhythmic>
|
||||
* 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.gpu.config;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum UIScalingMode
|
||||
{
|
||||
NEAREST("Nearest Neighbor", 0),
|
||||
LINEAR("Bilinear", 0),
|
||||
MITCHELL("Bicubic (Mitchell)", 1),
|
||||
CATMULL_ROM("Bicubic (Catmull-Rom)", 2),
|
||||
XBR("xBR", 3);
|
||||
|
||||
private final String name;
|
||||
private final int mode;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.plugins.gpu.template;
|
||||
|
||||
import com.google.common.io.CharStreams;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class Template
|
||||
{
|
||||
private final List<Function<String, String>> resourceLoaders = new ArrayList<>();
|
||||
|
||||
public String process(String str)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String line : str.split("\r?\n"))
|
||||
{
|
||||
if (line.startsWith("#include "))
|
||||
{
|
||||
String resource = line.substring(9);
|
||||
String resourceStr = load(resource);
|
||||
sb.append(resourceStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(line).append('\n');
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String load(String filename)
|
||||
{
|
||||
for (Function<String, String> loader : resourceLoaders)
|
||||
{
|
||||
String value = loader.apply(filename);
|
||||
if (value != null)
|
||||
{
|
||||
return process(value);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public Template add(Function<String, String> fn)
|
||||
{
|
||||
resourceLoaders.add(fn);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Template addInclude(Class<?> clazz)
|
||||
{
|
||||
return add(f ->
|
||||
{
|
||||
InputStream is = clazz.getResourceAsStream(f);
|
||||
if (is != null)
|
||||
{
|
||||
return inputStreamToString(is);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private static String inputStreamToString(InputStream in)
|
||||
{
|
||||
try
|
||||
{
|
||||
return CharStreams.toString(new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,9 +26,7 @@ package net.runelite.client.plugins.xptracker;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import static net.runelite.client.plugins.xptracker.XpInfoBox.TWO_DECIMAL_FORMAT;
|
||||
|
||||
@Getter
|
||||
|
||||
@@ -500,7 +500,7 @@ public class XpTrackerPlugin extends Plugin
|
||||
MenuEntry menuEntry = menuEntries[menuEntries.length - 1] = new MenuEntry();
|
||||
menuEntry.setTarget(skillText);
|
||||
menuEntry.setOption(hasOverlay(skill) ? MENUOP_REMOVE_CANVAS_TRACKER : MENUOP_ADD_CANVAS_TRACKER);
|
||||
menuEntry.setParam0(event.getActionParam0());
|
||||
menuEntry.setActionParam(event.getActionParam());
|
||||
menuEntry.setParam1(widgetID);
|
||||
menuEntry.setType(MenuAction.RUNELITE.getId());
|
||||
|
||||
@@ -513,9 +513,6 @@ public class XpTrackerPlugin extends Plugin
|
||||
if (event.getMenuAction().getId() != MenuAction.RUNELITE.getId()
|
||||
|| TO_GROUP(event.getWidgetId()) != WidgetID.SKILLS_GROUP_ID)
|
||||
{
|
||||
System.out.println("opcode " + event.getMenuAction());
|
||||
System.out.println("id " + event.getMenuAction().getId());
|
||||
System.out.println("group " + TO_GROUP(event.getWidgetId()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -538,8 +535,6 @@ public class XpTrackerPlugin extends Plugin
|
||||
case MENUOP_REMOVE_CANVAS_TRACKER:
|
||||
removeOverlay(skill);
|
||||
break;
|
||||
default:
|
||||
System.out.println(event.getMenuOption());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -974,7 +974,7 @@ c 7 4
|
||||
#100
|
||||
R 19 19 21 20
|
||||
|
||||
// Cosmic entity's plane
|
||||
// Cosmic renderable's plane
|
||||
#040404
|
||||
r 32 75
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
57892EA7C7677AF80D1DA094F34C8A8738F2F4E5DDA2E861680F7D598664B216
|
||||
@@ -1,238 +0,0 @@
|
||||
.id 509
|
||||
.int_stack_count 12
|
||||
.string_stack_count 0
|
||||
.int_var_count 18
|
||||
.string_var_count 0
|
||||
iconst 0
|
||||
istore 12
|
||||
iload 11
|
||||
iconst 0
|
||||
if_icmpeq LABEL6
|
||||
jump LABEL19
|
||||
LABEL6:
|
||||
iload 2
|
||||
iconst 0
|
||||
if_icmpgt LABEL10
|
||||
jump LABEL19
|
||||
LABEL10:
|
||||
invoke 514
|
||||
iconst 1
|
||||
if_icmpeq LABEL14
|
||||
jump LABEL19
|
||||
LABEL14:
|
||||
iload 8
|
||||
iconst 15
|
||||
sconst "tabTextSpace" ;
|
||||
runelite_callback ;
|
||||
add
|
||||
istore 12
|
||||
jump LABEL21
|
||||
LABEL19:
|
||||
iload 8
|
||||
istore 12
|
||||
LABEL21:
|
||||
iconst -1
|
||||
istore 13
|
||||
iconst 0
|
||||
istore 14
|
||||
iconst 0
|
||||
istore 15
|
||||
iconst 0
|
||||
sconst "rowIndexInit" ;
|
||||
runelite_callback ;
|
||||
istore 16
|
||||
iconst 0
|
||||
istore 17
|
||||
LABEL31:
|
||||
iload 0
|
||||
iload 1
|
||||
if_icmplt LABEL35
|
||||
jump LABEL107
|
||||
LABEL35:
|
||||
iload 3
|
||||
iload 0
|
||||
cc_find
|
||||
iconst 1
|
||||
if_icmpeq LABEL41
|
||||
jump LABEL102
|
||||
LABEL41:
|
||||
iconst 95
|
||||
iload 0
|
||||
inv_getobj
|
||||
istore 13
|
||||
iload 13
|
||||
invoke 279
|
||||
iconst 1
|
||||
if_icmpeq LABEL50
|
||||
jump LABEL102
|
||||
LABEL50:
|
||||
iconst 0
|
||||
cc_sethide
|
||||
iload 13
|
||||
iconst 95
|
||||
iload 0
|
||||
inv_getnum
|
||||
iload 3
|
||||
iload 4
|
||||
iload 5
|
||||
iload 6
|
||||
iload 7
|
||||
invoke 278
|
||||
iload 15
|
||||
iconst 1
|
||||
add
|
||||
istore 15
|
||||
iload 12
|
||||
iload 17
|
||||
iconst 36
|
||||
multiply
|
||||
add
|
||||
istore 14
|
||||
iconst 51
|
||||
iload 16
|
||||
iconst 36
|
||||
iload 9
|
||||
add
|
||||
multiply
|
||||
add
|
||||
iload 14
|
||||
iconst 0
|
||||
iconst 0
|
||||
cc_setposition
|
||||
iload 14
|
||||
iconst 32
|
||||
add
|
||||
istore 14
|
||||
iload 16
|
||||
iload 10
|
||||
if_icmplt LABEL91
|
||||
jump LABEL96
|
||||
LABEL91:
|
||||
iload 16
|
||||
iconst 1
|
||||
add
|
||||
sconst "rowIndex" ;
|
||||
runelite_callback ;
|
||||
istore 16
|
||||
jump LABEL102
|
||||
LABEL96:
|
||||
iconst 0
|
||||
iload 17
|
||||
iconst 1
|
||||
add
|
||||
istore 17
|
||||
sconst "rowIndex" ;
|
||||
runelite_callback ;
|
||||
istore 16
|
||||
LABEL102:
|
||||
iload 0
|
||||
iconst 1
|
||||
add
|
||||
istore 0
|
||||
jump LABEL31
|
||||
LABEL107:
|
||||
iload 14
|
||||
iconst 0
|
||||
if_icmple LABEL111
|
||||
jump LABEL114
|
||||
LABEL111:
|
||||
iload 8
|
||||
iload 15
|
||||
return
|
||||
LABEL114:
|
||||
iload 11
|
||||
iconst 0
|
||||
if_icmpeq LABEL118
|
||||
jump LABEL189
|
||||
LABEL118:
|
||||
iload 16
|
||||
iconst 0
|
||||
if_icmpgt LABEL122
|
||||
jump LABEL166
|
||||
LABEL122:
|
||||
iload 3
|
||||
iconst 816
|
||||
iconst 9
|
||||
iconst 2
|
||||
multiply
|
||||
add
|
||||
iload 2
|
||||
add
|
||||
cc_find
|
||||
iconst 1
|
||||
if_icmpeq LABEL134
|
||||
jump LABEL166
|
||||
LABEL134:
|
||||
iconst 0
|
||||
sconst "hideTabText" ;
|
||||
runelite_callback ;
|
||||
cc_sethide
|
||||
iconst 51
|
||||
iload 16
|
||||
iconst 36
|
||||
iload 9
|
||||
add
|
||||
multiply
|
||||
add
|
||||
iload 14
|
||||
iconst 32
|
||||
sub
|
||||
iconst 0
|
||||
iconst 0
|
||||
cc_setposition
|
||||
iconst 8
|
||||
iload 16
|
||||
sub
|
||||
istore 16
|
||||
iload 16
|
||||
iconst 36
|
||||
multiply
|
||||
iload 16
|
||||
iconst 1
|
||||
sub
|
||||
iload 9
|
||||
multiply
|
||||
add
|
||||
iconst 32
|
||||
iconst 0
|
||||
iconst 0
|
||||
cc_setsize
|
||||
LABEL166:
|
||||
iload 12
|
||||
iload 8
|
||||
if_icmpgt LABEL170
|
||||
jump LABEL189
|
||||
LABEL170:
|
||||
iload 3
|
||||
iconst 816
|
||||
iconst 9
|
||||
add
|
||||
iload 2
|
||||
add
|
||||
iconst 1
|
||||
sub
|
||||
cc_find
|
||||
iconst 1
|
||||
if_icmpeq LABEL182
|
||||
jump LABEL189
|
||||
LABEL182:
|
||||
iconst 51
|
||||
iload 8
|
||||
iconst 0
|
||||
iconst 0
|
||||
cc_setposition
|
||||
iconst 0
|
||||
sconst "hideTabText" ;
|
||||
runelite_callback ;
|
||||
cc_sethide
|
||||
LABEL189:
|
||||
iload 14
|
||||
iload 2 ;
|
||||
sconst "newBankRow" ;
|
||||
runelite_callback ;
|
||||
pop_int ;
|
||||
iload 15
|
||||
return
|
||||
iconst 0
|
||||
iconst 0
|
||||
return
|
||||
@@ -1 +0,0 @@
|
||||
F7482938547BB43B14BCE44F35FC7555EBA9EF3A9D2E4AAEA916DF2855387F07
|
||||
@@ -1,36 +0,0 @@
|
||||
.id 510
|
||||
.int_stack_count 3
|
||||
.string_stack_count 0
|
||||
.int_var_count 3
|
||||
.string_var_count 0
|
||||
iload 1
|
||||
iconst 816
|
||||
iload 0
|
||||
add
|
||||
iconst 1
|
||||
sub
|
||||
cc_find
|
||||
iconst 1
|
||||
if_icmpeq LABEL10
|
||||
jump LABEL19
|
||||
LABEL10:
|
||||
iconst 51
|
||||
iload 2
|
||||
iconst 5
|
||||
add
|
||||
iconst 0
|
||||
iconst 0
|
||||
cc_setposition
|
||||
iconst 0
|
||||
sconst "hideLine" ;
|
||||
runelite_callback ;
|
||||
cc_sethide
|
||||
LABEL19:
|
||||
iload 2
|
||||
iconst 12
|
||||
sconst "lineSpace" ;
|
||||
runelite_callback ;
|
||||
add
|
||||
return
|
||||
iconst 0
|
||||
return
|
||||
@@ -836,8 +836,6 @@ LABEL729:
|
||||
iload 10
|
||||
iload 11
|
||||
iload 28
|
||||
sconst "addLastRow"
|
||||
runelite_callback
|
||||
iload 29
|
||||
iload 12
|
||||
iload 13
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
48C575A71CD8CDDBFC8A7C0D2F8915A5EE263731213FFC23BEFFF39B6441A9FF
|
||||
@@ -0,0 +1,121 @@
|
||||
.id 679
|
||||
.int_stack_count 21
|
||||
.string_stack_count 0
|
||||
.int_var_count 21
|
||||
.string_var_count 0
|
||||
iload 0
|
||||
cc_deleteall
|
||||
iload 0
|
||||
iconst 3
|
||||
iconst 0
|
||||
cc_create
|
||||
iconst 64
|
||||
iconst 64
|
||||
iconst 0
|
||||
iconst 0
|
||||
cc_setsize
|
||||
iconst 0
|
||||
iconst 0
|
||||
iconst 0
|
||||
iconst 0
|
||||
cc_setposition
|
||||
iconst 16777215
|
||||
cc_setcolour
|
||||
iconst 1
|
||||
cc_setfill
|
||||
iconst 255
|
||||
cc_settrans
|
||||
iload 0
|
||||
iconst 4
|
||||
iconst 1
|
||||
cc_create 1
|
||||
iconst 10
|
||||
iconst 15
|
||||
iconst 0
|
||||
iconst 0
|
||||
cc_setsize 1
|
||||
iconst 5
|
||||
iload 0
|
||||
if_getwidth
|
||||
iconst 15
|
||||
sub
|
||||
randominc
|
||||
add
|
||||
iconst 5
|
||||
iload 0
|
||||
if_getheight
|
||||
iconst 20
|
||||
sub
|
||||
randominc
|
||||
add
|
||||
iconst 0
|
||||
iconst 0
|
||||
cc_setposition 1
|
||||
iconst 1
|
||||
iconst 1
|
||||
iconst 0
|
||||
cc_settextalign 1
|
||||
iconst 496
|
||||
cc_settextfont 1
|
||||
iconst 16744192
|
||||
cc_setcolour 1
|
||||
iconst 0
|
||||
cc_settextshadow 1
|
||||
sconst ""
|
||||
cc_settext 1
|
||||
iconst 683
|
||||
iconst -2147483645
|
||||
iconst -2147483643
|
||||
iload 1
|
||||
clientclock
|
||||
iconst 5
|
||||
add
|
||||
sconst "Iiii"
|
||||
cc_setontimer 1
|
||||
iconst 684
|
||||
iconst 0
|
||||
iconst -2147483645
|
||||
iconst -2147483643
|
||||
cc_getid 1
|
||||
sconst "1Iii"
|
||||
cc_setonmouserepeat
|
||||
iconst 684
|
||||
iconst 1
|
||||
iconst -2147483645
|
||||
iconst -2147483643
|
||||
cc_getid 1
|
||||
sconst "1Iii"
|
||||
cc_setonmouseleave
|
||||
iconst 1
|
||||
sconst "Select"
|
||||
cc_setop
|
||||
iconst 685
|
||||
iload 1
|
||||
iload 2
|
||||
iload 3
|
||||
iload 4
|
||||
iload 5
|
||||
iload 6
|
||||
iload 7
|
||||
iload 8
|
||||
iload 9
|
||||
iload 10
|
||||
iload 11
|
||||
iload 12
|
||||
iload 13
|
||||
iload 14
|
||||
iload 15
|
||||
iload 16
|
||||
iload 17
|
||||
iload 18
|
||||
iload 19
|
||||
iload 20
|
||||
sconst "iiiIIIIIIIIIIIIIIIII"
|
||||
cc_setonop
|
||||
iload 0 ; button component id
|
||||
iload 1 ; 0-9
|
||||
sconst "bankpinButtonSetup"
|
||||
runelite_callback
|
||||
pop_int ; 0-9
|
||||
pop_int ; button component id
|
||||
return
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user