Addeds arrow api, quest helper plugin, changed raids etc (#81)

* add a skull timer

* fix the feature and check edge cases

* Add config option and a check for deadman worlds

* add support for NPE

* Add reskulling on players and the BH shop skull option

* Add skull notifier plugin

* move to idle notification plugin

* remove old plugin

* fix plugin and change default config option to false

* fix to try and fix travis

* should fix travis

* indentation fix and adds a method for the logic

* fix config

* fix config #2

* Stop a NPE from happening on log in

* actually fix NPE

* fix notifications on first tick

* Remove raids timer infobox and add tooltip to points widget

* Add get widget overlay

* client: add custom arrow API

Currently supports:
Minimap
Arbitrary World Point
NPCs (by ID, and multiple per arrow)
Objects (by ID, and multiple per arrow)

TODO: Add world map point support

* Add quest helper

* Add Npc Talk Step to quest helper

* Add Cooks Assistant quest helper

* Add Imp Catcher to quest helper

* Add Dig Step to quest helper

* Add X Marks The Spot to quest helper

* Adds back skull timer
This commit is contained in:
James
2019-04-23 17:36:42 -07:00
committed by Kyleeld
parent 90e5025acd
commit f8363b9c23
33 changed files with 2705 additions and 370 deletions

View File

@@ -66,6 +66,8 @@ import net.runelite.client.ui.RuneLiteSplashScreen;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.OverlayRenderer;
import net.runelite.client.ui.overlay.WidgetOverlay;
import net.runelite.client.ui.overlay.arrow.ArrowMinimapOverlay;
import net.runelite.client.ui.overlay.arrow.ArrowWorldOverlay;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.ui.overlay.infobox.InfoBoxOverlay;
import net.runelite.client.ui.overlay.tooltip.TooltipOverlay;
@@ -148,6 +150,12 @@ public class RuneLite
@Inject
private Provider<WorldMapOverlay> worldMapOverlay;
@Inject
private Provider<ArrowWorldOverlay> arrowWorldOverlay;
@Inject
private Provider<ArrowMinimapOverlay> arrowMinimapOverlay;
@Inject
private Provider<LootManager> lootManager;
@@ -323,6 +331,8 @@ public class RuneLite
overlayManager.add(infoBoxOverlay.get());
overlayManager.add(worldMapOverlay.get());
overlayManager.add(tooltipOverlay.get());
overlayManager.add(arrowWorldOverlay.get());
overlayManager.add(arrowMinimapOverlay.get());
}
// Start plugins

View File

@@ -0,0 +1,162 @@
package net.runelite.client.plugins.example;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.EnumSet;
import java.util.HashSet;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.ItemID;
import net.runelite.api.Point;
import net.runelite.api.SpriteID;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit;
import net.runelite.client.ui.overlay.arrow.ArrowPoint;
import net.runelite.client.ui.overlay.arrow.ArrowPointManager;
import net.runelite.client.ui.overlay.arrow.ArrowType;
@PluginDescriptor(
name = "ArrowTest",
developerPlugin = true
)
public class ExamplePlugin extends Plugin
{
@Inject
private ArrowPointManager arrowPointManager;
@Inject
private SpriteManager spriteManager;
@Inject
private ItemManager itemManager;
@Inject
private Client client;
@Inject
private ClientThread clientThread;
private boolean firstLogin = true;
@Override
public void startUp() throws Exception
{
clientThread.invokeLater(this::loadArrows);
}
@Override
public void shutDown()
{
arrowPointManager.clear();
}
public void loadArrows()
{
if (client.getGameState() != GameState.LOGGED_IN)
{
return;
}
BufferedImage i = spriteManager.getSprite(SpriteID.MINIMAP_GUIDE_ARROW_YELLOW, 0);
BufferedImage i3 = spriteManager.getSprite(SpriteID.RED_GUIDE_ARROW, 0);
AffineTransform at = new AffineTransform();
at.concatenate(AffineTransform.getScaleInstance(1, -1));
at.concatenate(AffineTransform.getTranslateInstance(0, -i3.getHeight()));
BufferedImage i4 = new BufferedImage(i3.getWidth(), i3.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = i4.createGraphics();
g.transform(at);
g.drawImage(i3, 0, 0, null);
g.dispose();
Point i3Offset = new Point(0, i3.getHeight() / -2);
ArrowPoint one = ArrowPoint.builder()
.worldPoint(new WorldPoint(1554, 3551, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, one);
ArrowPoint two = ArrowPoint.builder()
.worldPoint(new WorldPoint(1544, 3580, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, two);
ArrowPoint three = ArrowPoint.builder()
.worldPoint(new WorldPoint(1571, 3541, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, three);
HashSet<Integer> NPCs = new HashSet<>();
NPCs.add(6910);
ArrowPoint npcone = ArrowPoint.builder()
.worldPoint(new WorldPoint(1545, 3595, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.npcIDs(NPCs)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.NPC))
.build();
//arrowPointManager.add(this, npcone);
HashSet<Integer> NPCs2 = new HashSet<>();
NPCs2.add(6889);
NPCs2.add(6883);
NPCs2.add(6885);
ArrowPoint npctwo = ArrowPoint.builder()
.worldPoint(new WorldPoint(1551, 3561, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.worldImageOffset(new Point(0, -i4.getHeight()))
.npcIDs(NPCs2)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.NPC))
.build();
//arrowPointManager.add(this, npctwo);
HashSet<Integer> OBJs = new HashSet<>();
OBJs.add(STASHUnit.SHAYZIEN_WAR_TENT);
ArrowPoint objone = ArrowPoint.builder()
.worldPoint(new WorldPoint(1550, 3541, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.worldImageOffset(new Point(0, -i4.getHeight()))
.objectIDs(OBJs)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.OBJECT))
.build();
arrowPointManager.add(this, objone);
BufferedImage i5 = itemManager.getImage(ItemID.BONES);
ArrowPoint four = ArrowPoint.builder()
.worldPoint(new WorldPoint(1517, 3553, 0))
.minimapImage(i5)
.worldImage(i5)
.minimapImagePointToTarget(false)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, four);
}
}

View File

@@ -64,11 +64,33 @@ public interface IdleNotifierConfig extends Config
return true;
}
@ConfigItem(
position = 4,
keyName = "skullNotification",
name = "Skull Notification",
description = "Receive a notification when you skull."
)
default boolean showSkullNotification()
{
return false;
}
@ConfigItem(
position = 5,
keyName = "unskullNotification",
name = "Unskull Notification",
description = "Receive a notification when you unskull."
)
default boolean showUnskullNotification()
{
return false;
}
@ConfigItem(
keyName = "timeout",
name = "Idle Notification Delay (ms)",
description = "The notification delay after the player is idle",
position = 4
position = 6
)
default int getIdleNotificationDelay()
{
@@ -79,7 +101,7 @@ public interface IdleNotifierConfig extends Config
keyName = "hitpoints",
name = "Hitpoints Notification Threshold",
description = "The amount of hitpoints to send a notification at. A value of 0 will disable notification.",
position = 5
position = 7
)
default int getHitpointsThreshold()
{
@@ -90,7 +112,7 @@ public interface IdleNotifierConfig extends Config
keyName = "prayer",
name = "Prayer Notification Threshold",
description = "The amount of prayer points to send a notification at. A value of 0 will disable notification.",
position = 6
position = 8
)
default int getPrayerThreshold()
{
@@ -100,7 +122,7 @@ public interface IdleNotifierConfig extends Config
@ConfigItem(
keyName = "oxygen",
name = "Oxygen Notification Threshold",
position = 7,
position = 9,
description = "The amount of remaining oxygen to send a notification at. A value of 0 will disable notification."
)
default int getOxygenThreshold()
@@ -111,7 +133,7 @@ public interface IdleNotifierConfig extends Config
@ConfigItem(
keyName = "spec",
name = "Special Attack Energy Notification Threshold",
position = 8,
position = 10,
description = "The amount of spec energy reached to send a notification at. A value of 0 will disable notification."
)
default int getSpecEnergyThreshold()

View File

@@ -31,6 +31,7 @@ import java.awt.*;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import javax.inject.Inject;
import net.runelite.api.Actor;
@@ -44,8 +45,10 @@ import net.runelite.api.NPC;
import net.runelite.api.NPCComposition;
import net.runelite.api.Player;
import net.runelite.api.Skill;
import net.runelite.api.SkullIcon;
import net.runelite.api.VarPlayer;
import net.runelite.api.Varbits;
import net.runelite.api.WorldType;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
@@ -100,6 +103,8 @@ public class IdleNotifierPlugin extends Plugin
private Instant sixHourWarningTime;
private boolean ready;
private boolean lastInteractWasCombat;
private SkullIcon lastTickSkull = null;
private boolean isFirstTick = true;
@Provides
IdleNotifierConfig provideConfig(ConfigManager configManager)
@@ -327,9 +332,13 @@ public class IdleNotifierPlugin extends Plugin
{
case LOGIN_SCREEN:
resetTimers();
isFirstTick = true;
break;
case HOPPING:
isFirstTick = true;
ready = true;
break;
case LOGGING_IN:
case HOPPING:
case CONNECTION_LOST:
ready = true;
break;
@@ -381,6 +390,8 @@ public class IdleNotifierPlugin extends Plugin
@Subscribe
public void onGameTick(GameTick event)
{
skullNotifier();
final Player local = client.getLocalPlayer();
final Duration waitDuration = Duration.ofMillis(config.getIdleNotificationDelay());
lastCombatCountdown = Math.max(lastCombatCountdown - 1, 0);
@@ -666,4 +677,31 @@ public class IdleNotifierPlugin extends Plugin
lastInteract = null;
}
}
private void skullNotifier()
{
final Player local = client.getLocalPlayer();
SkullIcon currentTickSkull = local.getSkullIcon();
EnumSet worldTypes = client.getWorldType();
if (!(worldTypes.contains(WorldType.DEADMAN) || worldTypes.contains(WorldType.SEASONAL_DEADMAN)))
{
if (!isFirstTick)
{
if (config.showSkullNotification() && lastTickSkull == null && currentTickSkull == SkullIcon.SKULL)
{
notifier.notify("[" + local.getName() + "] is now skulled!");
}
else if (config.showUnskullNotification() && lastTickSkull == SkullIcon.SKULL && currentTickSkull == null)
{
notifier.notify("[" + local.getName() + "] is now unskulled!");
}
}
else
{
isFirstTick = false;
}
lastTickSkull = currentTickSkull;
}
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.InventoryID;
import net.runelite.api.Item;
public class ItemRequirement
{
@Getter
private int id;
@Getter
private int quantity;
private boolean equip;
public ItemRequirement(int id)
{
this(id, 1);
}
public ItemRequirement(int id, int quantity)
{
this.id = id;
this.quantity = quantity;
equip = false;
}
public ItemRequirement(int id, int quantity, boolean equip)
{
this(id, quantity);
this.equip = equip;
}
public boolean check(Client client)
{
Item[] items;
if (equip)
{
items = client.getItemContainer(InventoryID.EQUIPMENT).getItems();
}
else
{
items = client.getItemContainer(InventoryID.INVENTORY).getItems();
}
int tempQuantity = quantity;
for (Item item : items)
{
if (item.getId() == id)
{
if (item.getQuantity() >= tempQuantity)
{
return true;
}
else
{
tempQuantity -= item.getQuantity();
}
}
}
return false;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import net.runelite.api.Quest;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface QuestDescriptor
{
Quest quest();
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import com.google.inject.Binder;
import com.google.inject.CreationException;
import com.google.inject.Injector;
import com.google.inject.Module;
import java.util.Map;
import javax.inject.Inject;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.Client;
import net.runelite.api.Quest;
import net.runelite.api.QuestState;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.plugins.questhelper.steps.QuestStep;
public abstract class QuestHelper implements Module
{
@Inject
private Client client;
@Inject
private EventBus eventBus;
@Getter
protected QuestStep currentStep;
protected Map<Integer, QuestStep> steps;
protected Injector injector;
@Getter
@Setter
private Quest quest;
protected int var;
@Override
public void configure(Binder binder)
{
}
protected void startUp() throws Exception
{
steps = loadSteps();
instantiateSteps();
var = getVar();
startUpStep(var);
}
protected void shutDown() throws Exception
{
steps = null;
shutDownStep();
}
protected void startUpStep(int i) throws Exception
{
if (steps.containsKey(i))
{
QuestStep step = steps.get(i);
currentStep = step;
eventBus.register(currentStep);
currentStep.startUp();
}
else
{
currentStep = null;
}
}
protected void shutDownStep() throws Exception
{
if (currentStep != null)
{
eventBus.unregister(currentStep);
currentStep.shutDown();
currentStep = null;
}
}
protected void updateQuest() throws Exception
{
shutDownStep();
if (!isCompleted())
{
currentStep = steps.get(getVar());
currentStep.startUp();
}
else
{
currentStep = null;
}
}
protected void instantiateSteps()
{
for (QuestStep step : steps.values())
{
try
{
injector.injectMembers(step);
}
catch (CreationException ex)
{
ex.printStackTrace();
}
}
}
protected boolean isCompleted()
{
return (quest.getState(client) == QuestState.FINISHED);
}
protected int getVar()
{
return quest.getVar(client);
}
protected abstract Map<Integer, QuestStep> loadSteps();
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2016-2017, Seth <Sethtroll3@gmail.com>
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import javax.inject.Inject;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPriority;
import net.runelite.client.ui.overlay.components.ComponentConstants;
import net.runelite.client.ui.overlay.components.PanelComponent;
public class QuestHelperOverlay extends Overlay
{
public static final Color TITLED_CONTENT_COLOR = new Color(190, 190, 190);
private final QuestHelperPlugin plugin;
private final PanelComponent panelComponent = new PanelComponent();
@Inject
public QuestHelperOverlay(QuestHelperPlugin plugin)
{
this.plugin = plugin;
setPriority(OverlayPriority.LOW);
}
@Override
public Dimension render(Graphics2D graphics)
{
QuestHelper questHelper = plugin.getSelectedQuest();
if (questHelper == null || questHelper.getCurrentStep() == null)
{
return null;
}
panelComponent.getChildren().clear();
panelComponent.setPreferredSize(new Dimension(ComponentConstants.STANDARD_WIDTH, 0));
questHelper.getCurrentStep().makeOverlayHint(panelComponent, plugin);
return panelComponent.render(graphics);
}
}

View File

@@ -0,0 +1,263 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import com.google.inject.Binder;
import com.google.inject.CreationException;
import com.google.inject.Injector;
import com.google.inject.Module;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Quest;
import net.runelite.api.events.GameStateChanged;
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.RuneLite;
import net.runelite.client.eventbus.EventBus;
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.Text;
@PluginDescriptor(
name = "Quest Helper",
description = "Helps you with your quests"
)
@Slf4j
public class QuestHelperPlugin extends Plugin
{
@Inject
private Client client;
@Inject
private EventBus eventBus;
@Inject
private OverlayManager overlayManager;
@Inject
private QuestHelperOverlay questHelperOverlay;
@Inject
private QuestHelperWorldOverlay questHelperWorldOverlay;
private static final String QUEST_PACKAGE = "net.runelite.client.plugins.questhelper.quests";
private Map<String, QuestHelper> quests;
@Getter
private QuestHelper selectedQuest = null;
@Override
protected void startUp() throws IOException
{
if (client.getGameState() == GameState.LOGGED_IN)
{
if (quests == null)
{
quests = scanAndInstantiate(getClass().getClassLoader(), QUEST_PACKAGE);
}
}
overlayManager.add(questHelperOverlay);
overlayManager.add(questHelperWorldOverlay);
}
@Override
protected void shutDown() throws Exception
{
overlayManager.remove(questHelperOverlay);
overlayManager.remove(questHelperWorldOverlay);
quests = null;
shutDownQuest();
}
@Subscribe
public void onWidgetLoaded(WidgetLoaded event) throws Exception
{
int groupId = event.getGroupId();
if (groupId == WidgetID.DIARY_QUEST_GROUP_ID)
{
Widget widget = client.getWidget(WidgetInfo.DIARY_QUEST_WIDGET_TITLE);
String questname = Text.removeTags(widget.getText());
if (quests.containsKey(questname))
{
QuestHelper widgetQuest = quests.get(questname);
if (selectedQuest == null || !selectedQuest.equals(widgetQuest))
{
shutDownQuest();
startUpQuest(widgetQuest);
}
}
}
}
@Subscribe
public void onGameStateChanged(GameStateChanged event) throws IOException
{
if (event.getGameState() == GameState.LOGGED_IN)
{
if (quests == null)
{
quests = scanAndInstantiate(getClass().getClassLoader(), QUEST_PACKAGE);
}
}
}
@Subscribe
public void onVarbitChanged(VarbitChanged event) throws Exception
{
if (selectedQuest != null && selectedQuest.var != selectedQuest.getVar())
{
selectedQuest.var = selectedQuest.getVar();
selectedQuest.updateQuest();
if (selectedQuest.getCurrentStep() == null)
{
shutDownQuest();
}
}
}
private void startUpQuest(QuestHelper questHelper) throws Exception
{
if (!questHelper.isCompleted())
{
selectedQuest = questHelper;
eventBus.register(selectedQuest);
selectedQuest.startUp();
}
else
{
selectedQuest = null;
}
}
private void shutDownQuest() throws Exception
{
if (selectedQuest != null)
{
selectedQuest.shutDown();
eventBus.unregister(selectedQuest);
selectedQuest = null;
}
}
Map<String, QuestHelper> scanAndInstantiate(ClassLoader classLoader, String packageName) throws IOException
{
Map<Quest, Class<? extends QuestHelper>> quests = new HashMap<>();
Map<String, QuestHelper> scannedQuests = new HashMap<>();
ClassPath classPath = ClassPath.from(classLoader);
ImmutableSet<ClassPath.ClassInfo> classes = packageName == null ? classPath.getAllClasses()
: classPath.getTopLevelClassesRecursive(packageName);
for (ClassPath.ClassInfo classInfo : classes)
{
Class<?> clazz = classInfo.load();
QuestDescriptor questDescriptor = clazz.getAnnotation(QuestDescriptor.class);
if (questDescriptor == null)
{
if (clazz.getSuperclass() == QuestHelper.class)
{
log.warn("Class {} is a quest helper, but has no quest descriptor",
clazz);
}
continue;
}
if (clazz.getSuperclass() != QuestHelper.class)
{
log.warn("Class {} has quest descriptor, but is not a quest helper",
clazz);
continue;
}
Class<QuestHelper> questClass = (Class<QuestHelper>) clazz;
quests.put(questDescriptor.quest(), questClass);
}
for (Map.Entry<Quest, Class<? extends QuestHelper>> questClazz : quests.entrySet())
{
QuestHelper questHelper;
try
{
questHelper = instantiate((Class<QuestHelper>) questClazz.getValue(), questClazz.getKey());
}
catch (QuestInstantiationException ex)
{
log.warn("Error instantiating quest helper!", ex);
continue;
}
scannedQuests.put(questClazz.getKey().getName(), questHelper);
}
return scannedQuests;
}
private QuestHelper instantiate(Class<QuestHelper> clazz, Quest quest) throws QuestInstantiationException
{
QuestHelper questHelper;
try
{
questHelper = clazz.newInstance();
questHelper.setQuest(quest);
}
catch (InstantiationException | IllegalAccessException ex)
{
throw new QuestInstantiationException(ex);
}
try
{
Module questModule = (Binder binder) ->
{
binder.bind(clazz).toInstance(questHelper);
binder.install(questHelper);
};
Injector questInjector = RuneLite.getInjector().createChildInjector(questModule);
questInjector.injectMembers(questHelper);
questHelper.injector = questInjector;
}
catch (CreationException ex)
{
throw new QuestInstantiationException(ex);
}
log.debug("Loaded quest helper {}", clazz.getSimpleName());
return questHelper;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2018, Morgan Lewis <https://github.com/MESLewis>
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
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;
import net.runelite.client.util.ImageUtil;
public class QuestHelperWorldMapPoint extends WorldMapPoint
{
private final BufferedImage questWorldImage;
private final Point questWorldImagePoint;
private final BufferedImage questImage;
public QuestHelperWorldMapPoint(final WorldPoint worldPoint, BufferedImage image)
{
super(worldPoint, null);
BufferedImage mapArrow = ImageUtil.getResourceStreamFromClass(getClass(), "/util/clue_arrow.png");
questWorldImage = new BufferedImage(mapArrow.getWidth(), mapArrow.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics graphics = questWorldImage.getGraphics();
graphics.drawImage(mapArrow, 0, 0, null);
int buffer = mapArrow.getWidth() / 2 - image.getWidth() / 2;
buffer = buffer < 0 ? 0 : buffer;
graphics.drawImage(image, buffer, buffer, null);
questWorldImagePoint = new Point(questWorldImage.getWidth() / 2, questWorldImage.getHeight());
this.questImage = image;
this.setSnapToEdge(true);
this.setJumpOnClick(true);
this.setImage(questWorldImage);
this.setImagePoint(questWorldImagePoint);
}
@Override
public void onEdgeSnap()
{
this.setImage(questImage);
this.setImagePoint(null);
}
@Override
public void onEdgeUnsnap()
{
this.setImage(questWorldImage);
this.setImagePoint(questWorldImagePoint);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import javax.inject.Inject;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
public class QuestHelperWorldOverlay extends Overlay
{
public static final int IMAGE_Z_OFFSET = 30;
public static final Color CLICKBOX_BORDER_COLOR = Color.CYAN;
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 QuestHelperPlugin plugin;
@Inject
public QuestHelperWorldOverlay(QuestHelperPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
this.plugin = plugin;
}
@Override
public Dimension render(Graphics2D graphics)
{
QuestHelper quest = plugin.getSelectedQuest();
if (quest != null && quest.getCurrentStep() != null)
{
quest.getCurrentStep().makeWorldOverlayHint(graphics, plugin);
}
return null;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper;
public class QuestInstantiationException extends Exception
{
public QuestInstantiationException(Throwable cause)
{
super(cause);
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.quests.cooksassistant;
import java.util.HashMap;
import java.util.Map;
import net.runelite.api.ItemID;
import net.runelite.api.NpcID;
import net.runelite.api.Quest;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.questhelper.ItemRequirement;
import net.runelite.client.plugins.questhelper.QuestHelper;
import net.runelite.client.plugins.questhelper.steps.NpcTalkStep;
import net.runelite.client.plugins.questhelper.steps.QuestStep;
import net.runelite.client.plugins.questhelper.QuestDescriptor;
@QuestDescriptor(
quest = Quest.COOKS_ASSISTANT
)
public class CooksAssistant extends QuestHelper
{
@Override
protected Map<Integer, QuestStep> loadSteps()
{
Map<Integer, QuestStep> steps = new HashMap<>();
steps.put(0, new NpcTalkStep(this, NpcID.COOK_4626, new WorldPoint(3206, 3214, 0),
"Give the Cook in Lumbridge Castle's kitchen the required items to finish the quest.",
new ItemRequirement(ItemID.BUCKET_OF_MILK), new ItemRequirement(ItemID.POT_OF_FLOUR),
new ItemRequirement(ItemID.EGG)));
steps.put(1, steps.get(0));
return steps;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.quests.impcatcher;
import java.util.HashMap;
import java.util.Map;
import net.runelite.api.ItemID;
import net.runelite.api.NpcID;
import net.runelite.api.Quest;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.questhelper.ItemRequirement;
import net.runelite.client.plugins.questhelper.QuestHelper;
import net.runelite.client.plugins.questhelper.steps.NpcTalkStep;
import net.runelite.client.plugins.questhelper.steps.QuestStep;
import net.runelite.client.plugins.questhelper.QuestDescriptor;
@QuestDescriptor(
quest = Quest.IMP_CATCHER
)
public class ImpCatcher extends QuestHelper
{
@Override
protected Map<Integer, QuestStep> loadSteps()
{
Map<Integer, QuestStep> steps = new HashMap<>();
steps.put(0, new NpcTalkStep(this, NpcID.WIZARD_MIZGOG, new WorldPoint(3103, 3163, 2),
"Talk to Wizard Mizgog on the top floor of the Wizards' Tower with the required items to finish the quest.",
new ItemRequirement(ItemID.BLACK_BEAD), new ItemRequirement(ItemID.WHITE_BEAD),
new ItemRequirement(ItemID.RED_BEAD), new ItemRequirement(ItemID.YELLOW_BEAD)));
steps.put(1, steps.get(0));
return steps;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.quests.xmarksthespot;
import java.util.HashMap;
import java.util.Map;
import net.runelite.api.ItemID;
import net.runelite.api.NpcID;
import net.runelite.api.Quest;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.questhelper.ItemRequirement;
import net.runelite.client.plugins.questhelper.QuestHelper;
import net.runelite.client.plugins.questhelper.steps.DigStep;
import net.runelite.client.plugins.questhelper.steps.NpcTalkStep;
import net.runelite.client.plugins.questhelper.steps.QuestStep;
import net.runelite.client.plugins.questhelper.QuestDescriptor;
@QuestDescriptor(
quest = Quest.X_MARKS_THE_SPOT
)
public class XMarksTheSpot extends QuestHelper
{
@Override
protected Map<Integer, QuestStep> loadSteps()
{
Map<Integer, QuestStep> steps = new HashMap<>();
steps.put(0, new NpcTalkStep(this, NpcID.VEOS_8484, new WorldPoint(3228, 3242, 0),
"Talk to Veos in The Sheared Ram pub in Lumbridge to start the quest."));
steps.put(1, steps.get(0));
steps.put(2, new DigStep(this, new WorldPoint(3230, 3209, 0),
"Dig north of Bob's Brilliant Axes, on the west side of the plant against the wall of his house.",
new ItemRequirement(ItemID.TREASURE_SCROLL)));
steps.put(3, new DigStep(this, new WorldPoint(3203, 3212, 0),
"Dig behind Lumbridge Castle, just outside the kitchen door.",
new ItemRequirement(ItemID.TREASURE_SCROLL_23068)));
steps.put(4, new DigStep(this, new WorldPoint(3109, 3264, 0),
"Dig north-west of the Draynor Village jail, just by the wheat farm.",
new ItemRequirement(ItemID.MYSTERIOUS_ORB_23069)));
steps.put(5, new DigStep(this, new WorldPoint(3078, 3259, 0),
"Dig in the pig pen just west where Martin the Master Gardener is.",
new ItemRequirement(ItemID.TREASURE_SCROLL_23070)));
steps.put(6, new NpcTalkStep(this, NpcID.VEOS_8484, new WorldPoint(3054, 3245, 0),
"Talk to Veos directly south of the Rusty Anchor Inn in Port Sarim to finish the quest.",
new ItemRequirement(ItemID.ANCIENT_CASKET)));
steps.put(7, new NpcTalkStep(this, NpcID.VEOS_8484, new WorldPoint(3054, 3245, 0),
"Talk to Veos directly south of the Rusty Anchor Inn in Port Sarim to finish the quest."));
return steps;
}
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.steps;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.ItemID;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.questhelper.ItemRequirement;
import net.runelite.client.plugins.questhelper.QuestHelper;
import net.runelite.client.plugins.questhelper.QuestHelperPlugin;
import net.runelite.client.plugins.questhelper.QuestHelperWorldMapPoint;
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.worldmap.WorldMapPointManager;
public class DigStep extends QuestStep
{
@Inject
Client client;
@Inject
ItemManager itemManager;
@Inject
WorldMapPointManager worldMapPointManager;
private final WorldPoint worldPoint;
private final List<ItemRequirement> itemRequirements = new ArrayList<>();
public DigStep(QuestHelper questHelper, WorldPoint worldPoint, String text, ItemRequirement... itemRequirements)
{
super(questHelper, text);
this.worldPoint = worldPoint;
this.itemRequirements.add(0, new ItemRequirement(ItemID.SPADE));
Collections.addAll(this.itemRequirements, itemRequirements);
}
@Override
public void startUp() throws Exception
{
worldMapPointManager.add(new QuestHelperWorldMapPoint(worldPoint, getQuestImage()));
}
@Override
public void shutDown() throws Exception
{
worldMapPointManager.removeIf(QuestHelperWorldMapPoint.class::isInstance);
}
@Override
public void makeOverlayHint(PanelComponent panelComponent, QuestHelperPlugin plugin)
{
super.makeOverlayHint(panelComponent, plugin);
panelComponent.getChildren().add(LineComponent.builder().left("Required Items:").build());
for (ItemRequirement itemRequirement : itemRequirements)
{
String text = itemRequirement.getQuantity() + " x " + itemManager.getItemComposition(itemRequirement.getId()).getName();
Color color;
if (itemRequirement.check(client))
{
color = Color.GREEN;
}
else
{
color = Color.RED;
}
panelComponent.getChildren().add(LineComponent.builder()
.left(text)
.leftColor(color)
.build());
}
}
@Override
public void makeWorldOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin)
{
LocalPoint localLocation = LocalPoint.fromWorld(client, worldPoint);
if (localLocation == null)
{
return;
}
OverlayUtil.renderTileOverlay(client, graphics, localLocation, getSpadeImage(), Color.ORANGE);
}
private BufferedImage getSpadeImage()
{
return itemManager.getImage(ItemID.SPADE);
}
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.steps;
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.NpcSpawned;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.ItemManager;
import net.runelite.client.plugins.questhelper.ItemRequirement;
import net.runelite.client.plugins.questhelper.QuestHelper;
import net.runelite.client.plugins.questhelper.QuestHelperPlugin;
import net.runelite.client.plugins.questhelper.QuestHelperWorldMapPoint;
import static net.runelite.client.plugins.questhelper.QuestHelperWorldOverlay.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.worldmap.WorldMapPointManager;
public class NpcTalkStep extends QuestStep
{
@Inject
protected Client client;
@Inject
protected ItemManager itemManager;
@Inject
protected WorldMapPointManager worldMapPointManager;
private int npcID;
private WorldPoint worldPoint;
private List<NPC> npcsToHighlight = new ArrayList<>();
List<ItemRequirement> itemRequirements;
public NpcTalkStep(QuestHelper questHelper, int npcID, WorldPoint worldPoint, String text, ItemRequirement... itemRequirements)
{
super(questHelper, text);
this.npcID = npcID;
this.worldPoint = worldPoint;
this.itemRequirements = Arrays.asList(itemRequirements);
}
@Override
public void startUp() throws Exception
{
for (NPC npc : client.getNpcs())
{
if (npcID == npc.getId())
{
npcsToHighlight.add(npc);
}
}
worldMapPointManager.add(new QuestHelperWorldMapPoint(worldPoint, getQuestImage()));
}
@Override
public void shutDown() throws Exception
{
npcsToHighlight.clear();
worldMapPointManager.removeIf(QuestHelperWorldMapPoint.class::isInstance);
}
@Subscribe
public void onNpcSpawned(NpcSpawned event)
{
if (event.getNpc().getId() == npcID)
{
npcsToHighlight.add(event.getNpc());
}
}
@Subscribe
public void onNpcDespawned(NpcDespawned event)
{
if (npcsToHighlight.contains(event.getNpc()))
{
npcsToHighlight.remove(event.getNpc());
}
}
@Override
public void makeOverlayHint(PanelComponent panelComponent, QuestHelperPlugin plugin)
{
super.makeOverlayHint(panelComponent, plugin);
if (itemRequirements.isEmpty())
{
return;
}
panelComponent.getChildren().add(LineComponent.builder().left("Required Items:").build());
for (ItemRequirement itemRequirement : itemRequirements)
{
String text = itemRequirement.getQuantity() + " x " + itemManager.getItemComposition(itemRequirement.getId()).getName();
Color color;
if (itemRequirement.check(client))
{
color = Color.GREEN;
}
else
{
color = Color.RED;
}
panelComponent.getChildren().add(LineComponent.builder()
.left(text)
.leftColor(color)
.build());
}
}
@Override
public void makeWorldOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin)
{
if (!worldPoint.isInScene(client))
{
return;
}
if (npcsToHighlight.isEmpty())
{
return;
}
for (NPC npc : npcsToHighlight)
{
OverlayUtil.renderActorOverlayImage(graphics, npc, getQuestImage(), Color.CYAN, IMAGE_Z_OFFSET);
}
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2019, Trevor <https://github.com/Trevor159>
* 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.questhelper.steps;
import com.google.inject.Binder;
import com.google.inject.Inject;
import com.google.inject.Module;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import net.runelite.api.SpriteID;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.plugins.questhelper.QuestHelper;
import static net.runelite.client.plugins.questhelper.QuestHelperOverlay.TITLED_CONTENT_COLOR;
import net.runelite.client.plugins.questhelper.QuestHelperPlugin;
import net.runelite.client.ui.overlay.components.LineComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.ui.overlay.components.TitleComponent;
public abstract class QuestStep implements Module
{
@Inject
SpriteManager spriteManager;
private final String text;
private final QuestHelper questHelper;
public QuestStep(QuestHelper questHelper, String text)
{
this.text = text;
this.questHelper = questHelper;
}
@Override
public void configure(Binder binder)
{
}
public void startUp() throws Exception
{
}
public void shutDown() throws Exception
{
}
public void makeOverlayHint(PanelComponent panelComponent, QuestHelperPlugin plugin)
{
panelComponent.getChildren().add(TitleComponent.builder().text(questHelper.getQuest().getName()).build());
panelComponent.getChildren().add(LineComponent.builder().left("Step:").build());
panelComponent.getChildren().add(LineComponent.builder()
.left(text)
.leftColor(TITLED_CONTENT_COLOR)
.build());
}
public abstract void makeWorldOverlayHint(Graphics2D graphics, QuestHelperPlugin plugin);
public BufferedImage getQuestImage()
{
return spriteManager.getSprite(SpriteID.TAB_QUESTS, 0);
}
}

View File

@@ -38,8 +38,8 @@ public interface RaidsConfig extends Config
@ConfigItem(
position = 0,
keyName = "raidsTimer",
name = "Display elapsed raid time",
description = "Display elapsed raid time"
name = "Level time tooltip",
description = "Displays your level times as a tooltip on the points overlay"
)
default boolean raidsTimer()
{

View File

@@ -31,7 +31,6 @@ import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.text.DecimalFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -57,11 +56,13 @@ import net.runelite.api.Tile;
import net.runelite.api.VarPlayer;
import net.runelite.api.Varbits;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.ClientTick;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.VarbitChanged;
import net.runelite.api.events.WidgetHiddenChanged;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.chat.ChatColorType;
import net.runelite.client.chat.ChatMessageBuilder;
@@ -81,9 +82,13 @@ import net.runelite.client.plugins.raids.solver.RotationSolver;
import net.runelite.client.ui.DrawManager;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.ui.overlay.WidgetOverlay;
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
import net.runelite.client.ui.overlay.tooltip.Tooltip;
import net.runelite.client.ui.overlay.tooltip.TooltipManager;
import net.runelite.client.util.Text;
import net.runelite.client.util.HotkeyListener;
import org.apache.commons.lang3.StringUtils;
@PluginDescriptor(
name = "Chambers Of Xeric",
@@ -95,9 +100,6 @@ import net.runelite.client.util.HotkeyListener;
public class RaidsPlugin extends Plugin
{
private static final int LOBBY_PLANE = 3;
private static final String RAID_START_MESSAGE = "The raid has begun!";
private static final String LEVEL_COMPLETE_MESSAGE = "level complete!";
private static final String RAID_COMPLETE_MESSAGE = "Congratulations - your raid is complete!";
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###.##");
static final DecimalFormat POINTS_FORMAT = new DecimalFormat("#,###");
private static final String SPLIT_REGEX = "\\s*,\\s*";
@@ -106,6 +108,8 @@ public class RaidsPlugin extends Plugin
@Inject
private ItemManager itemManager;
private static final Pattern LEVEL_COMPLETE_REGEX = Pattern.compile("(.+) level complete! Duration: ([0-9:]+)");
private static final Pattern RAID_COMPLETE_REGEX = Pattern.compile("Congratulations - your raid is complete! Duration: ([0-9:]+)");
@Inject
private ChatMessageManager chatMessageManager;
@@ -146,6 +150,9 @@ public class RaidsPlugin extends Plugin
@Inject
private KeyManager keyManager;
@Inject
private TooltipManager tooltipManager;
@Getter
private final ArrayList<String> roomWhitelist = new ArrayList<>();
@@ -167,7 +174,12 @@ public class RaidsPlugin extends Plugin
@Getter
private boolean inRaidChambers;
private RaidsTimer timer;
private int upperTime = -1;
private int middleTime = -1;
private int lowerTime = -1;
private int raidTime = -1;
private WidgetOverlay widgetOverlay;
private String tooltip;
@Provides
RaidsConfig provideConfig(ConfigManager configManager)
@@ -188,6 +200,7 @@ public class RaidsPlugin extends Plugin
overlayManager.add(pointsOverlay);
updateLists();
clientThread.invokeLater(() -> checkRaidPresence(true));
widgetOverlay = overlayManager.getWidgetOverlay(WidgetInfo.RAIDS_POINTS_INFOBOX);
}
@Override
@@ -195,16 +208,16 @@ public class RaidsPlugin extends Plugin
{
overlayManager.remove(overlay);
overlayManager.remove(pointsOverlay);
infoBoxManager.removeInfoBox(timer);
inRaidChambers = false;
widgetOverlay = null;
raid = null;
timer = null;
final Widget widget = client.getWidget(WidgetInfo.RAIDS_POINTS_INFOBOX);
if (widget != null)
{
widget.setHidden(false);
}
reset();
}
@Subscribe
@@ -215,12 +228,6 @@ public class RaidsPlugin extends Plugin
return;
}
if (event.getKey().equals("raidsTimer"))
{
updateInfoBoxState();
return;
}
updateLists();
clientThread.invokeLater(() -> checkRaidPresence(true));
}
@@ -253,25 +260,33 @@ public class RaidsPlugin extends Plugin
if (inRaidChambers && event.getType() == ChatMessageType.FRIENDSCHATNOTIFICATION)
{
String message = Text.removeTags(event.getMessage());
Matcher matcher;
if (config.raidsTimer() && message.startsWith(RAID_START_MESSAGE))
matcher = LEVEL_COMPLETE_REGEX.matcher(message);
if (matcher.find())
{
timer = new RaidsTimer(spriteManager.getSprite(TAB_QUESTS_BROWN_RAIDING_PARTY, 0), this, Instant.now());
infoBoxManager.addInfoBox(timer);
}
if (timer != null && message.contains(LEVEL_COMPLETE_MESSAGE))
{
timer.timeFloor();
}
if (message.startsWith(RAID_COMPLETE_MESSAGE))
{
if (timer != null)
String floor = matcher.group(1);
int time = timeToSeconds(matcher.group(2));
if (floor.equals("Upper"))
{
timer.timeOlm();
timer.setStopped(true);
upperTime = time;
}
else if (floor.equals("Middle"))
{
middleTime = time;
}
else if (floor.equals("Lower"))
{
lowerTime = time;
}
updateTooltip();
}
matcher = RAID_COMPLETE_REGEX.matcher(message);
if (matcher.find())
{
raidTime = timeToSeconds(matcher.group(1));
updateTooltip();
if (config.pointsMessage())
{
@@ -306,6 +321,23 @@ public class RaidsPlugin extends Plugin
}
}
@Subscribe
public void onClientTick(ClientTick event)
{
if (!config.raidsTimer()
|| !client.getGameState().equals(GameState.LOGGED_IN)
|| tooltip == null)
{
return;
}
final Point mousePosition = client.getMouseCanvasPosition();
if (widgetOverlay.getBounds().contains(mousePosition.getX(), mousePosition.getY()))
{
tooltipManager.add(new Tooltip(tooltip));
}
}
private void checkRaidPresence(boolean force)
{
if (client.getGameState() != GameState.LOGGED_IN)
@@ -318,7 +350,6 @@ public class RaidsPlugin extends Plugin
if (force || inRaidChambers != setting)
{
inRaidChambers = setting;
updateInfoBoxState();
if (inRaidChambers)
{
@@ -343,9 +374,14 @@ public class RaidsPlugin extends Plugin
overlay.setScoutOverlayShown(true);
sendRaidLayoutMessage();
}
else if (!config.scoutOverlayAtBank())
else
{
overlay.setScoutOverlayShown(false);
if (!config.scoutOverlayAtBank())
{
overlay.setScoutOverlayShown(false);
}
reset();
}
}
@@ -378,30 +414,6 @@ public class RaidsPlugin extends Plugin
.build());
}
private void updateInfoBoxState()
{
if (timer == null)
{
return;
}
if (inRaidChambers && config.raidsTimer())
{
if (!infoBoxManager.getInfoBoxes().contains(timer))
{
infoBoxManager.addInfoBox(timer);
}
}
else
{
infoBoxManager.removeInfoBox(timer);
}
if (!inRaidChambers)
{
timer = null;
}
}
private void updateLists()
{
@@ -700,4 +712,94 @@ public class RaidsPlugin extends Plugin
return room;
}
public void reset()
{
raid = null;
upperTime = -1;
middleTime = -1;
lowerTime = -1;
raidTime = -1;
tooltip = null;
}
private int timeToSeconds(String s)
{
int seconds = -1;
String[] split = s.split(":");
if (split.length == 2)
{
seconds = Integer.parseInt(split[0]) * 60 + Integer.parseInt(split[1]);
}
if (split.length == 3)
{
seconds = Integer.parseInt(split[0]) * 3600 + Integer.parseInt(split[1]) * 60 + Integer.parseInt(split[2]);
}
return seconds;
}
private String secondsToTime(int seconds)
{
StringBuilder builder = new StringBuilder();
if (seconds >= 3600)
{
builder.append((int)Math.floor(seconds / 3600) + ";");
}
seconds %= 3600;
if (builder.toString().equals(""))
{
builder.append((int)Math.floor(seconds / 60));
}
else
{
builder.append(StringUtils.leftPad(String.valueOf((int)Math.floor(seconds / 60)), 2, '0'));
}
builder.append(":");
seconds %= 60;
builder.append(StringUtils.leftPad(String.valueOf(seconds), 2, '0'));
return builder.toString();
}
private void updateTooltip()
{
StringBuilder builder = new StringBuilder();
if (upperTime == -1)
{
tooltip = null;
return;
}
builder.append("Upper level: " + secondsToTime(upperTime));
if (middleTime == -1)
{
if (lowerTime == -1)
{
tooltip = builder.toString();
return;
}
else
{
builder.append("</br>Lower level: " + secondsToTime(lowerTime - upperTime));
}
}
else
{
builder.append("</br>Middle level: " + secondsToTime(middleTime - upperTime));
if (lowerTime == -1)
{
tooltip = builder.toString();
return;
}
else
{
builder.append("</br>Lower level: " + secondsToTime(lowerTime - middleTime));
}
}
if (raidTime == -1)
{
tooltip = builder.toString();
return;
}
builder.append("</br>Olm: " + secondsToTime(raidTime - lowerTime));
tooltip = builder.toString();
}
}

View File

@@ -1,150 +0,0 @@
/*
* Copyright (c) 2018, Kamiel
* 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.raids;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import lombok.Setter;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.ui.overlay.infobox.InfoBox;
public class RaidsTimer extends InfoBox
{
private final Instant startTime;
private Instant floorTime;
private LocalTime time;
private LocalTime firstFloorTime;
private LocalTime secondFloorTime;
private LocalTime thirdFloorTime;
private LocalTime olmTime;
@Setter
private boolean stopped;
public RaidsTimer(BufferedImage image, Plugin plugin, Instant startTime)
{
super(image, plugin);
this.startTime = startTime;
floorTime = startTime;
stopped = false;
}
public void timeFloor()
{
Duration elapsed = Duration.between(floorTime, Instant.now());
if (firstFloorTime == null)
{
firstFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
else if (secondFloorTime == null)
{
secondFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
else if (thirdFloorTime == null)
{
thirdFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
floorTime = Instant.now();
}
public void timeOlm()
{
Duration elapsed = Duration.between(floorTime, Instant.now());
olmTime = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
@Override
public String getText()
{
if (startTime == null)
{
return "";
}
if (!stopped)
{
Duration elapsed = Duration.between(startTime, Instant.now());
time = LocalTime.ofSecondOfDay(elapsed.getSeconds());
}
if (time.getHour() > 0)
{
return time.format(DateTimeFormatter.ofPattern("HH:mm"));
}
return time.format(DateTimeFormatter.ofPattern("mm:ss"));
}
@Override
public Color getTextColor()
{
if (stopped)
{
return Color.GREEN;
}
return Color.WHITE;
}
@Override
public String getTooltip()
{
StringBuilder builder = new StringBuilder();
builder.append("Elapsed raid time: ");
builder.append(time.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
if (firstFloorTime != null)
{
builder.append("</br>First floor: ");
builder.append(firstFloorTime.format(DateTimeFormatter.ofPattern("mm:ss")));
}
if (secondFloorTime != null)
{
builder.append("</br>Second floor: ");
builder.append(secondFloorTime.format(DateTimeFormatter.ofPattern("mm:ss")));
}
if (thirdFloorTime != null)
{
builder.append("</br>Third floor: ");
builder.append(thirdFloorTime.format(DateTimeFormatter.ofPattern("mm:ss")));
}
if (olmTime != null)
{
builder.append("</br>Olm: ");
builder.append(olmTime.format(DateTimeFormatter.ofPattern("mm:ss")));
}
return builder.toString();
}
}

View File

@@ -42,6 +42,7 @@ import lombok.Getter;
import net.runelite.api.MenuAction;
import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.widgets.WidgetItem;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.config.RuneLiteConfig;
@@ -364,4 +365,21 @@ public class OverlayManager
final String locationKey = overlay.getName() + OVERLAY_CONFIG_PREFERRED_POSITION;
return configManager.getConfiguration(RUNELITE_CONFIG_GROUP_NAME, locationKey, OverlayPosition.class);
}
public WidgetOverlay getWidgetOverlay(final WidgetInfo info)
{
for (Overlay o : overlays)
{
if (o instanceof WidgetOverlay)
{
WidgetOverlay overlay = (WidgetOverlay) o;
if (overlay.getWidgetInfo().equals(info))
{
return overlay;
}
}
}
return null;
}
}

View File

@@ -32,6 +32,7 @@ import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.Getter;
import net.runelite.api.Client;
import net.runelite.api.widgets.Widget;
import net.runelite.api.widgets.WidgetInfo;
@@ -64,6 +65,7 @@ public class WidgetOverlay extends Overlay
}
private final Client client;
@Getter
private final WidgetInfo widgetInfo;
private final Rectangle parentBounds = new Rectangle();

View File

@@ -0,0 +1,210 @@
/*
* Copyright (c) 2018, 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.ui.overlay.arrow;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameObject;
import net.runelite.api.NPC;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
@Singleton
@Slf4j
public class ArrowMinimapOverlay extends Overlay
{
private static final int MINIMAP_VISIBLE_RANGE = 17 * 128;
private final ArrowPointManager arrowPointManager;
private final Client client;
@Inject
private ArrowMinimapOverlay(Client client, ArrowPointManager arrowPointManager)
{
this.client = client;
this.arrowPointManager = arrowPointManager;
setPosition(OverlayPosition.DYNAMIC);
setPriority(OverlayPriority.HIGH);
setLayer(OverlayLayer.ALWAYS_ON_TOP);
}
@Override
public Dimension render(Graphics2D graphics)
{
final Collection<ArrowPoint> points = arrowPointManager.getArrowPoints().values();
if (points.isEmpty())
{
return null;
}
final LocalPoint localPlayerPos = client.getLocalPlayer().getLocalLocation();
final WorldPoint worldPlayerPos = WorldPoint.fromLocal(client, localPlayerPos);
for (ArrowPoint arrowPoint : points)
{
final WorldPoint worldPoint = arrowPoint.getWorldPoint();
if (worldPoint.distanceTo(worldPlayerPos) < arrowPoint.getVisibleRange())
{
LocalPoint fallBackPoint = LocalPoint.fromWorld(client, worldPoint);
if (arrowPoint.types.contains(ArrowType.NPC))
{
boolean found = false;
for (NPC npc : client.getCachedNPCs())
{
if (npc != null && arrowPoint.getNpcIDs().contains(npc.getId()))
{
found = true;
renderMinimapArrow(graphics, arrowPoint, npc.getLocalLocation(), localPlayerPos, worldPlayerPos);
}
}
if (found || fallBackPoint == null)
{
continue;
}
renderMinimapArrow(graphics, arrowPoint, null, localPlayerPos, worldPlayerPos);
}
else if (arrowPoint.types.contains(ArrowType.OBJECT))
{
ArrayList<GameObject> objects = ArrowUtil.getObjects(client, arrowPoint.getObjectIDs());
if (objects.isEmpty() && fallBackPoint != null)
{
renderMinimapArrow(graphics, arrowPoint, null, localPlayerPos, worldPlayerPos);
continue;
}
for (GameObject object : objects)
{
renderMinimapArrow(graphics, arrowPoint, object.getLocalLocation(), localPlayerPos, worldPlayerPos);
}
}
else if (arrowPoint.types.contains(ArrowType.WORLD_POINT))
{
renderMinimapArrow(graphics, arrowPoint, null, localPlayerPos, worldPlayerPos);
}
}
}
return null;
}
private void renderMinimapArrow(Graphics2D graphics, ArrowPoint arrowPoint, LocalPoint localPoint, LocalPoint localPlayerPos, WorldPoint worldPlayerPos)
{
final BufferedImage minimapImage = arrowPoint.getMinimapImage();
final WorldPoint worldPoint = arrowPoint.getWorldPoint();
final Point minimapImageOffset = arrowPoint.getMinimapImageOffset();
if (localPoint != null && localPlayerPos.distanceTo(localPoint) < MINIMAP_VISIBLE_RANGE)
{
final Point minimapLoc = Perspective.getMiniMapImageLocation(client, localPoint, minimapImage);
if (minimapLoc != null)
{
graphics.drawImage(minimapImage, minimapLoc.getX() + minimapImageOffset.getX(), minimapLoc.getY() + minimapImageOffset.getY(), null);
}
}
else
{
if (!arrowPoint.isMinimapUseFallback())
{
return;
}
final Point minimapPlayerPos = Perspective.localToMinimap(client, localPlayerPos);
double cameraAngle = ((client.getCameraYaw()) / 2048.0) * 2 * Math.PI;
//Use localPoint if it's available for a smoother rotation
double theta;
if (localPoint != null)
{
theta = Math.atan2(localPoint.getX() - localPlayerPos.getX(), localPlayerPos.getY() - localPoint.getY());
}
else
{
theta = Math.atan2(worldPoint.getX() - worldPlayerPos.getX(), worldPlayerPos.getY() - worldPoint.getY());
}
AffineTransform at = new AffineTransform();
if (arrowPoint.isMinimapImagePointToTarget())
{
at.translate(minimapPlayerPos.getX(), minimapPlayerPos.getY());
at.rotate(cameraAngle - theta);
at.translate(0, 66);
at.translate(minimapImageOffset.getX() - minimapImage.getWidth() / 2, minimapImageOffset.getY() - minimapImage.getHeight() / 2);
}
else
{
//Get the correct position as if it were rotated
at.rotate(cameraAngle - theta);
at.translate(0, 66);
double dX = at.getTranslateX();
double dY = at.getTranslateY();
//Then apply that position to an un-rotated transform
at = new AffineTransform();
at.translate(minimapPlayerPos.getX(), minimapPlayerPos.getY());
at.translate(dX, dY);
at.translate(minimapImageOffset.getX() - minimapImage.getWidth() / 2, minimapImageOffset.getY() - minimapImage.getHeight() / 2);
}
graphics.drawImage(minimapImage, at, null);
}
}
private void renderMinimapArrowNPC(Graphics2D graphics, ArrowPoint arrowPoint, NPC npc, LocalPoint localPlayerPos, WorldPoint worldPlayerPos)
{
final BufferedImage minimapImage = arrowPoint.getMinimapImage();
final WorldPoint worldPoint = arrowPoint.getWorldPoint();
LocalPoint localPoint;
if (npc != null)
{
localPoint = LocalPoint.fromWorld(client, worldPoint);
if (localPoint != null)
{
//For whatever reason, LocalPoint.fromWorld returns a point (-1, -1) from the actual point
localPoint = new LocalPoint(localPoint.getX() + 1, localPoint.getY() + 1);
}
}
else
{
localPoint = npc.getLocalLocation();
}
renderMinimapArrow(graphics, arrowPoint, localPoint, localPlayerPos, worldPlayerPos);
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.ui.overlay.arrow;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.EnumSet;
import java.util.HashSet;
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;
import net.runelite.api.Point;
import net.runelite.api.coords.WorldPoint;
@Data
@Builder
public class ArrowPoint
{
/**
* Can define a point to mark, or be used as a fallback when an object or an NPC is outside of the scene
*/
@NonNull
private WorldPoint worldPoint;
private HashSet<Integer> npcIDs;
private HashSet<Integer> objectIDs;
/**
* The regions that an NPC or Object has to be in to be marked (eg. Exam Centre locked chest Hard Clue requires
* killing a Barbarian in Barbarian Village, or Konar Slayer requires killing monsters in a certain area)
*/
private HashSet<Integer> region;
private BufferedImage worldImage;
@Builder.Default
private Point worldImageOffset = new Point(0, 0);
@Builder.Default
private Color tileColor = Color.RED;
private BufferedImage minimapImage;
@Builder.Default
private Point minimapImageOffset = new Point(0, 0);
/**
* Whether the minimap arrow should rotate. Set to false if not using an arrow
*/
@Builder.Default
private boolean minimapImagePointToTarget = true;
@Builder.Default
private int visibleRange = 128;
/**
* Whether the minimap arrow should use the fallback point. Useful when there are multiple locations that could be
* used as a fallback (eg. Seers' Village locked draw Medium Clue, where you need to kill any chicken for a key)
*/
@Builder.Default
private boolean minimapUseFallback = true;
/**
* A Set of what arrows should be rendered
*/
@NonNull
EnumSet<ArrowType> types;
}

View File

@@ -0,0 +1,61 @@
/*
* 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.ui.overlay.arrow;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.api.events.NpcSpawned;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.Plugin;
@Singleton
public class ArrowPointManager
{
@Getter(AccessLevel.PACKAGE)
private final Multimap<Plugin, ArrowPoint> arrowPoints = HashMultimap.create();
public void add(Plugin plugin, ArrowPoint arrowPoint)
{
arrowPoints.put(plugin, arrowPoint);
}
public void remove(Plugin plugin, ArrowPoint arrowPoint)
{
arrowPoints.remove(plugin, arrowPoint);
}
public void clear(Plugin plugin)
{
arrowPoints.removeAll(plugin);
}
public void clear()
{
arrowPoints.clear();
}
}

View File

@@ -0,0 +1,10 @@
package net.runelite.client.ui.overlay.arrow;
public enum ArrowType
{
MINIMAP,
NPC,
OBJECT,
WORLD_MAP,
WORLD_POINT
}

View File

@@ -0,0 +1,50 @@
package net.runelite.client.ui.overlay.arrow;
import java.util.ArrayList;
import java.util.HashSet;
import net.runelite.api.Client;
import net.runelite.api.GameObject;
import net.runelite.api.ObjectComposition;
import net.runelite.api.Scene;
import net.runelite.api.Tile;
class ArrowUtil
{
static ArrayList<GameObject> getObjects(final Client client, HashSet<Integer> objectIDs)
{
final Scene scene = client.getScene();
final Tile[][] tiles = scene.getTiles()[client.getPlane()];
final ArrayList<GameObject> found = new ArrayList<>();
for (Tile[] tiles2 : tiles)
{
for (Tile tile : tiles2)
{
for (GameObject object : tile.getGameObjects())
{
if (object == null)
{
continue;
}
if (objectIDs.contains(object.getId()))
{
found.add(object);
continue;
}
// Check impostors
final ObjectComposition comp = client.getObjectDefinition(object.getId());
final ObjectComposition impostor = comp.getImpostorIds() != null ? comp.getImpostor() : comp;
if (impostor != null && objectIDs.contains(impostor.getId()))
{
found.add(object);
}
}
}
}
return found;
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (c) 2018, 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.ui.overlay.arrow;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameObject;
import net.runelite.api.NPC;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
import net.runelite.client.ui.overlay.OverlayUtil;
@Singleton
@Slf4j
public class ArrowWorldOverlay extends Overlay
{
private static final int Z_OFFSET = 20;
private final ArrowPointManager arrowPointManager;
private final Client client;
@Inject
private ArrowWorldOverlay(Client client, ArrowPointManager arrowPointManager)
{
this.client = client;
this.arrowPointManager = arrowPointManager;
setPosition(OverlayPosition.DYNAMIC);
setPriority(OverlayPriority.HIGHEST);
setLayer(OverlayLayer.UNDER_WIDGETS);
}
@Override
public Dimension render(Graphics2D graphics)
{
final Collection<ArrowPoint> points = arrowPointManager.getArrowPoints().values();
if (points.isEmpty())
{
return null;
}
WorldPoint playerLocation = client.getLocalPlayer().getWorldLocation();
for (ArrowPoint arrowPoint : points)
{
WorldPoint point = arrowPoint.getWorldPoint();
if (point.distanceTo(playerLocation) < client.getScene().getDrawDistance())
{
LocalPoint fallBackPoint = LocalPoint.fromWorld(client, point);
if (arrowPoint.types.contains(ArrowType.NPC))
{
boolean found = false;
for (NPC npc : client.getCachedNPCs())
{
if (npc != null && arrowPoint.getNpcIDs().contains(npc.getId()))
{
found = true;
renderWorldArrow(graphics, arrowPoint, npc.getLocalLocation(), npc.getLogicalHeight() + Z_OFFSET);
}
}
if (found || fallBackPoint == null)
{
continue;
}
renderWorldArrow(graphics, arrowPoint, fallBackPoint);
}
else if (arrowPoint.types.contains(ArrowType.OBJECT))
{
ArrayList<GameObject> objects = ArrowUtil.getObjects(client, arrowPoint.getObjectIDs());
if (objects.isEmpty() && fallBackPoint != null)
{
renderWorldArrow(graphics, arrowPoint, fallBackPoint);
continue;
}
for (GameObject object : objects)
{
if (object.getRenderable().getModel() == null)
{
renderWorldArrow(graphics, arrowPoint, object.getLocalLocation(), 0);
}
else
{
renderWorldArrow(graphics, arrowPoint, object.getLocalLocation(), object.getRenderable().getModel().getModelHeight() + Z_OFFSET);
}
}
}
else if (arrowPoint.types.contains(ArrowType.WORLD_POINT))
{
renderWorldArrow(graphics, arrowPoint, fallBackPoint);
}
}
}
return null;
}
private void renderWorldArrow(Graphics2D graphics, ArrowPoint arrowPoint, LocalPoint localPoint)
{
renderWorldArrow(graphics, arrowPoint, localPoint, 0);
}
private void renderWorldArrow(Graphics2D graphics, ArrowPoint arrowPoint, LocalPoint localPoint, int zOffset)
{
final BufferedImage worldImage = arrowPoint.getWorldImage();
//Draw Tile
Polygon poly = Perspective.getCanvasTilePoly(client, localPoint);
if (poly != null)
{
OverlayUtil.renderPolygon(graphics, poly, arrowPoint.getTileColor());
}
Point worldIconOffset = arrowPoint.getWorldImageOffset();
//Draw World Arrow
Point canvasPoint = Perspective.getCanvasImageLocation(client, localPoint, worldImage, zOffset);
if (canvasPoint != null)
{
graphics.drawImage(worldImage, canvasPoint.getX() + worldIconOffset.getX(), canvasPoint.getY() + worldIconOffset.getY(), null);
}
}
}