develop
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.barrows;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
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.OverlayPriority;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
|
||||
public class BarrowsBrotherSlainOverlay extends OverlayPanel
|
||||
{
|
||||
private final Client client;
|
||||
|
||||
@Inject
|
||||
private BarrowsBrotherSlainOverlay(BarrowsPlugin plugin, Client client)
|
||||
{
|
||||
super(plugin);
|
||||
setPosition(OverlayPosition.TOP_LEFT);
|
||||
setPriority(OverlayPriority.LOW);
|
||||
this.client = client;
|
||||
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Barrows overlay"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
// Do not display overlay if potential is null/hidden
|
||||
final Widget potential = client.getWidget(WidgetInfo.BARROWS_POTENTIAL);
|
||||
if (potential == null || potential.isHidden())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Hide original overlay
|
||||
final Widget barrowsBrothers = client.getWidget(WidgetInfo.BARROWS_BROTHERS);
|
||||
if (barrowsBrothers != null)
|
||||
{
|
||||
barrowsBrothers.setHidden(true);
|
||||
potential.setHidden(true);
|
||||
}
|
||||
|
||||
for (BarrowsBrothers brother : BarrowsBrothers.values())
|
||||
{
|
||||
final boolean brotherSlain = client.getVar(brother.getKilledVarbit()) > 0;
|
||||
String slain = brotherSlain ? "\u2713" : "\u2717";
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(brother.getName())
|
||||
.right(slain)
|
||||
.rightColor(brotherSlain ? Color.GREEN : Color.RED)
|
||||
.build());
|
||||
}
|
||||
|
||||
final int rewardPotential = rewardPotential();
|
||||
float rewardPercent = rewardPotential / 10.12f;
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Potential")
|
||||
.right(rewardPercent != 0 ? rewardPercent + "%" : "0%")
|
||||
.rightColor(rewardPotential >= 756 && rewardPotential < 881 ? Color.GREEN : rewardPotential < 631 ? Color.WHITE : Color.YELLOW)
|
||||
.build());
|
||||
|
||||
return super.render(graphics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the barrows reward potential. Potential rewards are based off of the amount of
|
||||
* potential.
|
||||
* <p>
|
||||
* The reward potential thresholds are as follows:
|
||||
* Mind rune - 381
|
||||
* Chaos rune - 506
|
||||
* Death rune - 631
|
||||
* Blood rune - 756
|
||||
* Bolt rack - 881
|
||||
* Half key - 1006
|
||||
* Dragon med - 1012
|
||||
*
|
||||
* @return potential, 0-1012 inclusive
|
||||
* @see <a href="https://twitter.com/jagexkieren/status/705428283509366785?lang=en">source</a>
|
||||
*/
|
||||
private int rewardPotential()
|
||||
{
|
||||
// this is from [proc,barrows_overlay_reward]
|
||||
int brothers = client.getVar(Varbits.BARROWS_KILLED_AHRIM)
|
||||
+ client.getVar(Varbits.BARROWS_KILLED_DHAROK)
|
||||
+ client.getVar(Varbits.BARROWS_KILLED_GUTHAN)
|
||||
+ client.getVar(Varbits.BARROWS_KILLED_KARIL)
|
||||
+ client.getVar(Varbits.BARROWS_KILLED_TORAG)
|
||||
+ client.getVar(Varbits.BARROWS_KILLED_VERAC);
|
||||
return client.getVar(Varbits.BARROWS_REWARD_POTENTIAL) + brothers * 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <Sethtroll3@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.barrows;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public enum BarrowsBrothers
|
||||
{
|
||||
AHRIM("Ahrim", new WorldPoint(3566, 3289, 0), Varbits.BARROWS_KILLED_AHRIM),
|
||||
DHAROK("Dharok", new WorldPoint(3575, 3298, 0), Varbits.BARROWS_KILLED_DHAROK),
|
||||
GUTHAN("Guthan", new WorldPoint(3577, 3283, 0), Varbits.BARROWS_KILLED_GUTHAN),
|
||||
KARIL("Karil", new WorldPoint(3566, 3275, 0), Varbits.BARROWS_KILLED_KARIL),
|
||||
TORAG("Torag", new WorldPoint(3553, 3283, 0), Varbits.BARROWS_KILLED_TORAG),
|
||||
VERAC("Verac", new WorldPoint(3557, 3298, 0), Varbits.BARROWS_KILLED_VERAC);
|
||||
|
||||
@Getter
|
||||
private final String name;
|
||||
@Getter
|
||||
private final WorldPoint location;
|
||||
@Getter
|
||||
private final Varbits killedVarbit;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <Sethtroll3@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.barrows;
|
||||
|
||||
import java.awt.Color;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("barrows")
|
||||
public interface BarrowsConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "showBrotherLoc",
|
||||
name = "Show Brothers location",
|
||||
description = "Configures whether or not the brothers location is displayed",
|
||||
position = 1
|
||||
)
|
||||
default boolean showBrotherLoc()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showChestValue",
|
||||
name = "Show Value of Chests",
|
||||
description = "Configure whether to show total exchange value of chest when opened",
|
||||
position = 2
|
||||
)
|
||||
default boolean showChestValue()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "brotherLocColor",
|
||||
name = "Brother location color",
|
||||
description = "Change the color of the name displayed on the minimap",
|
||||
position = 3
|
||||
)
|
||||
default Color brotherLocColor()
|
||||
{
|
||||
return Color.CYAN;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "deadBrotherLocColor",
|
||||
name = "Dead Brother loc. color",
|
||||
description = "Change the color of the name displayed on the minimap for a dead brother",
|
||||
position = 4
|
||||
)
|
||||
default Color deadBrotherLocColor()
|
||||
{
|
||||
return Color.RED;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showPuzzleAnswer",
|
||||
name = "Show Puzzle Answer",
|
||||
description = "Configures if the puzzle answer should be shown.",
|
||||
position = 5
|
||||
)
|
||||
default boolean showPuzzleAnswer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showPrayerDrainTimer",
|
||||
name = "Show Prayer Drain Timer",
|
||||
description = "Configure whether or not a countdown until the next prayer drain is displayed",
|
||||
position = 6
|
||||
)
|
||||
default boolean showPrayerDrainTimer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <Sethtroll3@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.barrows;
|
||||
|
||||
import java.awt.Color;
|
||||
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.Perspective;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
class BarrowsOverlay extends Overlay
|
||||
{
|
||||
private final Client client;
|
||||
private final BarrowsPlugin plugin;
|
||||
private final BarrowsConfig config;
|
||||
|
||||
@Inject
|
||||
private BarrowsOverlay(Client client, BarrowsPlugin plugin, BarrowsConfig config)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_WIDGETS);
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
Widget puzzleAnswer = plugin.getPuzzleAnswer();
|
||||
|
||||
if (config.showBrotherLoc())
|
||||
{
|
||||
renderBarrowsBrothers(graphics);
|
||||
}
|
||||
|
||||
if (puzzleAnswer != null && config.showPuzzleAnswer() && !puzzleAnswer.isHidden())
|
||||
{
|
||||
Rectangle answerRect = puzzleAnswer.getBounds();
|
||||
graphics.setColor(Color.GREEN);
|
||||
graphics.draw(answerRect);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void renderBarrowsBrothers(Graphics2D graphics)
|
||||
{
|
||||
for (BarrowsBrothers brother : BarrowsBrothers.values())
|
||||
{
|
||||
LocalPoint localLocation = LocalPoint.fromWorld(client, brother.getLocation());
|
||||
|
||||
if (localLocation == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
String brotherLetter = Character.toString(brother.getName().charAt(0));
|
||||
net.runelite.api.Point minimapText = Perspective.getCanvasTextMiniMapLocation(client, graphics,
|
||||
localLocation, brotherLetter);
|
||||
|
||||
if (minimapText != null)
|
||||
{
|
||||
graphics.setColor(Color.black);
|
||||
graphics.drawString(brotherLetter, minimapText.getX() + 1, minimapText.getY() + 1);
|
||||
|
||||
if (client.getVar(brother.getKilledVarbit()) > 0)
|
||||
{
|
||||
graphics.setColor(config.deadBrotherLocColor());
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.setColor(config.brotherLocColor());
|
||||
}
|
||||
|
||||
graphics.drawString(brotherLetter, minimapText.getX(), minimapText.getY());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <Sethtroll3@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.barrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.inject.Provides;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import javax.inject.Inject;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.SpriteID;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
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.game.ItemManager;
|
||||
import net.runelite.client.game.SpriteManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxPriority;
|
||||
import net.runelite.client.ui.overlay.infobox.LoopTimer;
|
||||
import net.runelite.client.util.QuantityFormatter;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Barrows Brothers",
|
||||
description = "Show helpful information for the Barrows minigame",
|
||||
tags = {"combat", "minigame", "bosses", "pve", "pvm"}
|
||||
)
|
||||
public class BarrowsPlugin extends Plugin
|
||||
{
|
||||
private static final ImmutableList<WidgetInfo> POSSIBLE_SOLUTIONS = ImmutableList.of(
|
||||
WidgetInfo.BARROWS_PUZZLE_ANSWER1,
|
||||
WidgetInfo.BARROWS_PUZZLE_ANSWER2,
|
||||
WidgetInfo.BARROWS_PUZZLE_ANSWER3
|
||||
);
|
||||
|
||||
private static final long PRAYER_DRAIN_INTERVAL_MS = 18200;
|
||||
private static final int CRYPT_REGION_ID = 14231;
|
||||
|
||||
private LoopTimer barrowsPrayerDrainTimer;
|
||||
private boolean wasInCrypt = false;
|
||||
|
||||
@Getter
|
||||
private Widget puzzleAnswer;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private BarrowsOverlay barrowsOverlay;
|
||||
|
||||
@Inject
|
||||
private BarrowsBrotherSlainOverlay brotherOverlay;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
|
||||
@Inject
|
||||
private SpriteManager spriteManager;
|
||||
|
||||
@Inject
|
||||
private InfoBoxManager infoBoxManager;
|
||||
|
||||
@Inject
|
||||
private ChatMessageManager chatMessageManager;
|
||||
|
||||
@Inject
|
||||
private BarrowsConfig config;
|
||||
|
||||
@Provides
|
||||
BarrowsConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(BarrowsConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(barrowsOverlay);
|
||||
overlayManager.add(brotherOverlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
overlayManager.remove(barrowsOverlay);
|
||||
overlayManager.remove(brotherOverlay);
|
||||
puzzleAnswer = null;
|
||||
wasInCrypt = false;
|
||||
stopPrayerDrainTimer();
|
||||
|
||||
// Restore widgets
|
||||
final Widget potential = client.getWidget(WidgetInfo.BARROWS_POTENTIAL);
|
||||
if (potential != null)
|
||||
{
|
||||
potential.setHidden(false);
|
||||
}
|
||||
|
||||
final Widget barrowsBrothers = client.getWidget(WidgetInfo.BARROWS_BROTHERS);
|
||||
if (barrowsBrothers != null)
|
||||
{
|
||||
barrowsBrothers.setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals("barrows") && !config.showPrayerDrainTimer())
|
||||
{
|
||||
stopPrayerDrainTimer();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
if (event.getGameState() == GameState.LOADING)
|
||||
{
|
||||
wasInCrypt = isInCrypt();
|
||||
// on region changes the tiles get set to null
|
||||
puzzleAnswer = null;
|
||||
}
|
||||
else if (event.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
boolean isInCrypt = isInCrypt();
|
||||
if (wasInCrypt && !isInCrypt)
|
||||
{
|
||||
stopPrayerDrainTimer();
|
||||
}
|
||||
else if (!wasInCrypt && isInCrypt)
|
||||
{
|
||||
startPrayerDrainTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onWidgetLoaded(WidgetLoaded event)
|
||||
{
|
||||
if (event.getGroupId() == WidgetID.BARROWS_REWARD_GROUP_ID && config.showChestValue())
|
||||
{
|
||||
ItemContainer barrowsRewardContainer = client.getItemContainer(InventoryID.BARROWS_REWARD);
|
||||
Item[] items = barrowsRewardContainer.getItems();
|
||||
long chestPrice = 0;
|
||||
|
||||
for (Item item : items)
|
||||
{
|
||||
long itemStack = (long) itemManager.getItemPrice(item.getId()) * (long) item.getQuantity();
|
||||
chestPrice += itemStack;
|
||||
}
|
||||
|
||||
final ChatMessageBuilder message = new ChatMessageBuilder()
|
||||
.append(ChatColorType.HIGHLIGHT)
|
||||
.append("Your chest is worth around ")
|
||||
.append(QuantityFormatter.formatNumber(chestPrice))
|
||||
.append(" coins.")
|
||||
.append(ChatColorType.NORMAL);
|
||||
|
||||
chatMessageManager.queue(QueuedMessage.builder()
|
||||
.type(ChatMessageType.ITEM_EXAMINE)
|
||||
.runeLiteFormattedMessage(message.build())
|
||||
.build());
|
||||
}
|
||||
else if (event.getGroupId() == WidgetID.BARROWS_PUZZLE_GROUP_ID)
|
||||
{
|
||||
final int answer = client.getWidget(WidgetInfo.BARROWS_FIRST_PUZZLE).getModelId() - 3;
|
||||
puzzleAnswer = null;
|
||||
|
||||
for (WidgetInfo puzzleNode : POSSIBLE_SOLUTIONS)
|
||||
{
|
||||
final Widget widgetToCheck = client.getWidget(puzzleNode);
|
||||
|
||||
if (widgetToCheck != null && widgetToCheck.getModelId() == answer)
|
||||
{
|
||||
puzzleAnswer = client.getWidget(puzzleNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startPrayerDrainTimer()
|
||||
{
|
||||
if (config.showPrayerDrainTimer())
|
||||
{
|
||||
final LoopTimer loopTimer = new LoopTimer(
|
||||
PRAYER_DRAIN_INTERVAL_MS,
|
||||
ChronoUnit.MILLIS,
|
||||
null,
|
||||
this,
|
||||
true);
|
||||
|
||||
spriteManager.getSpriteAsync(SpriteID.TAB_PRAYER, 0, loopTimer);
|
||||
|
||||
loopTimer.setPriority(InfoBoxPriority.MED);
|
||||
loopTimer.setTooltip("Prayer Drain");
|
||||
|
||||
infoBoxManager.addInfoBox(loopTimer);
|
||||
barrowsPrayerDrainTimer = loopTimer;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopPrayerDrainTimer()
|
||||
{
|
||||
infoBoxManager.removeInfoBox(barrowsPrayerDrainTimer);
|
||||
barrowsPrayerDrainTimer = null;
|
||||
}
|
||||
|
||||
private boolean isInCrypt()
|
||||
{
|
||||
Player localPlayer = client.getLocalPlayer();
|
||||
return localPlayer != null && localPlayer.getWorldLocation().getRegionID() == CRYPT_REGION_ID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <Sethtroll3@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.blastfurnace;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.Varbits;
|
||||
|
||||
public enum BarsOres
|
||||
{
|
||||
COPPER_ORE(Varbits.BLAST_FURNACE_COPPER_ORE, ItemID.COPPER_ORE),
|
||||
TIN_ORE(Varbits.BLAST_FURNACE_TIN_ORE, ItemID.TIN_ORE),
|
||||
IRON_ORE(Varbits.BLAST_FURNACE_IRON_ORE, ItemID.IRON_ORE),
|
||||
COAL(Varbits.BLAST_FURNACE_COAL, ItemID.COAL),
|
||||
MITHRIL_ORE(Varbits.BLAST_FURNACE_MITHRIL_ORE, ItemID.MITHRIL_ORE),
|
||||
ADAMANTITE_ORE(Varbits.BLAST_FURNACE_ADAMANTITE_ORE, ItemID.ADAMANTITE_ORE),
|
||||
RUNITE_ORE(Varbits.BLAST_FURNACE_RUNITE_ORE, ItemID.RUNITE_ORE),
|
||||
SILVER_ORE(Varbits.BLAST_FURNACE_SILVER_ORE, ItemID.SILVER_ORE),
|
||||
GOLD_ORE(Varbits.BLAST_FURNACE_GOLD_ORE, ItemID.GOLD_ORE),
|
||||
BRONZE_BAR(Varbits.BLAST_FURNACE_BRONZE_BAR, ItemID.BRONZE_BAR),
|
||||
IRON_BAR(Varbits.BLAST_FURNACE_IRON_BAR, ItemID.IRON_BAR),
|
||||
STEEL_BAR(Varbits.BLAST_FURNACE_STEEL_BAR, ItemID.STEEL_BAR),
|
||||
MITHRIL_BAR(Varbits.BLAST_FURNACE_MITHRIL_BAR, ItemID.MITHRIL_BAR),
|
||||
ADAMANTITE_BAR(Varbits.BLAST_FURNACE_ADAMANTITE_BAR, ItemID.ADAMANTITE_BAR),
|
||||
RUNITE_BAR(Varbits.BLAST_FURNACE_RUNITE_BAR, ItemID.RUNITE_BAR),
|
||||
SILVER_BAR(Varbits.BLAST_FURNACE_SILVER_BAR, ItemID.SILVER_BAR),
|
||||
GOLD_BAR(Varbits.BLAST_FURNACE_GOLD_BAR, ItemID.GOLD_BAR);
|
||||
|
||||
private static final Map<Varbits, BarsOres> VARBIT;
|
||||
|
||||
static
|
||||
{
|
||||
ImmutableMap.Builder<Varbits, BarsOres> builder = new ImmutableMap.Builder<>();
|
||||
|
||||
for (BarsOres s : values())
|
||||
{
|
||||
builder.put(s.getVarbit(), s);
|
||||
}
|
||||
|
||||
VARBIT = builder.build();
|
||||
}
|
||||
|
||||
@Getter
|
||||
private final Varbits varbit;
|
||||
@Getter
|
||||
private final int itemID;
|
||||
|
||||
BarsOres(Varbits varbit, int itemID)
|
||||
{
|
||||
this.varbit = varbit;
|
||||
this.itemID = itemID;
|
||||
}
|
||||
|
||||
public static BarsOres getVarbit(Varbits varbit)
|
||||
{
|
||||
return VARBIT.get(varbit);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <Sethtroll3@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.blastfurnace;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.ItemContainer;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
|
||||
class BlastFurnaceClickBoxOverlay extends Overlay
|
||||
{
|
||||
private static final int MAX_DISTANCE = 2350;
|
||||
|
||||
private final Client client;
|
||||
private final BlastFurnacePlugin plugin;
|
||||
private final BlastFurnaceConfig config;
|
||||
|
||||
@Inject
|
||||
private BlastFurnaceClickBoxOverlay(Client client, BlastFurnacePlugin plugin, BlastFurnaceConfig config)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
int dispenserState = client.getVar(Varbits.BAR_DISPENSER);
|
||||
|
||||
if (config.showConveyorBelt() && plugin.getConveyorBelt() != null)
|
||||
{
|
||||
Color color = dispenserState == 1 ? Color.RED : Color.GREEN;
|
||||
renderObject(plugin.getConveyorBelt(), graphics, color);
|
||||
}
|
||||
|
||||
if (config.showBarDispenser() && plugin.getBarDispenser() != null)
|
||||
{
|
||||
boolean hasIceGloves = hasIceGloves();
|
||||
Color color = dispenserState == 2 && hasIceGloves ? Color.GREEN : (dispenserState == 3 ? Color.GREEN : Color.RED);
|
||||
|
||||
renderObject(plugin.getBarDispenser(), graphics, color);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean hasIceGloves()
|
||||
{
|
||||
ItemContainer equipmentContainer = client.getItemContainer(InventoryID.EQUIPMENT);
|
||||
if (equipmentContainer == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return equipmentContainer.contains(ItemID.ICE_GLOVES);
|
||||
}
|
||||
|
||||
private void renderObject(GameObject object, Graphics2D graphics, Color color)
|
||||
{
|
||||
LocalPoint localLocation = client.getLocalPlayer().getLocalLocation();
|
||||
Point mousePosition = client.getMouseCanvasPosition();
|
||||
|
||||
LocalPoint location = object.getLocalLocation();
|
||||
|
||||
if (localLocation.distanceTo(location) <= MAX_DISTANCE)
|
||||
{
|
||||
Shape objectClickbox = object.getClickbox();
|
||||
if (objectClickbox != null)
|
||||
{
|
||||
if (objectClickbox.contains(mousePosition.getX(), mousePosition.getY()))
|
||||
{
|
||||
graphics.setColor(color.darker());
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.setColor(color);
|
||||
}
|
||||
graphics.draw(objectClickbox);
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 20));
|
||||
graphics.fill(objectClickbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <Sethtroll3@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.blastfurnace;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||
import static net.runelite.api.Varbits.BLAST_FURNACE_COFFER;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
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.LineComponent;
|
||||
import net.runelite.client.util.QuantityFormatter;
|
||||
import static org.apache.commons.lang3.time.DurationFormatUtils.formatDuration;
|
||||
|
||||
class BlastFurnaceCofferOverlay extends OverlayPanel
|
||||
{
|
||||
private static final float COST_PER_HOUR = 72000.0f;
|
||||
|
||||
private final Client client;
|
||||
private final BlastFurnacePlugin plugin;
|
||||
private final BlastFurnaceConfig config;
|
||||
|
||||
@Inject
|
||||
private BlastFurnaceCofferOverlay(Client client, BlastFurnacePlugin plugin, BlastFurnaceConfig config)
|
||||
{
|
||||
super(plugin);
|
||||
setPosition(OverlayPosition.TOP_LEFT);
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Coffer overlay"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (plugin.getConveyorBelt() == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget sack = client.getWidget(WidgetInfo.BLAST_FURNACE_COFFER);
|
||||
|
||||
if (sack != null)
|
||||
{
|
||||
final int coffer = client.getVar(BLAST_FURNACE_COFFER);
|
||||
|
||||
sack.setHidden(true);
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Coffer:")
|
||||
.right(QuantityFormatter.quantityToStackSize(coffer) + " gp")
|
||||
.build());
|
||||
|
||||
if (config.showCofferTime())
|
||||
{
|
||||
final long millis = (long) (coffer / COST_PER_HOUR * 60 * 60 * 1000);
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Time:")
|
||||
.right(formatDuration(millis, "H'h' m'm' s's'", true))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
return super.render(graphics);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <Sethtroll3@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.blastfurnace;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("blastfurnace")
|
||||
public interface BlastFurnaceConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "showConveyorBelt",
|
||||
name = "Show conveyor belt clickbox",
|
||||
description = "Configures whether or not the clickbox for the conveyor belt is displayed",
|
||||
position = 1
|
||||
)
|
||||
default boolean showConveyorBelt()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showBarDispenser",
|
||||
name = "Show bar dispenser clickbox",
|
||||
description = "Configures whether or not the clickbox for the bar dispenser is displayed",
|
||||
position = 2
|
||||
)
|
||||
default boolean showBarDispenser()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showCofferTime",
|
||||
name = "Show coffer time remaining",
|
||||
description = "Configures whether or not the coffer time remaining is displayed",
|
||||
position = 3
|
||||
)
|
||||
default boolean showCofferTime()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <Sethtroll3@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.blastfurnace;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
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.ComponentOrientation;
|
||||
import net.runelite.client.ui.overlay.components.ImageComponent;
|
||||
|
||||
class BlastFurnaceOverlay extends OverlayPanel
|
||||
{
|
||||
private final Client client;
|
||||
private final BlastFurnacePlugin plugin;
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
|
||||
@Inject
|
||||
BlastFurnaceOverlay(Client client, BlastFurnacePlugin plugin)
|
||||
{
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
this.client = client;
|
||||
setPosition(OverlayPosition.TOP_LEFT);
|
||||
panelComponent.setOrientation(ComponentOrientation.HORIZONTAL);
|
||||
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Blast furnace overlay"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (plugin.getConveyorBelt() == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (BarsOres varbit : BarsOres.values())
|
||||
{
|
||||
int amount = client.getVar(varbit.getVarbit());
|
||||
|
||||
if (amount == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
panelComponent.getChildren().add(new ImageComponent(getImage(varbit.getItemID(), amount)));
|
||||
}
|
||||
|
||||
return super.render(graphics);
|
||||
}
|
||||
|
||||
private BufferedImage getImage(int itemID, int amount)
|
||||
{
|
||||
BufferedImage image = itemManager.getImage(itemID, amount, true);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <Sethtroll3@gmail.com>
|
||||
* Copyright (c) 2019, Brandon White <bmwqg@live.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.blastfurnace;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import javax.inject.Inject;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.GameState;
|
||||
import static net.runelite.api.NullObjectID.NULL_9092;
|
||||
import static net.runelite.api.ObjectID.CONVEYOR_BELT;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.events.GameObjectDespawned;
|
||||
import net.runelite.api.events.GameObjectSpawned;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||
import net.runelite.client.util.Text;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Blast Furnace",
|
||||
description = "Show helpful information for the Blast Furnace minigame",
|
||||
tags = {"minigame", "overlay", "skilling", "smithing"}
|
||||
)
|
||||
public class BlastFurnacePlugin extends Plugin
|
||||
{
|
||||
private static final int BAR_DISPENSER = NULL_9092;
|
||||
private static final String FOREMAN_PERMISSION_TEXT = "Okay, you can use the furnace for ten minutes. Remember, you only need half as much coal as with a regular furnace.";
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private GameObject conveyorBelt;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private GameObject barDispenser;
|
||||
|
||||
private ForemanTimer foremanTimer;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private BlastFurnaceOverlay overlay;
|
||||
|
||||
@Inject
|
||||
private BlastFurnaceCofferOverlay cofferOverlay;
|
||||
|
||||
@Inject
|
||||
private BlastFurnaceClickBoxOverlay clickBoxOverlay;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
|
||||
@Inject
|
||||
private InfoBoxManager infoBoxManager;
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(overlay);
|
||||
overlayManager.add(cofferOverlay);
|
||||
overlayManager.add(clickBoxOverlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
infoBoxManager.removeIf(ForemanTimer.class::isInstance);
|
||||
overlayManager.remove(overlay);
|
||||
overlayManager.remove(cofferOverlay);
|
||||
overlayManager.remove(clickBoxOverlay);
|
||||
conveyorBelt = null;
|
||||
barDispenser = null;
|
||||
foremanTimer = null;
|
||||
}
|
||||
|
||||
@Provides
|
||||
BlastFurnaceConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(BlastFurnaceConfig.class);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameObjectSpawned(GameObjectSpawned event)
|
||||
{
|
||||
GameObject gameObject = event.getGameObject();
|
||||
|
||||
switch (gameObject.getId())
|
||||
{
|
||||
case CONVEYOR_BELT:
|
||||
conveyorBelt = gameObject;
|
||||
break;
|
||||
|
||||
case BAR_DISPENSER:
|
||||
barDispenser = gameObject;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameObjectDespawned(GameObjectDespawned event)
|
||||
{
|
||||
GameObject gameObject = event.getGameObject();
|
||||
|
||||
switch (gameObject.getId())
|
||||
{
|
||||
case CONVEYOR_BELT:
|
||||
conveyorBelt = null;
|
||||
break;
|
||||
|
||||
case BAR_DISPENSER:
|
||||
barDispenser = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
if (event.getGameState() == GameState.LOADING)
|
||||
{
|
||||
conveyorBelt = null;
|
||||
barDispenser = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
Widget npcDialog = client.getWidget(WidgetInfo.DIALOG_NPC_TEXT);
|
||||
if (npcDialog == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// blocking dialog check until 5 minutes needed to avoid re-adding while dialog message still displayed
|
||||
boolean shouldCheckForemanFee = client.getRealSkillLevel(Skill.SMITHING) < 60
|
||||
&& (foremanTimer == null || Duration.between(Instant.now(), foremanTimer.getEndTime()).toMinutes() <= 5);
|
||||
|
||||
if (shouldCheckForemanFee)
|
||||
{
|
||||
String npcText = Text.sanitizeMultilineText(npcDialog.getText());
|
||||
|
||||
if (npcText.equals(FOREMAN_PERMISSION_TEXT))
|
||||
{
|
||||
infoBoxManager.removeIf(ForemanTimer.class::isInstance);
|
||||
|
||||
foremanTimer = new ForemanTimer(this, itemManager);
|
||||
infoBoxManager.addInfoBox(foremanTimer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Brandon White <bmwqg@live.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.blastfurnace;
|
||||
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.ui.overlay.infobox.Timer;
|
||||
|
||||
class ForemanTimer extends Timer
|
||||
{
|
||||
private static final String TOOLTIP_TEXT = "Foreman Fee";
|
||||
|
||||
ForemanTimer(BlastFurnacePlugin plugin, ItemManager itemManager)
|
||||
{
|
||||
super(10, ChronoUnit.MINUTES, itemManager.getImage(ItemID.COAL_BAG), plugin);
|
||||
|
||||
setTooltip(TOOLTIP_TEXT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Unmoon <https://github.com/Unmoon>
|
||||
* 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.blastmine;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.ItemID;
|
||||
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
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.ComponentOrientation;
|
||||
import net.runelite.client.ui.overlay.components.ImageComponent;
|
||||
|
||||
class BlastMineOreCountOverlay extends OverlayPanel
|
||||
{
|
||||
private final Client client;
|
||||
private final BlastMinePluginConfig config;
|
||||
private final ItemManager itemManager;
|
||||
|
||||
@Inject
|
||||
private BlastMineOreCountOverlay(BlastMinePlugin plugin, Client client, BlastMinePluginConfig config, ItemManager itemManager)
|
||||
{
|
||||
super(plugin);
|
||||
setPosition(OverlayPosition.TOP_LEFT);
|
||||
this.client = client;
|
||||
this.config = config;
|
||||
this.itemManager = itemManager;
|
||||
panelComponent.setOrientation(ComponentOrientation.HORIZONTAL);
|
||||
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Blast mine overlay"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
final Widget blastMineWidget = client.getWidget(WidgetInfo.BLAST_MINE);
|
||||
|
||||
if (blastMineWidget == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (config.showOreOverlay())
|
||||
{
|
||||
blastMineWidget.setHidden(true);
|
||||
panelComponent.getChildren().add(new ImageComponent(getImage(ItemID.COAL, client.getVar(Varbits.BLAST_MINE_COAL))));
|
||||
panelComponent.getChildren().add(new ImageComponent(getImage(ItemID.GOLD_ORE, client.getVar(Varbits.BLAST_MINE_GOLD))));
|
||||
panelComponent.getChildren().add(new ImageComponent(getImage(ItemID.MITHRIL_ORE, client.getVar(Varbits.BLAST_MINE_MITHRIL))));
|
||||
panelComponent.getChildren().add(new ImageComponent(getImage(ItemID.ADAMANTITE_ORE, client.getVar(Varbits.BLAST_MINE_ADAMANTITE))));
|
||||
panelComponent.getChildren().add(new ImageComponent(getImage(ItemID.RUNITE_ORE, client.getVar(Varbits.BLAST_MINE_RUNITE))));
|
||||
}
|
||||
else
|
||||
{
|
||||
blastMineWidget.setHidden(false);
|
||||
}
|
||||
|
||||
return super.render(graphics);
|
||||
}
|
||||
|
||||
private BufferedImage getImage(int itemID, int amount)
|
||||
{
|
||||
return itemManager.getImage(itemID, amount, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Unmoon <https://github.com/Unmoon>
|
||||
* 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.blastmine;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.GameObjectSpawned;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
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;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Blast Mine",
|
||||
description = "Show helpful information for the Blast Mine minigame",
|
||||
tags = {"explode", "explosive", "mining", "minigame", "skilling"}
|
||||
)
|
||||
public class BlastMinePlugin extends Plugin
|
||||
{
|
||||
@Getter
|
||||
private final Map<WorldPoint, BlastMineRock> rocks = new HashMap<>();
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private BlastMineRockOverlay blastMineRockOverlay;
|
||||
|
||||
@Inject
|
||||
private BlastMineOreCountOverlay blastMineOreCountOverlay;
|
||||
|
||||
@Provides
|
||||
BlastMinePluginConfig getConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(BlastMinePluginConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(blastMineRockOverlay);
|
||||
overlayManager.add(blastMineOreCountOverlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
overlayManager.remove(blastMineRockOverlay);
|
||||
overlayManager.remove(blastMineOreCountOverlay);
|
||||
final Widget blastMineWidget = client.getWidget(WidgetInfo.BLAST_MINE);
|
||||
|
||||
if (blastMineWidget != null)
|
||||
{
|
||||
blastMineWidget.setHidden(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameObjectSpawned(GameObjectSpawned event)
|
||||
{
|
||||
final GameObject gameObject = event.getGameObject();
|
||||
BlastMineRockType blastMineRockType = BlastMineRockType.getRockType(gameObject.getId());
|
||||
if (blastMineRockType == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final BlastMineRock newRock = new BlastMineRock(gameObject, blastMineRockType);
|
||||
final BlastMineRock oldRock = rocks.get(gameObject.getWorldLocation());
|
||||
|
||||
if (oldRock == null || oldRock.getType() != newRock.getType())
|
||||
{
|
||||
rocks.put(gameObject.getWorldLocation(), newRock);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
if (event.getGameState() == GameState.LOADING)
|
||||
{
|
||||
rocks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick gameTick)
|
||||
{
|
||||
if (rocks.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rocks.values().removeIf(rock ->
|
||||
(rock.getRemainingTimeRelative() == 1 && rock.getType() != BlastMineRockType.NORMAL) ||
|
||||
(rock.getRemainingFuseTimeRelative() == 1 && rock.getType() == BlastMineRockType.LIT));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Unmoon <https://github.com/Unmoon>
|
||||
* 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.blastmine;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
@ConfigGroup("blastmine")
|
||||
public interface BlastMinePluginConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
position = 0,
|
||||
keyName = "showOreOverlay",
|
||||
name = "Show ore overlay",
|
||||
description = "Configures whether or not the ore count overlay is displayed"
|
||||
)
|
||||
default boolean showOreOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 1,
|
||||
keyName = "showRockIconOverlay",
|
||||
name = "Show icons overlay",
|
||||
description = "Configures whether or not the icon overlay is displayed"
|
||||
)
|
||||
default boolean showRockIconOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 2,
|
||||
keyName = "showTimerOverlay",
|
||||
name = "Show timer overlay",
|
||||
description = "Configures whether or not the timer overlay is displayed"
|
||||
)
|
||||
default boolean showTimerOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 3,
|
||||
keyName = "showWarningOverlay",
|
||||
name = "Show explosion warning",
|
||||
description = "Configures whether or not the explosion warning overlay is displayed"
|
||||
)
|
||||
default boolean showWarningOverlay()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 4,
|
||||
keyName = "hexTimerColor",
|
||||
name = "Timer color",
|
||||
description = "Color of timer overlay"
|
||||
)
|
||||
default Color getTimerColor()
|
||||
{
|
||||
return new Color(217, 54, 0);
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 5,
|
||||
keyName = "hexWarningColor",
|
||||
name = "Warning color",
|
||||
description = "Color of warning overlay"
|
||||
)
|
||||
default Color getWarningColor()
|
||||
{
|
||||
return new Color(217, 54, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Unmoon <https://github.com/Unmoon>
|
||||
* 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.blastmine;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.GameObject;
|
||||
|
||||
class BlastMineRock
|
||||
{
|
||||
private static final Duration PLANT_TIME = Duration.ofSeconds(30);
|
||||
private static final Duration FUSE_TIME = Duration.ofMillis(4200);
|
||||
|
||||
@Getter
|
||||
private final GameObject gameObject;
|
||||
|
||||
@Getter
|
||||
private final BlastMineRockType type;
|
||||
|
||||
private final Instant creationTime = Instant.now();
|
||||
|
||||
BlastMineRock(final GameObject gameObject, BlastMineRockType blastMineRockType)
|
||||
{
|
||||
this.gameObject = gameObject;
|
||||
this.type = blastMineRockType;
|
||||
}
|
||||
|
||||
double getRemainingFuseTimeRelative()
|
||||
{
|
||||
Duration duration = Duration.between(creationTime, Instant.now());
|
||||
return duration.compareTo(FUSE_TIME) < 0 ? (double) duration.toMillis() / FUSE_TIME.toMillis() : 1;
|
||||
}
|
||||
|
||||
double getRemainingTimeRelative()
|
||||
{
|
||||
Duration duration = Duration.between(creationTime, Instant.now());
|
||||
return duration.compareTo(PLANT_TIME) < 0 ? (double) duration.toMillis() / PLANT_TIME.toMillis() : 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Unmoon <https://github.com/Unmoon>
|
||||
* 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.blastmine;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Polygon;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.NullObjectID;
|
||||
import net.runelite.api.ObjectID;
|
||||
import net.runelite.api.Perspective;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.Tile;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
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.components.ProgressPieComponent;
|
||||
|
||||
public class BlastMineRockOverlay extends Overlay
|
||||
{
|
||||
private static final int MAX_DISTANCE = 16;
|
||||
private static final int WARNING_DISTANCE = 2;
|
||||
private static final ImmutableSet<Integer> WALL_OBJECTS = ImmutableSet.of(
|
||||
NullObjectID.NULL_28570, NullObjectID.NULL_28571, NullObjectID.NULL_28572, NullObjectID.NULL_28573, NullObjectID.NULL_28574,
|
||||
NullObjectID.NULL_28575, NullObjectID.NULL_28576, NullObjectID.NULL_28577, NullObjectID.NULL_28578,
|
||||
ObjectID.HARD_ROCK, ObjectID.HARD_ROCK_28580, ObjectID.CAVITY, ObjectID.CAVITY_28582,
|
||||
ObjectID.POT_OF_DYNAMITE, ObjectID.POT_OF_DYNAMITE_28584, ObjectID.POT_OF_DYNAMITE_28585, ObjectID.POT_OF_DYNAMITE_28586,
|
||||
ObjectID.SHATTERED_ROCKFACE, ObjectID.SHATTERED_ROCKFACE_28588);
|
||||
|
||||
private final Client client;
|
||||
private final BlastMinePlugin plugin;
|
||||
private final BlastMinePluginConfig config;
|
||||
|
||||
private final BufferedImage chiselIcon;
|
||||
private final BufferedImage dynamiteIcon;
|
||||
private final BufferedImage tinderboxIcon;
|
||||
|
||||
@Inject
|
||||
private BlastMineRockOverlay(Client client, BlastMinePlugin plugin, BlastMinePluginConfig config, ItemManager itemManager)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_SCENE);
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
chiselIcon = itemManager.getImage(ItemID.CHISEL);
|
||||
dynamiteIcon = itemManager.getImage(ItemID.DYNAMITE);
|
||||
tinderboxIcon = itemManager.getImage(ItemID.TINDERBOX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
Map<WorldPoint, BlastMineRock> rocks = plugin.getRocks();
|
||||
if (rocks.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final Tile[][][] tiles = client.getScene().getTiles();
|
||||
final Widget viewport = client.getViewportWidget();
|
||||
|
||||
for (final BlastMineRock rock : rocks.values())
|
||||
{
|
||||
if (viewport == null ||
|
||||
rock.getGameObject().getCanvasLocation() == null ||
|
||||
rock.getGameObject().getWorldLocation().distanceTo(client.getLocalPlayer().getWorldLocation()) > MAX_DISTANCE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (rock.getType())
|
||||
{
|
||||
case NORMAL:
|
||||
drawIconOnRock(graphics, rock, chiselIcon);
|
||||
break;
|
||||
case CHISELED:
|
||||
drawIconOnRock(graphics, rock, dynamiteIcon);
|
||||
break;
|
||||
case LOADED:
|
||||
drawIconOnRock(graphics, rock, tinderboxIcon);
|
||||
break;
|
||||
case LIT:
|
||||
drawTimerOnRock(graphics, rock, config.getTimerColor());
|
||||
drawAreaWarning(graphics, rock, config.getWarningColor(), tiles);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void drawIconOnRock(Graphics2D graphics, BlastMineRock rock, BufferedImage icon)
|
||||
{
|
||||
if (!config.showRockIconOverlay())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Point loc = Perspective.getCanvasImageLocation(client, rock.getGameObject().getLocalLocation(), icon, 150);
|
||||
|
||||
if (loc != null)
|
||||
{
|
||||
graphics.drawImage(icon, loc.getX(), loc.getY(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawTimerOnRock(Graphics2D graphics, BlastMineRock rock, Color color)
|
||||
{
|
||||
if (!config.showTimerOverlay())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Point loc = Perspective.localToCanvas(client, rock.getGameObject().getLocalLocation(), rock.getGameObject().getPlane(), 150);
|
||||
|
||||
if (loc != null)
|
||||
{
|
||||
final double timeLeft = 1 - rock.getRemainingFuseTimeRelative();
|
||||
final ProgressPieComponent pie = new ProgressPieComponent();
|
||||
pie.setFill(color);
|
||||
pie.setBorderColor(color);
|
||||
pie.setPosition(loc);
|
||||
pie.setProgress(timeLeft);
|
||||
pie.render(graphics);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawAreaWarning(Graphics2D graphics, BlastMineRock rock, Color color, Tile[][][] tiles)
|
||||
{
|
||||
if (!config.showWarningOverlay())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final int z = client.getPlane();
|
||||
int x = rock.getGameObject().getLocalLocation().getX() / Perspective.LOCAL_TILE_SIZE;
|
||||
int y = rock.getGameObject().getLocalLocation().getY() / Perspective.LOCAL_TILE_SIZE;
|
||||
final int orientation = tiles[z][x][y].getWallObject().getOrientationA();
|
||||
|
||||
switch (orientation) //calculate explosion around the tile in front of the wall
|
||||
{
|
||||
case 1:
|
||||
x--;
|
||||
break;
|
||||
case 4:
|
||||
x++;
|
||||
break;
|
||||
case 8:
|
||||
y--;
|
||||
break;
|
||||
default:
|
||||
y++;
|
||||
}
|
||||
|
||||
for (int i = -WARNING_DISTANCE; i <= WARNING_DISTANCE; i++)
|
||||
{
|
||||
for (int j = -WARNING_DISTANCE; j <= WARNING_DISTANCE; j++)
|
||||
{
|
||||
final GameObject gameObject = tiles[z][x + i][y + j].getGameObjects()[0];
|
||||
|
||||
//check if tile is empty, or is a wall...
|
||||
if (gameObject == null || !WALL_OBJECTS.contains(gameObject.getId()))
|
||||
{
|
||||
final LocalPoint localTile = new LocalPoint(
|
||||
(x + i) * Perspective.LOCAL_TILE_SIZE + Perspective.LOCAL_TILE_SIZE / 2,
|
||||
(y + j) * Perspective.LOCAL_TILE_SIZE + Perspective.LOCAL_TILE_SIZE / 2);
|
||||
final Polygon poly = Perspective.getCanvasTilePoly(client, localTile);
|
||||
|
||||
if (poly != null)
|
||||
{
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 100));
|
||||
graphics.fillPolygon(poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Unmoon <https://github.com/Unmoon>
|
||||
* 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.blastmine;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.Map;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.ObjectID;
|
||||
|
||||
public enum BlastMineRockType
|
||||
{
|
||||
NORMAL(ObjectID.HARD_ROCK, ObjectID.HARD_ROCK_28580),
|
||||
CHISELED(ObjectID.CAVITY, ObjectID.CAVITY_28582),
|
||||
LOADED(ObjectID.POT_OF_DYNAMITE, ObjectID.POT_OF_DYNAMITE_28584),
|
||||
LIT(ObjectID.POT_OF_DYNAMITE_28585, ObjectID.POT_OF_DYNAMITE_28586),
|
||||
EXPLODED(ObjectID.SHATTERED_ROCKFACE, ObjectID.SHATTERED_ROCKFACE_28588);
|
||||
|
||||
private static final Map<Integer, BlastMineRockType> rockTypes;
|
||||
|
||||
static
|
||||
{
|
||||
ImmutableMap.Builder<Integer, BlastMineRockType> builder = new ImmutableMap.Builder<>();
|
||||
|
||||
for (BlastMineRockType type : values())
|
||||
{
|
||||
for (int spotId : type.getObjectIds())
|
||||
{
|
||||
builder.put(spotId, type);
|
||||
}
|
||||
}
|
||||
|
||||
rockTypes = builder.build();
|
||||
}
|
||||
|
||||
@Getter
|
||||
private final int[] objectIds;
|
||||
|
||||
BlastMineRockType(int... objectIds)
|
||||
{
|
||||
this.objectIds = objectIds;
|
||||
}
|
||||
|
||||
public static BlastMineRockType getRockType(int objectId)
|
||||
{
|
||||
return rockTypes.get(objectId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.boosts;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBox;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxPriority;
|
||||
|
||||
public class BoostIndicator extends InfoBox
|
||||
{
|
||||
private final BoostsPlugin plugin;
|
||||
private final BoostsConfig config;
|
||||
private final Client client;
|
||||
|
||||
@Getter
|
||||
private final Skill skill;
|
||||
|
||||
BoostIndicator(Skill skill, BufferedImage image, BoostsPlugin plugin, Client client, BoostsConfig config)
|
||||
{
|
||||
super(image, plugin);
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.client = client;
|
||||
this.skill = skill;
|
||||
setTooltip(skill.getName() + " boost");
|
||||
setPriority(InfoBoxPriority.HIGH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText()
|
||||
{
|
||||
if (!config.useRelativeBoost())
|
||||
{
|
||||
return String.valueOf(client.getBoostedSkillLevel(skill));
|
||||
}
|
||||
|
||||
int boost = client.getBoostedSkillLevel(skill) - client.getRealSkillLevel(skill);
|
||||
String text = String.valueOf(boost);
|
||||
if (boost > 0)
|
||||
{
|
||||
text = "+" + text;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getTextColor()
|
||||
{
|
||||
int boosted = client.getBoostedSkillLevel(skill),
|
||||
base = client.getRealSkillLevel(skill);
|
||||
|
||||
if (boosted < base)
|
||||
{
|
||||
return new Color(238, 51, 51);
|
||||
}
|
||||
|
||||
return boosted - base <= config.boostThreshold() ? Color.YELLOW : Color.GREEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean render()
|
||||
{
|
||||
return config.displayInfoboxes() && plugin.canShowBoosts() && plugin.getSkillsToDisplay().contains(getSkill());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "Boost " + skill.getName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Seth <Sethtroll3@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.boosts;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("boosts")
|
||||
public interface BoostsConfig extends Config
|
||||
{
|
||||
enum DisplayChangeMode
|
||||
{
|
||||
ALWAYS,
|
||||
BOOSTED,
|
||||
NEVER
|
||||
}
|
||||
|
||||
enum DisplayBoosts
|
||||
{
|
||||
NONE,
|
||||
COMBAT,
|
||||
NON_COMBAT,
|
||||
BOTH
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "displayBoosts",
|
||||
name = "Display Boosts",
|
||||
description = "Configures which skill boosts to display",
|
||||
position = 1
|
||||
)
|
||||
default DisplayBoosts displayBoosts()
|
||||
{
|
||||
return DisplayBoosts.BOTH;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "relativeBoost",
|
||||
name = "Use Relative Boosts",
|
||||
description = "Configures whether or not relative boost is used",
|
||||
position = 2
|
||||
)
|
||||
default boolean useRelativeBoost()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "displayIndicators",
|
||||
name = "Display as infoboxes",
|
||||
description = "Configures whether or not to display the boost as infoboxes",
|
||||
position = 3
|
||||
)
|
||||
default boolean displayInfoboxes()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "displayNextBuffChange",
|
||||
name = "Display next buff change",
|
||||
description = "Configures whether or not to display when the next buffed stat change will be",
|
||||
position = 4
|
||||
)
|
||||
default DisplayChangeMode displayNextBuffChange()
|
||||
{
|
||||
return DisplayChangeMode.BOOSTED;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "displayNextDebuffChange",
|
||||
name = "Display next debuff change",
|
||||
description = "Configures whether or not to display when the next debuffed stat change will be",
|
||||
position = 5
|
||||
)
|
||||
default DisplayChangeMode displayNextDebuffChange()
|
||||
{
|
||||
return DisplayChangeMode.NEVER;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "boostThreshold",
|
||||
name = "Boost amount threshold",
|
||||
description = "The threshold at which boosted levels will be displayed in a different color. A value of 0 will disable the feature.",
|
||||
position = 6
|
||||
)
|
||||
default int boostThreshold()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "notifyOnBoost",
|
||||
name = "Notify on boost threshold",
|
||||
description = "Configures whether or not a notification will be sent for boosted stats.",
|
||||
position = 7
|
||||
)
|
||||
default boolean notifyOnBoost()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.boosts;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG;
|
||||
import net.runelite.api.Skill;
|
||||
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.OverlayPriority;
|
||||
import net.runelite.client.ui.overlay.components.LineComponent;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
|
||||
class BoostsOverlay extends OverlayPanel
|
||||
{
|
||||
private final Client client;
|
||||
private final BoostsConfig config;
|
||||
private final BoostsPlugin plugin;
|
||||
|
||||
@Inject
|
||||
private BoostsOverlay(Client client, BoostsConfig config, BoostsPlugin plugin)
|
||||
{
|
||||
super(plugin);
|
||||
this.plugin = plugin;
|
||||
this.client = client;
|
||||
this.config = config;
|
||||
setPosition(OverlayPosition.TOP_LEFT);
|
||||
setPriority(OverlayPriority.MED);
|
||||
getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Boosts overlay"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (config.displayInfoboxes())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int nextChange = plugin.getChangeDownTicks();
|
||||
|
||||
if (nextChange != -1)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Next + restore in")
|
||||
.right(String.valueOf(plugin.getChangeTime(nextChange)))
|
||||
.build());
|
||||
}
|
||||
|
||||
nextChange = plugin.getChangeUpTicks();
|
||||
|
||||
if (nextChange != -1)
|
||||
{
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left("Next - restore in")
|
||||
.right(String.valueOf(plugin.getChangeTime(nextChange)))
|
||||
.build());
|
||||
}
|
||||
|
||||
final Set<Skill> boostedSkills = plugin.getSkillsToDisplay();
|
||||
|
||||
if (boostedSkills.isEmpty())
|
||||
{
|
||||
return super.render(graphics);
|
||||
}
|
||||
|
||||
if (plugin.canShowBoosts())
|
||||
{
|
||||
for (Skill skill : boostedSkills)
|
||||
{
|
||||
final int boosted = client.getBoostedSkillLevel(skill);
|
||||
final int base = client.getRealSkillLevel(skill);
|
||||
final int boost = boosted - base;
|
||||
final Color strColor = getTextColor(boost);
|
||||
String str;
|
||||
|
||||
if (config.useRelativeBoost())
|
||||
{
|
||||
str = String.valueOf(boost);
|
||||
if (boost > 0)
|
||||
{
|
||||
str = "+" + str;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
str = ColorUtil.prependColorTag(Integer.toString(boosted), strColor)
|
||||
+ ColorUtil.prependColorTag("/" + base, Color.WHITE);
|
||||
}
|
||||
|
||||
panelComponent.getChildren().add(LineComponent.builder()
|
||||
.left(skill.getName())
|
||||
.right(str)
|
||||
.rightColor(strColor)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
return super.render(graphics);
|
||||
}
|
||||
|
||||
private Color getTextColor(int boost)
|
||||
{
|
||||
if (boost < 0)
|
||||
{
|
||||
return new Color(238, 51, 51);
|
||||
}
|
||||
|
||||
return boost <= config.boostThreshold() ? Color.YELLOW : Color.GREEN;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.boosts;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Provides;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Constants;
|
||||
import net.runelite.api.Prayer;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.StatChanged;
|
||||
import net.runelite.client.Notifier;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.game.SkillIconManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Boosts Information",
|
||||
description = "Show combat and/or skill boost information",
|
||||
tags = {"combat", "notifications", "skilling", "overlay"}
|
||||
)
|
||||
@Singleton
|
||||
public class BoostsPlugin extends Plugin
|
||||
{
|
||||
private static final Set<Skill> BOOSTABLE_COMBAT_SKILLS = ImmutableSet.of(
|
||||
Skill.ATTACK,
|
||||
Skill.STRENGTH,
|
||||
Skill.DEFENCE,
|
||||
Skill.RANGED,
|
||||
Skill.MAGIC);
|
||||
|
||||
private static final Set<Skill> BOOSTABLE_NON_COMBAT_SKILLS = ImmutableSet.of(
|
||||
Skill.MINING, Skill.AGILITY, Skill.SMITHING, Skill.HERBLORE, Skill.FISHING, Skill.THIEVING,
|
||||
Skill.COOKING, Skill.CRAFTING, Skill.FIREMAKING, Skill.FLETCHING, Skill.WOODCUTTING, Skill.RUNECRAFT,
|
||||
Skill.SLAYER, Skill.FARMING, Skill.CONSTRUCTION, Skill.HUNTER);
|
||||
|
||||
@Inject
|
||||
private Notifier notifier;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private InfoBoxManager infoBoxManager;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private BoostsOverlay boostsOverlay;
|
||||
|
||||
@Inject
|
||||
private BoostsConfig config;
|
||||
|
||||
@Inject
|
||||
private SkillIconManager skillIconManager;
|
||||
|
||||
@Getter
|
||||
private final Set<Skill> skillsToDisplay = EnumSet.noneOf(Skill.class);
|
||||
|
||||
private final Set<Skill> shownSkills = EnumSet.noneOf(Skill.class);
|
||||
|
||||
private boolean isChangedDown = false;
|
||||
private boolean isChangedUp = false;
|
||||
private final int[] lastSkillLevels = new int[Skill.values().length - 1];
|
||||
private int lastChangeDown = -1;
|
||||
private int lastChangeUp = -1;
|
||||
private boolean preserveBeenActive = false;
|
||||
private long lastTickMillis;
|
||||
|
||||
@Provides
|
||||
BoostsConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(BoostsConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(boostsOverlay);
|
||||
|
||||
updateShownSkills();
|
||||
Arrays.fill(lastSkillLevels, -1);
|
||||
|
||||
// Add infoboxes for everything at startup and then determine inside if it will be rendered
|
||||
infoBoxManager.addInfoBox(new StatChangeIndicator(true, ImageUtil.getResourceStreamFromClass(getClass(), "debuffed.png"), this, config));
|
||||
infoBoxManager.addInfoBox(new StatChangeIndicator(false, ImageUtil.getResourceStreamFromClass(getClass(), "buffed.png"), this, config));
|
||||
|
||||
for (final Skill skill : Skill.values())
|
||||
{
|
||||
if (skill != Skill.OVERALL)
|
||||
{
|
||||
infoBoxManager.addInfoBox(new BoostIndicator(skill, skillIconManager.getSkillImage(skill), this, client, config));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
overlayManager.remove(boostsOverlay);
|
||||
infoBoxManager.removeIf(t -> t instanceof BoostIndicator || t instanceof StatChangeIndicator);
|
||||
preserveBeenActive = false;
|
||||
lastChangeDown = -1;
|
||||
lastChangeUp = -1;
|
||||
isChangedUp = false;
|
||||
isChangedDown = false;
|
||||
skillsToDisplay.clear();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
switch (event.getGameState())
|
||||
{
|
||||
case LOGIN_SCREEN:
|
||||
case HOPPING:
|
||||
// After world hop and log out timers are in undefined state so just reset
|
||||
lastChangeDown = -1;
|
||||
lastChangeUp = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (!event.getGroup().equals("boosts"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
updateShownSkills();
|
||||
|
||||
if (config.displayNextBuffChange() == BoostsConfig.DisplayChangeMode.NEVER)
|
||||
{
|
||||
lastChangeDown = -1;
|
||||
}
|
||||
|
||||
if (config.displayNextDebuffChange() == BoostsConfig.DisplayChangeMode.NEVER)
|
||||
{
|
||||
lastChangeUp = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onStatChanged(StatChanged statChanged)
|
||||
{
|
||||
Skill skill = statChanged.getSkill();
|
||||
|
||||
if (!BOOSTABLE_COMBAT_SKILLS.contains(skill) && !BOOSTABLE_NON_COMBAT_SKILLS.contains(skill))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int skillIdx = skill.ordinal();
|
||||
int last = lastSkillLevels[skillIdx];
|
||||
int cur = client.getBoostedSkillLevel(skill);
|
||||
|
||||
if (cur == last - 1)
|
||||
{
|
||||
// Stat was restored down (from buff)
|
||||
lastChangeDown = client.getTickCount();
|
||||
}
|
||||
|
||||
if (cur == last + 1)
|
||||
{
|
||||
// Stat was restored up (from debuff)
|
||||
lastChangeUp = client.getTickCount();
|
||||
}
|
||||
|
||||
lastSkillLevels[skillIdx] = cur;
|
||||
updateBoostedStats();
|
||||
|
||||
int boostThreshold = config.boostThreshold();
|
||||
|
||||
if (boostThreshold != 0 && config.notifyOnBoost())
|
||||
{
|
||||
int real = client.getRealSkillLevel(skill);
|
||||
int lastBoost = last - real;
|
||||
int boost = cur - real;
|
||||
if (boost <= boostThreshold && boostThreshold < lastBoost)
|
||||
{
|
||||
notifier.notify(skill.getName() + " level is getting low!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
lastTickMillis = System.currentTimeMillis();
|
||||
|
||||
if (getChangeUpTicks() <= 0)
|
||||
{
|
||||
switch (config.displayNextDebuffChange())
|
||||
{
|
||||
case ALWAYS:
|
||||
if (lastChangeUp != -1)
|
||||
{
|
||||
lastChangeUp = client.getTickCount();
|
||||
}
|
||||
|
||||
break;
|
||||
case BOOSTED:
|
||||
case NEVER:
|
||||
lastChangeUp = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (getChangeDownTicks() <= 0)
|
||||
{
|
||||
switch (config.displayNextBuffChange())
|
||||
{
|
||||
case ALWAYS:
|
||||
if (lastChangeDown != -1)
|
||||
{
|
||||
lastChangeDown = client.getTickCount();
|
||||
}
|
||||
|
||||
break;
|
||||
case BOOSTED:
|
||||
case NEVER:
|
||||
lastChangeDown = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateShownSkills()
|
||||
{
|
||||
switch (config.displayBoosts())
|
||||
{
|
||||
case NONE:
|
||||
shownSkills.removeAll(BOOSTABLE_COMBAT_SKILLS);
|
||||
shownSkills.removeAll(BOOSTABLE_NON_COMBAT_SKILLS);
|
||||
break;
|
||||
case COMBAT:
|
||||
shownSkills.addAll(BOOSTABLE_COMBAT_SKILLS);
|
||||
shownSkills.removeAll(BOOSTABLE_NON_COMBAT_SKILLS);
|
||||
break;
|
||||
case NON_COMBAT:
|
||||
shownSkills.removeAll(BOOSTABLE_COMBAT_SKILLS);
|
||||
shownSkills.addAll(BOOSTABLE_NON_COMBAT_SKILLS);
|
||||
break;
|
||||
case BOTH:
|
||||
shownSkills.addAll(BOOSTABLE_COMBAT_SKILLS);
|
||||
shownSkills.addAll(BOOSTABLE_NON_COMBAT_SKILLS);
|
||||
break;
|
||||
}
|
||||
updateBoostedStats();
|
||||
}
|
||||
|
||||
private void updateBoostedStats()
|
||||
{
|
||||
// Reset is boosted
|
||||
isChangedDown = false;
|
||||
isChangedUp = false;
|
||||
skillsToDisplay.clear();
|
||||
|
||||
// Check if we are still boosted
|
||||
for (final Skill skill : Skill.values())
|
||||
{
|
||||
if (!shownSkills.contains(skill))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final int boosted = client.getBoostedSkillLevel(skill);
|
||||
final int base = client.getRealSkillLevel(skill);
|
||||
|
||||
if (boosted > base)
|
||||
{
|
||||
isChangedUp = true;
|
||||
}
|
||||
else if (boosted < base)
|
||||
{
|
||||
isChangedDown = true;
|
||||
}
|
||||
|
||||
if (boosted != base)
|
||||
{
|
||||
skillsToDisplay.add(skill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean canShowBoosts()
|
||||
{
|
||||
return isChangedDown || isChangedUp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the amount of time until boosted stats decay,
|
||||
* accounting for the effect of preserve prayer.
|
||||
* Preserve extends the time of boosted stats by 50% while active.
|
||||
* The length of a boost is split into 4 sections of 15 seconds each.
|
||||
* If the preserve prayer is active for the entire duration of the final
|
||||
* section it will "activate" adding an additional 15 second section
|
||||
* to the boost timing. If again the preserve prayer is active for that
|
||||
* entire section a second 15 second section will be added.
|
||||
*
|
||||
* Preserve is only required to be on for the 4th and 5th sections of the boost timer
|
||||
* to gain full effect (seconds 45-75).
|
||||
*
|
||||
* @return integer value in ticks until next boost change
|
||||
*/
|
||||
int getChangeDownTicks()
|
||||
{
|
||||
if (lastChangeDown == -1 ||
|
||||
config.displayNextBuffChange() == BoostsConfig.DisplayChangeMode.NEVER ||
|
||||
(config.displayNextBuffChange() == BoostsConfig.DisplayChangeMode.BOOSTED && !isChangedUp))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ticksSinceChange = client.getTickCount() - lastChangeDown;
|
||||
boolean isPreserveActive = client.isPrayerActive(Prayer.PRESERVE);
|
||||
|
||||
if ((isPreserveActive && (ticksSinceChange < 75 || preserveBeenActive)) || ticksSinceChange > 125)
|
||||
{
|
||||
preserveBeenActive = true;
|
||||
return 150 - ticksSinceChange;
|
||||
}
|
||||
|
||||
preserveBeenActive = false;
|
||||
return (ticksSinceChange > 100) ? 125 - ticksSinceChange : 100 - ticksSinceChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restoration from debuff is separate timer as restoration from buff because of preserve messing up the buff timer.
|
||||
* Restoration timer is always in 100 tick cycles.
|
||||
*
|
||||
* @return integer value in ticks until next stat restoration up
|
||||
*/
|
||||
int getChangeUpTicks()
|
||||
{
|
||||
if (lastChangeUp == -1 ||
|
||||
config.displayNextDebuffChange() == BoostsConfig.DisplayChangeMode.NEVER ||
|
||||
(config.displayNextDebuffChange() == BoostsConfig.DisplayChangeMode.BOOSTED && !isChangedDown))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ticksSinceChange = client.getTickCount() - lastChangeUp;
|
||||
return 100 - ticksSinceChange;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts tick-based time to accurate second time
|
||||
* @param time tick-based time
|
||||
* @return second-based time
|
||||
*/
|
||||
int getChangeTime(final int time)
|
||||
{
|
||||
final long diff = System.currentTimeMillis() - lastTickMillis;
|
||||
return time != -1 ? (int)((time * Constants.GAME_TICK_LENGTH - diff) / 1000d) : time;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.boosts;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBox;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxPriority;
|
||||
|
||||
public class StatChangeIndicator extends InfoBox
|
||||
{
|
||||
private final boolean up;
|
||||
private final BoostsPlugin plugin;
|
||||
private final BoostsConfig config;
|
||||
|
||||
StatChangeIndicator(boolean up, BufferedImage image, BoostsPlugin plugin, BoostsConfig config)
|
||||
{
|
||||
super(image, plugin);
|
||||
this.up = up;
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
setPriority(InfoBoxPriority.MED);
|
||||
setTooltip(up ? "Next debuff change" : "Next buff change");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText()
|
||||
{
|
||||
return String.format("%02d", plugin.getChangeTime(up ? plugin.getChangeUpTicks() : plugin.getChangeDownTicks()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getTextColor()
|
||||
{
|
||||
return (up ? plugin.getChangeUpTicks() : plugin.getChangeDownTicks()) < 10 ? Color.RED.brighter() : Color.WHITE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean render()
|
||||
{
|
||||
final int time = up ? plugin.getChangeUpTicks() : plugin.getChangeDownTicks();
|
||||
return config.displayInfoboxes() && time != -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Cameron Moberg <Moberg@tuta.io>
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.bosstimer;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Map;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.NpcID;
|
||||
|
||||
enum Boss
|
||||
{
|
||||
GENERAL_GRAARDOR(NpcID.GENERAL_GRAARDOR, 90, ChronoUnit.SECONDS, ItemID.PET_GENERAL_GRAARDOR),
|
||||
KRIL_TSUTSAROTH(NpcID.KRIL_TSUTSAROTH, 90, ChronoUnit.SECONDS, ItemID.PET_KRIL_TSUTSAROTH),
|
||||
KREEARRA(NpcID.KREEARRA, 90, ChronoUnit.SECONDS, ItemID.PET_KREEARRA),
|
||||
COMMANDER_ZILYANA(NpcID.COMMANDER_ZILYANA, 90, ChronoUnit.SECONDS, ItemID.PET_ZILYANA),
|
||||
CALLISTO(NpcID.CALLISTO_6609, 30, ChronoUnit.SECONDS, ItemID.CALLISTO_CUB),
|
||||
CHAOS_ELEMENTAL(NpcID.CHAOS_ELEMENTAL, 60, ChronoUnit.SECONDS, ItemID.PET_CHAOS_ELEMENTAL),
|
||||
CHAOS_FANATIC(NpcID.CHAOS_FANATIC, 30, ChronoUnit.SECONDS, ItemID.ANCIENT_STAFF),
|
||||
CRAZY_ARCHAEOLOGIST(NpcID.CRAZY_ARCHAEOLOGIST, 30, ChronoUnit.SECONDS, ItemID.FEDORA),
|
||||
KING_BLACK_DRAGON(NpcID.KING_BLACK_DRAGON, 9, ChronoUnit.SECONDS, ItemID.PRINCE_BLACK_DRAGON),
|
||||
SCORPIA(NpcID.SCORPIA, 10, ChronoUnit.SECONDS, ItemID.SCORPIAS_OFFSPRING),
|
||||
VENENATIS(NpcID.VENENATIS_6610, 30, ChronoUnit.SECONDS, ItemID.VENENATIS_SPIDERLING),
|
||||
VETION(NpcID.VETION_REBORN, 30, ChronoUnit.SECONDS, ItemID.VETION_JR),
|
||||
DAGANNOTH_PRIME(NpcID.DAGANNOTH_PRIME, 90, ChronoUnit.SECONDS, ItemID.PET_DAGANNOTH_PRIME),
|
||||
DAGANNOTH_REX(NpcID.DAGANNOTH_REX, 90, ChronoUnit.SECONDS, ItemID.PET_DAGANNOTH_REX),
|
||||
DAGANNOTH_SUPREME(NpcID.DAGANNOTH_SUPREME, 90, ChronoUnit.SECONDS, ItemID.PET_DAGANNOTH_SUPREME),
|
||||
CORPOREAL_BEAST(NpcID.CORPOREAL_BEAST, 30, ChronoUnit.SECONDS, ItemID.PET_DARK_CORE),
|
||||
GIANT_MOLE(NpcID.GIANT_MOLE, 9000, ChronoUnit.MILLIS, ItemID.BABY_MOLE),
|
||||
DERANGED_ARCHAEOLOGIST(NpcID.DERANGED_ARCHAEOLOGIST, 29400, ChronoUnit.MILLIS, ItemID.UNIDENTIFIED_LARGE_FOSSIL),
|
||||
CERBERUS(NpcID.CERBERUS, 8400, ChronoUnit.MILLIS, ItemID.HELLPUPPY),
|
||||
THERMONUCLEAR_SMOKE_DEVIL(NpcID.THERMONUCLEAR_SMOKE_DEVIL, 8400, ChronoUnit.MILLIS, ItemID.PET_SMOKE_DEVIL),
|
||||
KRAKEN(NpcID.KRAKEN, 8400, ChronoUnit.MILLIS, ItemID.PET_KRAKEN),
|
||||
KALPHITE_QUEEN(NpcID.KALPHITE_QUEEN_965, 30, ChronoUnit.SECONDS, ItemID.KALPHITE_PRINCESS),
|
||||
DUSK(NpcID.DUSK_7889, 2, ChronoUnit.MINUTES, ItemID.NOON),
|
||||
ALCHEMICAL_HYDRA(NpcID.ALCHEMICAL_HYDRA_8622, 25200, ChronoUnit.MILLIS, ItemID.IKKLE_HYDRA),
|
||||
SARACHNIS(NpcID.SARACHNIS, 10, ChronoUnit.SECONDS, ItemID.SRARACHA),
|
||||
ZALCANO(NpcID.ZALCANO_9050, 21600, ChronoUnit.MILLIS, ItemID.SMOLCANO);
|
||||
|
||||
private static final Map<Integer, Boss> bosses;
|
||||
|
||||
private final int id;
|
||||
private final Duration spawnTime;
|
||||
private final int itemSpriteId;
|
||||
|
||||
static
|
||||
{
|
||||
ImmutableMap.Builder<Integer, Boss> builder = new ImmutableMap.Builder<>();
|
||||
|
||||
for (Boss boss : values())
|
||||
{
|
||||
builder.put(boss.getId(), boss);
|
||||
}
|
||||
|
||||
bosses = builder.build();
|
||||
}
|
||||
|
||||
Boss(int id, long period, ChronoUnit unit, int itemSpriteId)
|
||||
{
|
||||
this.id = id;
|
||||
this.spawnTime = Duration.of(period, unit);
|
||||
this.itemSpriteId = itemSpriteId;
|
||||
}
|
||||
|
||||
public int getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public Duration getSpawnTime()
|
||||
{
|
||||
return spawnTime;
|
||||
}
|
||||
|
||||
public int getItemSpriteId()
|
||||
{
|
||||
return itemSpriteId;
|
||||
}
|
||||
|
||||
public static Boss find(int id)
|
||||
{
|
||||
return bosses.get(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017, Cameron Moberg <Moberg@tuta.io>
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.bosstimer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.events.NpcDespawned;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Boss Timers",
|
||||
description = "Show boss spawn timer overlays",
|
||||
tags = {"combat", "pve", "overlay", "spawn"}
|
||||
)
|
||||
@Slf4j
|
||||
public class BossTimersPlugin extends Plugin
|
||||
{
|
||||
@Inject
|
||||
private InfoBoxManager infoBoxManager;
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
infoBoxManager.removeIf(t -> t instanceof RespawnTimer);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onNpcDespawned(NpcDespawned npcDespawned)
|
||||
{
|
||||
NPC npc = npcDespawned.getNpc();
|
||||
|
||||
if (!npc.isDead())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int npcId = npc.getId();
|
||||
|
||||
Boss boss = Boss.find(npcId);
|
||||
|
||||
if (boss == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// remove existing timer
|
||||
infoBoxManager.removeIf(t -> t instanceof RespawnTimer && ((RespawnTimer) t).getBoss() == boss);
|
||||
|
||||
log.debug("Creating spawn timer for {} ({} seconds)", npc.getName(), boss.getSpawnTime());
|
||||
|
||||
RespawnTimer timer = new RespawnTimer(boss, itemManager.getImage(boss.getItemSpriteId()), this);
|
||||
timer.setTooltip(npc.getName());
|
||||
infoBoxManager.addInfoBox(timer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.bosstimer;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.ui.overlay.infobox.Timer;
|
||||
|
||||
class RespawnTimer extends Timer
|
||||
{
|
||||
private final Boss boss;
|
||||
|
||||
public RespawnTimer(Boss boss, BufferedImage bossImage, Plugin plugin)
|
||||
{
|
||||
super(boss.getSpawnTime().toMillis(), ChronoUnit.MILLIS, bossImage, plugin);
|
||||
this.boss = boss;
|
||||
}
|
||||
|
||||
public Boss getBoss()
|
||||
{
|
||||
return boss;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2018, Seth <Sethtroll3@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.cannon;
|
||||
|
||||
import java.awt.Color;
|
||||
import net.runelite.client.config.Alpha;
|
||||
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.cannon.CannonPlugin.MAX_CBALLS;
|
||||
|
||||
@ConfigGroup("cannon")
|
||||
public interface CannonConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "showEmptyCannonNotification",
|
||||
name = "Enable cannon notifications",
|
||||
description = "Configures whether to notify you when your cannon is low on cannonballs",
|
||||
position = 1
|
||||
)
|
||||
default boolean showCannonNotifications()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Range(
|
||||
max = MAX_CBALLS
|
||||
)
|
||||
@ConfigItem(
|
||||
keyName = "lowWarningThreshold",
|
||||
name = "Low Warning Threshold",
|
||||
description = "Configures the number of cannonballs remaining before a notification is sent. <br>Regardless of this value, a notification will still be sent when your cannon is empty.",
|
||||
position = 2
|
||||
)
|
||||
default int lowWarningThreshold()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showInfobox",
|
||||
name = "Show Cannonball infobox",
|
||||
description = "Configures whether to show the cannonballs in an infobox",
|
||||
position = 3
|
||||
)
|
||||
default boolean showInfobox()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showDoubleHitSpot",
|
||||
name = "Show double hit spots",
|
||||
description = "Configures whether to show the NPC double hit spot",
|
||||
position = 4
|
||||
)
|
||||
default boolean showDoubleHitSpot()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Alpha
|
||||
@ConfigItem(
|
||||
keyName = "highlightDoubleHitColor",
|
||||
name = "Color of double hit spots",
|
||||
description = "Configures the highlight color of double hit spots",
|
||||
position = 5
|
||||
)
|
||||
default Color highlightDoubleHitColor()
|
||||
{
|
||||
return Color.RED;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showCannonSpots",
|
||||
name = "Show common cannon spots",
|
||||
description = "Configures whether to show common cannon spots or not",
|
||||
position = 6
|
||||
)
|
||||
default boolean showCannonSpots()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.cannon;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBox;
|
||||
|
||||
class CannonCounter extends InfoBox
|
||||
{
|
||||
private final CannonPlugin plugin;
|
||||
|
||||
CannonCounter(BufferedImage img, CannonPlugin plugin)
|
||||
{
|
||||
super(img, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText()
|
||||
{
|
||||
return String.valueOf(plugin.getCballsLeft());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getTextColor()
|
||||
{
|
||||
return plugin.getStateColor();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2018, Seth <Sethtroll3@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.cannon;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Polygon;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Perspective;
|
||||
import static net.runelite.api.Perspective.LOCAL_TILE_SIZE;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.OverlayPriority;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
import net.runelite.client.ui.overlay.components.TextComponent;
|
||||
|
||||
class CannonOverlay extends Overlay
|
||||
{
|
||||
private static final int MAX_DISTANCE = 2500;
|
||||
|
||||
private final Client client;
|
||||
private final CannonConfig config;
|
||||
private final CannonPlugin plugin;
|
||||
private final TextComponent textComponent = new TextComponent();
|
||||
|
||||
@Inject
|
||||
CannonOverlay(Client client, CannonConfig config, CannonPlugin plugin)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setPriority(OverlayPriority.MED);
|
||||
this.client = client;
|
||||
this.config = config;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (!plugin.isCannonPlaced() || plugin.getCannonPosition() == null || plugin.getCannonWorld() != client.getWorld())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
LocalPoint cannonPoint = LocalPoint.fromWorld(client, plugin.getCannonPosition());
|
||||
|
||||
if (cannonPoint == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
LocalPoint localLocation = client.getLocalPlayer().getLocalLocation();
|
||||
|
||||
if (localLocation.distanceTo(cannonPoint) <= MAX_DISTANCE)
|
||||
{
|
||||
Point cannonLoc = Perspective.getCanvasTextLocation(client,
|
||||
graphics,
|
||||
cannonPoint,
|
||||
String.valueOf(plugin.getCballsLeft()), 150);
|
||||
|
||||
if (cannonLoc != null)
|
||||
{
|
||||
textComponent.setText(String.valueOf(plugin.getCballsLeft()));
|
||||
textComponent.setPosition(new java.awt.Point(cannonLoc.getX(), cannonLoc.getY()));
|
||||
textComponent.setColor(plugin.getStateColor());
|
||||
textComponent.render(graphics);
|
||||
}
|
||||
|
||||
if (config.showDoubleHitSpot())
|
||||
{
|
||||
Color color = config.highlightDoubleHitColor();
|
||||
drawDoubleHitSpots(graphics, cannonPoint, color);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw the double hit spots on a 6 by 6 grid around the cannon
|
||||
* @param startTile The position of the cannon
|
||||
*/
|
||||
private void drawDoubleHitSpots(Graphics2D graphics, LocalPoint startTile, Color color)
|
||||
{
|
||||
for (int x = -3; x <= 3; x++)
|
||||
{
|
||||
for (int y = -3; y <= 3; y++)
|
||||
{
|
||||
if (y != 1 && x != 1 && y != -1 && x != -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//Ignore center square
|
||||
if (y >= -1 && y <= 1 && x >= -1 && x <= 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int xPos = startTile.getX() - (x * LOCAL_TILE_SIZE);
|
||||
int yPos = startTile.getY() - (y * LOCAL_TILE_SIZE);
|
||||
|
||||
LocalPoint marker = new LocalPoint(xPos, yPos);
|
||||
Polygon poly = Perspective.getCanvasTilePoly(client, marker);
|
||||
|
||||
if (poly == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OverlayUtil.renderPolygon(graphics, poly, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2018, Seth <Sethtroll3@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.cannon;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.inject.Inject;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.AnimationID;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.InventoryID;
|
||||
import net.runelite.api.Item;
|
||||
import net.runelite.api.ItemID;
|
||||
import static net.runelite.api.ObjectID.CANNON_BASE;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.Projectile;
|
||||
import static net.runelite.api.ProjectileID.CANNONBALL;
|
||||
import static net.runelite.api.ProjectileID.GRANITE_CANNONBALL;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.GameObjectSpawned;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.ItemContainerChanged;
|
||||
import net.runelite.api.events.ProjectileMoved;
|
||||
import net.runelite.client.Notifier;
|
||||
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.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Cannon",
|
||||
description = "Show information about cannon placement and/or amount of cannonballs",
|
||||
tags = {"combat", "notifications", "ranged", "overlay"}
|
||||
)
|
||||
public class CannonPlugin extends Plugin
|
||||
{
|
||||
private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)");
|
||||
static final int MAX_CBALLS = 30;
|
||||
|
||||
private CannonCounter counter;
|
||||
private boolean skipProjectileCheckThisTick;
|
||||
private boolean cannonBallNotificationSent;
|
||||
|
||||
@Getter
|
||||
private int cballsLeft;
|
||||
|
||||
@Getter
|
||||
private boolean cannonPlaced;
|
||||
|
||||
@Getter
|
||||
private WorldPoint cannonPosition;
|
||||
|
||||
@Getter
|
||||
private int cannonWorld = -1;
|
||||
|
||||
@Getter
|
||||
private GameObject cannon;
|
||||
|
||||
@Getter
|
||||
private List<WorldPoint> spotPoints = new ArrayList<>();
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
|
||||
@Inject
|
||||
private InfoBoxManager infoBoxManager;
|
||||
|
||||
@Inject
|
||||
private Notifier notifier;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private CannonOverlay cannonOverlay;
|
||||
|
||||
@Inject
|
||||
private CannonSpotOverlay cannonSpotOverlay;
|
||||
|
||||
@Inject
|
||||
private CannonConfig config;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Provides
|
||||
CannonConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(CannonConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(cannonOverlay);
|
||||
overlayManager.add(cannonSpotOverlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
cannonSpotOverlay.setHidden(true);
|
||||
overlayManager.remove(cannonOverlay);
|
||||
overlayManager.remove(cannonSpotOverlay);
|
||||
cannonPlaced = false;
|
||||
cannonWorld = -1;
|
||||
cannonPosition = null;
|
||||
cannonBallNotificationSent = false;
|
||||
cballsLeft = 0;
|
||||
removeCounter();
|
||||
skipProjectileCheckThisTick = false;
|
||||
spotPoints.clear();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onItemContainerChanged(ItemContainerChanged event)
|
||||
{
|
||||
if (event.getItemContainer() != client.getItemContainer(InventoryID.INVENTORY))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
boolean hasBase = false;
|
||||
boolean hasStand = false;
|
||||
boolean hasBarrels = false;
|
||||
boolean hasFurnace = false;
|
||||
boolean hasAll = false;
|
||||
|
||||
if (!cannonPlaced)
|
||||
{
|
||||
for (Item item : event.getItemContainer().getItems())
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (item.getId())
|
||||
{
|
||||
case ItemID.CANNON_BASE:
|
||||
hasBase = true;
|
||||
break;
|
||||
case ItemID.CANNON_STAND:
|
||||
hasStand = true;
|
||||
break;
|
||||
case ItemID.CANNON_BARRELS:
|
||||
hasBarrels = true;
|
||||
break;
|
||||
case ItemID.CANNON_FURNACE:
|
||||
hasFurnace = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasBase && hasStand && hasBarrels && hasFurnace)
|
||||
{
|
||||
hasAll = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cannonSpotOverlay.setHidden(!hasAll);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals("cannon"))
|
||||
{
|
||||
if (!config.showInfobox())
|
||||
{
|
||||
removeCounter();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cannonPlaced)
|
||||
{
|
||||
clientThread.invoke(this::addCounter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged gameStateChanged)
|
||||
{
|
||||
if (gameStateChanged.getGameState() != GameState.LOGGED_IN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
spotPoints.clear();
|
||||
for (WorldPoint spot : CannonSpots.getCannonSpots())
|
||||
{
|
||||
if (WorldPoint.isInScene(client, spot.getX(), spot.getY()))
|
||||
{
|
||||
spotPoints.add(spot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameObjectSpawned(GameObjectSpawned event)
|
||||
{
|
||||
GameObject gameObject = event.getGameObject();
|
||||
|
||||
Player localPlayer = client.getLocalPlayer();
|
||||
if (gameObject.getId() == CANNON_BASE && !cannonPlaced)
|
||||
{
|
||||
if (localPlayer.getWorldLocation().distanceTo(gameObject.getWorldLocation()) <= 2
|
||||
&& localPlayer.getAnimation() == AnimationID.BURYING_BONES)
|
||||
{
|
||||
cannonPosition = gameObject.getWorldLocation();
|
||||
cannonWorld = client.getWorld();
|
||||
cannon = gameObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProjectileMoved(ProjectileMoved event)
|
||||
{
|
||||
Projectile projectile = event.getProjectile();
|
||||
|
||||
if ((projectile.getId() == CANNONBALL || projectile.getId() == GRANITE_CANNONBALL) && cannonPosition != null && cannonWorld == client.getWorld())
|
||||
{
|
||||
WorldPoint projectileLoc = WorldPoint.fromLocal(client, projectile.getX1(), projectile.getY1(), client.getPlane());
|
||||
|
||||
//Check to see if projectile x,y is 0 else it will continuously decrease while ball is flying.
|
||||
if (projectileLoc.equals(cannonPosition) && projectile.getX() == 0 && projectile.getY() == 0)
|
||||
{
|
||||
// When there's a chat message about cannon reloaded/unloaded/out of ammo,
|
||||
// the message event runs before the projectile event. However they run
|
||||
// in the opposite order on the server. So if both fires in the same tick,
|
||||
// we don't want to update the cannonball counter if it was set to a specific
|
||||
// amount.
|
||||
if (!skipProjectileCheckThisTick)
|
||||
{
|
||||
cballsLeft--;
|
||||
|
||||
if (config.showCannonNotifications() && !cannonBallNotificationSent && cballsLeft > 0 && config.lowWarningThreshold() >= cballsLeft)
|
||||
{
|
||||
notifier.notify(String.format("Your cannon has %d cannon balls remaining!", cballsLeft));
|
||||
cannonBallNotificationSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onChatMessage(ChatMessage event)
|
||||
{
|
||||
if (event.getType() != ChatMessageType.SPAM && event.getType() != ChatMessageType.GAMEMESSAGE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getMessage().equals("You add the furnace."))
|
||||
{
|
||||
cannonPlaced = true;
|
||||
addCounter();
|
||||
cballsLeft = 0;
|
||||
}
|
||||
|
||||
if (event.getMessage().contains("You pick up the cannon")
|
||||
|| event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!"))
|
||||
{
|
||||
cannonPlaced = false;
|
||||
cballsLeft = 0;
|
||||
removeCounter();
|
||||
}
|
||||
|
||||
if (event.getMessage().startsWith("You load the cannon with"))
|
||||
{
|
||||
Matcher m = NUMBER_PATTERN.matcher(event.getMessage());
|
||||
if (m.find())
|
||||
{
|
||||
// The cannon will usually refill to MAX_CBALLS, but if the
|
||||
// player didn't have enough cannonballs in their inventory,
|
||||
// it could fill up less than that. Filling the cannon to
|
||||
// cballsLeft + amt is not always accurate though because our
|
||||
// counter doesn't decrease if the player has been too far away
|
||||
// from the cannon due to the projectiels not being in memory,
|
||||
// so our counter can be higher than it is supposed to be.
|
||||
int amt = Integer.valueOf(m.group());
|
||||
if (cballsLeft + amt >= MAX_CBALLS)
|
||||
{
|
||||
skipProjectileCheckThisTick = true;
|
||||
cballsLeft = MAX_CBALLS;
|
||||
}
|
||||
else
|
||||
{
|
||||
cballsLeft += amt;
|
||||
}
|
||||
}
|
||||
else if (event.getMessage().equals("You load the cannon with one cannonball."))
|
||||
{
|
||||
if (cballsLeft + 1 >= MAX_CBALLS)
|
||||
{
|
||||
skipProjectileCheckThisTick = true;
|
||||
cballsLeft = MAX_CBALLS;
|
||||
}
|
||||
else
|
||||
{
|
||||
cballsLeft++;
|
||||
}
|
||||
}
|
||||
|
||||
cannonBallNotificationSent = false;
|
||||
}
|
||||
|
||||
if (event.getMessage().contains("Your cannon is out of ammo!"))
|
||||
{
|
||||
skipProjectileCheckThisTick = true;
|
||||
|
||||
// If the player was out of range of the cannon, some cannonballs
|
||||
// may have been used without the client knowing, so having this
|
||||
// extra check is a good idea.
|
||||
cballsLeft = 0;
|
||||
|
||||
if (config.showCannonNotifications())
|
||||
{
|
||||
notifier.notify("Your cannon is out of ammo!");
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getMessage().startsWith("You unload your cannon and receive Cannonball")
|
||||
|| event.getMessage().startsWith("You unload your cannon and receive Granite cannonball"))
|
||||
{
|
||||
skipProjectileCheckThisTick = true;
|
||||
|
||||
cballsLeft = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
skipProjectileCheckThisTick = false;
|
||||
}
|
||||
|
||||
Color getStateColor()
|
||||
{
|
||||
if (cballsLeft > 15)
|
||||
{
|
||||
return Color.green;
|
||||
}
|
||||
else if (cballsLeft > 5)
|
||||
{
|
||||
return Color.orange;
|
||||
}
|
||||
|
||||
return Color.red;
|
||||
}
|
||||
|
||||
private void addCounter()
|
||||
{
|
||||
if (!config.showInfobox() || counter != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
counter = new CannonCounter(itemManager.getImage(ItemID.CANNONBALL), this);
|
||||
counter.setTooltip("Cannonballs");
|
||||
|
||||
infoBoxManager.addInfoBox(counter);
|
||||
}
|
||||
|
||||
private void removeCounter()
|
||||
{
|
||||
if (counter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
infoBoxManager.removeInfoBox(counter);
|
||||
counter = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <https://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.cannon;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Polygon;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Setter;
|
||||
import net.runelite.api.Client;
|
||||
import static net.runelite.api.ItemID.CANNONBALL;
|
||||
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.game.ItemManager;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
|
||||
class CannonSpotOverlay extends Overlay
|
||||
{
|
||||
private static final int MAX_DISTANCE = 2350;
|
||||
|
||||
private final Client client;
|
||||
private final CannonPlugin plugin;
|
||||
private final CannonConfig config;
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private boolean hidden;
|
||||
|
||||
@Inject
|
||||
CannonSpotOverlay(Client client, CannonPlugin plugin, CannonConfig config)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
List<WorldPoint> spotPoints = plugin.getSpotPoints();
|
||||
|
||||
if (hidden || spotPoints.isEmpty() || !config.showCannonSpots() || plugin.isCannonPlaced())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (WorldPoint spot : spotPoints)
|
||||
{
|
||||
if (spot.getPlane() != client.getPlane())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
LocalPoint spotPoint = LocalPoint.fromWorld(client, spot);
|
||||
LocalPoint localLocation = client.getLocalPlayer().getLocalLocation();
|
||||
|
||||
if (spotPoint != null && localLocation.distanceTo(spotPoint) <= MAX_DISTANCE)
|
||||
{
|
||||
renderCannonSpot(graphics, client, spotPoint, itemManager.getImage(CANNONBALL), Color.RED);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void renderCannonSpot(Graphics2D graphics, Client client, LocalPoint point, BufferedImage image, Color color)
|
||||
{
|
||||
//Render tile
|
||||
Polygon poly = Perspective.getCanvasTilePoly(client, point);
|
||||
|
||||
if (poly != null)
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, poly, color);
|
||||
}
|
||||
|
||||
//Render icon
|
||||
Point imageLoc = Perspective.getCanvasImageLocation(client, point, image, 0);
|
||||
|
||||
if (imageLoc != null)
|
||||
{
|
||||
OverlayUtil.renderImageLocation(graphics, imageLoc, image);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Seth <https://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.cannon;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
|
||||
enum CannonSpots
|
||||
{
|
||||
ABERRANT_SPECTRES(new WorldPoint(2456, 9791, 0)),
|
||||
ANKOU(new WorldPoint(3177, 10193, 0)),
|
||||
BANDIT(new WorldPoint(3037, 3700, 0)),
|
||||
BEAR(new WorldPoint(3113, 3672, 0)),
|
||||
BLACK_DEMONS(new WorldPoint(2859, 9778, 0), new WorldPoint(2841, 9791, 0), new WorldPoint(1421, 10089, 1), new WorldPoint(3174, 10154, 0), new WorldPoint(3089, 9960, 0)),
|
||||
BLACK_DRAGON(new WorldPoint(3239, 10206, 0)),
|
||||
BLACK_KNIGHTS(new WorldPoint(2906, 9685, 0), new WorldPoint(3053, 3852, 0)),
|
||||
BLOODVELDS(new WorldPoint(2439, 9821, 0), new WorldPoint(2448, 9821, 0), new WorldPoint(2472, 9832, 0), new WorldPoint(2453, 9817, 0), new WorldPoint(3597, 9743, 0)),
|
||||
BLUE_DRAGON(new WorldPoint(1933, 8973, 1)),
|
||||
BRINE_RAT(new WorldPoint(2707, 10132, 0)),
|
||||
CAVE_HORROR(new WorldPoint(3785, 9460, 0)),
|
||||
DAGGANOTH(new WorldPoint(2524, 10020, 0)),
|
||||
DARK_BEAST(new WorldPoint(1992, 4655, 0)),
|
||||
DARK_WARRIOR(new WorldPoint(3030, 3632, 0)),
|
||||
DUST_DEVIL(new WorldPoint(3218, 9366, 0)),
|
||||
EARTH_WARRIOR(new WorldPoint(3120, 9987, 0)),
|
||||
ELDER_CHAOS_DRUID(new WorldPoint(3237, 3622, 0)),
|
||||
ELVES(new WorldPoint(2044, 4635, 0), new WorldPoint(3278, 6098, 0)),
|
||||
FIRE_GIANTS(new WorldPoint(2393, 9782, 0), new WorldPoint(2412, 9776, 0), new WorldPoint(2401, 9780, 0), new WorldPoint(3047, 10340, 0)),
|
||||
GREATER_DEMONS(new WorldPoint(1435, 10086, 2), new WorldPoint(3224, 10132, 0)),
|
||||
GREEN_DRAGON(new WorldPoint(3225, 10068, 0)),
|
||||
HELLHOUNDS(new WorldPoint(2431, 9776, 0), new WorldPoint(2413, 9786, 0), new WorldPoint(2783, 9686, 0), new WorldPoint(3198, 10071, 0)),
|
||||
HILL_GIANT(new WorldPoint(3044, 10318, 0)),
|
||||
ICE_GIANT(new WorldPoint(3207, 10164, 0)),
|
||||
ICE_WARRIOR(new WorldPoint(2955, 3876, 0)),
|
||||
KALPHITE(new WorldPoint(3307, 9528, 0)),
|
||||
LESSER_DEMON(new WorldPoint(2838, 9559, 0), new WorldPoint(3163, 10114, 0)),
|
||||
LIZARDMEN(new WorldPoint(1500, 3703, 0)),
|
||||
LIZARDMEN_SHAMAN(new WorldPoint(1423, 3715, 0)),
|
||||
MAGIC_AXE(new WorldPoint(3190, 3960, 0)),
|
||||
MAMMOTH(new WorldPoint(3168, 3595, 0)),
|
||||
MINIONS_OF_SCARABAS(new WorldPoint(3297, 9252, 0)),
|
||||
ROGUE(new WorldPoint(3285, 3930, 0)),
|
||||
SCORPION(new WorldPoint(3233, 10335, 0)),
|
||||
SKELETON(new WorldPoint(3018, 3592, 0)),
|
||||
SMOKE_DEVIL(new WorldPoint(2398, 9444, 0)),
|
||||
SPIDER(new WorldPoint(3169, 3886, 0)),
|
||||
SUQAHS(new WorldPoint(2114, 3943, 0)),
|
||||
TROLLS(new WorldPoint(2401, 3856, 0), new WorldPoint(1242, 3517, 0)),
|
||||
ZOMBIE(new WorldPoint(3172, 3677, 0));
|
||||
|
||||
@Getter
|
||||
private static final List<WorldPoint> cannonSpots = new ArrayList<>();
|
||||
|
||||
static
|
||||
{
|
||||
for (CannonSpots cannonSpot : values())
|
||||
{
|
||||
cannonSpots.addAll(Arrays.asList(cannonSpot.spots));
|
||||
}
|
||||
}
|
||||
|
||||
private final WorldPoint[] spots;
|
||||
|
||||
CannonSpots(WorldPoint... spots)
|
||||
{
|
||||
this.spots = spots;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Woox <https://github.com/wooxsolo>
|
||||
* 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.chatboxperformance;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.ScriptID;
|
||||
import net.runelite.api.events.ScriptCallbackEvent;
|
||||
import net.runelite.api.widgets.WidgetType;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.api.widgets.WidgetPositionMode;
|
||||
import net.runelite.api.widgets.WidgetSizeMode;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Chatbox performance",
|
||||
hidden = true
|
||||
)
|
||||
public class ChatboxPerformancePlugin extends Plugin
|
||||
{
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Override
|
||||
public void startUp()
|
||||
{
|
||||
if (client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
clientThread.invokeLater(() -> client.runScript(ScriptID.MESSAGE_LAYER_CLOSE, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutDown()
|
||||
{
|
||||
if (client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
clientThread.invokeLater(() -> client.runScript(ScriptID.MESSAGE_LAYER_CLOSE, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
private void onScriptCallbackEvent(ScriptCallbackEvent ev)
|
||||
{
|
||||
if (!"chatboxBackgroundBuilt".equals(ev.getEventName()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixDarkBackground();
|
||||
fixWhiteLines(true);
|
||||
fixWhiteLines(false);
|
||||
}
|
||||
|
||||
private void fixDarkBackground()
|
||||
{
|
||||
int currOpacity = 256;
|
||||
int prevY = 0;
|
||||
Widget[] children = client.getWidget(WidgetInfo.CHATBOX_TRANSPARENT_BACKGROUND).getDynamicChildren();
|
||||
Widget prev = null;
|
||||
for (Widget w : children)
|
||||
{
|
||||
if (w.getType() != WidgetType.RECTANGLE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev != null)
|
||||
{
|
||||
int relY = w.getRelativeY();
|
||||
prev.setHeightMode(WidgetSizeMode.ABSOLUTE);
|
||||
prev.setYPositionMode(WidgetPositionMode.ABSOLUTE_TOP);
|
||||
prev.setRelativeY(prevY);
|
||||
prev.setOriginalY(prev.getRelativeY());
|
||||
prev.setHeight(relY - prevY);
|
||||
prev.setOriginalHeight(prev.getHeight());
|
||||
prev.setOpacity(currOpacity);
|
||||
}
|
||||
|
||||
prevY = w.getRelativeY();
|
||||
currOpacity -= 3; // Rough number, can't get exactly the same as Jagex because of rounding
|
||||
prev = w;
|
||||
}
|
||||
if (prev != null)
|
||||
{
|
||||
prev.setOpacity(currOpacity);
|
||||
}
|
||||
}
|
||||
|
||||
private void fixWhiteLines(boolean upperLine)
|
||||
{
|
||||
int currOpacity = 256;
|
||||
int prevWidth = 0;
|
||||
Widget[] children = client.getWidget(WidgetInfo.CHATBOX_TRANSPARENT_LINES).getDynamicChildren();
|
||||
Widget prev = null;
|
||||
for (Widget w : children)
|
||||
{
|
||||
if (w.getType() != WidgetType.RECTANGLE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((w.getRelativeY() == 0 && !upperLine) ||
|
||||
(w.getRelativeY() != 0 && upperLine))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev != null)
|
||||
{
|
||||
int width = w.getWidth();
|
||||
prev.setWidthMode(WidgetSizeMode.ABSOLUTE);
|
||||
prev.setRelativeX(width);
|
||||
prev.setOriginalX(width);
|
||||
prev.setWidth(prevWidth - width);
|
||||
prev.setOriginalWidth(prev.getWidth());
|
||||
prev.setOpacity(currOpacity);
|
||||
}
|
||||
|
||||
prevWidth = w.getWidth();
|
||||
|
||||
currOpacity -= upperLine ? 3 : 4; // Rough numbers, can't get exactly the same as Jagex because of rounding
|
||||
prev = w;
|
||||
}
|
||||
if (prev != null)
|
||||
{
|
||||
prev.setOpacity(currOpacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.chatcommands;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.Keybind;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
@ConfigGroup("chatcommands")
|
||||
public interface ChatCommandsConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
position = 0,
|
||||
keyName = "price",
|
||||
name = "Price Command",
|
||||
description = "Configures whether the Price command is enabled<br> !price [item]"
|
||||
)
|
||||
default boolean price()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 1,
|
||||
keyName = "lvl",
|
||||
name = "Level Command",
|
||||
description = "Configures whether the Level command is enabled<br> !lvl [skill]"
|
||||
)
|
||||
default boolean lvl()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 2,
|
||||
keyName = "clue",
|
||||
name = "Clue Command",
|
||||
description = "Configures whether the Clue command is enabled<br> !clues"
|
||||
)
|
||||
default boolean clue()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 3,
|
||||
keyName = "killcount",
|
||||
name = "Killcount Command",
|
||||
description = "Configures whether the Killcount command is enabled<br> !kc [boss]"
|
||||
)
|
||||
default boolean killcount()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 4,
|
||||
keyName = "qp",
|
||||
name = "QP Command",
|
||||
description = "Configures whether the quest point command is enabled<br> !qp"
|
||||
)
|
||||
default boolean qp()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 5,
|
||||
keyName = "pb",
|
||||
name = "PB Command",
|
||||
description = "Configures whether the personal best command is enabled<br> !pb"
|
||||
)
|
||||
default boolean pb()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 6,
|
||||
keyName = "gc",
|
||||
name = "GC Command",
|
||||
description = "Configures whether the Barbarian Assault High gamble count command is enabled<br> !gc"
|
||||
)
|
||||
default boolean gc()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 7,
|
||||
keyName = "duels",
|
||||
name = "Duels Command",
|
||||
description = "Configures whether the duel arena command is enabled<br> !duels"
|
||||
)
|
||||
default boolean duels()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 8,
|
||||
keyName = "bh",
|
||||
name = "BH Command",
|
||||
description = "Configures whether the Bounty Hunter - Hunter command is enabled<br> !bh"
|
||||
)
|
||||
default boolean bh()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 9,
|
||||
keyName = "bhRogue",
|
||||
name = "BH Rogue Command",
|
||||
description = "Configures whether the Bounty Hunter - Rogue command is enabled<br> !bhrogue"
|
||||
)
|
||||
default boolean bhRogue()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 10,
|
||||
keyName = "lms",
|
||||
name = "LMS Command",
|
||||
description = "Configures whether the Last Man Standing command is enabled<br> !lms"
|
||||
)
|
||||
default boolean lms()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 11,
|
||||
keyName = "lp",
|
||||
name = "LP Command",
|
||||
description = "Configures whether the League Points command is enabled<br> !lp"
|
||||
)
|
||||
default boolean lp()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 12,
|
||||
keyName = "clearSingleWord",
|
||||
name = "Clear Single Word",
|
||||
description = "Enable hot key to clear single word at a time"
|
||||
)
|
||||
default Keybind clearSingleWord()
|
||||
{
|
||||
return new Keybind(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK);
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 13,
|
||||
keyName = "clearEntireChatBox",
|
||||
name = "Clear Chat Box",
|
||||
description = "Enable hotkey to clear entire chat box"
|
||||
)
|
||||
default Keybind clearChatBox()
|
||||
{
|
||||
return new Keybind(KeyEvent.VK_BACK_SPACE, InputEvent.CTRL_DOWN_MASK);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.chatcommands;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
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.client.callback.ClientThread;
|
||||
import net.runelite.client.input.KeyListener;
|
||||
|
||||
@Singleton
|
||||
public class ChatKeyboardListener implements KeyListener
|
||||
{
|
||||
@Inject
|
||||
private ChatCommandsConfig chatCommandsConfig;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e)
|
||||
{
|
||||
if (chatCommandsConfig.clearSingleWord().matches(e))
|
||||
{
|
||||
int inputTye = client.getVar(VarClientInt.INPUT_TYPE);
|
||||
String input = inputTye == InputType.NONE.getType()
|
||||
? client.getVar(VarClientStr.CHATBOX_TYPED_TEXT)
|
||||
: client.getVar(VarClientStr.INPUT_TEXT);
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
// remove trailing space
|
||||
while (input.endsWith(" "))
|
||||
{
|
||||
input = input.substring(0, input.length() - 1);
|
||||
}
|
||||
|
||||
// find next word
|
||||
int idx = input.lastIndexOf(' ') + 1;
|
||||
final String replacement = input.substring(0, idx);
|
||||
|
||||
clientThread.invoke(() -> applyText(inputTye, replacement));
|
||||
}
|
||||
}
|
||||
else if (chatCommandsConfig.clearChatBox().matches(e))
|
||||
{
|
||||
int inputTye = client.getVar(VarClientInt.INPUT_TYPE);
|
||||
clientThread.invoke(() -> applyText(inputTye, ""));
|
||||
}
|
||||
}
|
||||
|
||||
private void applyText(int inputType, String replacement)
|
||||
{
|
||||
if (inputType == InputType.NONE.getType())
|
||||
{
|
||||
client.setVar(VarClientStr.CHATBOX_TYPED_TEXT, replacement);
|
||||
client.runScript(ScriptID.CHAT_PROMPT_INIT);
|
||||
}
|
||||
else if (inputType == InputType.PRIVATE_MESSAGE.getType())
|
||||
{
|
||||
client.setVar(VarClientStr.INPUT_TEXT, replacement);
|
||||
client.runScript(ScriptID.CHAT_TEXT_INPUT_REBUILD, "");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Adam <Adam@sigterm.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.chatcommands;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.Map;
|
||||
import net.runelite.api.Skill;
|
||||
|
||||
class SkillAbbreviations
|
||||
{
|
||||
private static final Map<String, String> MAP;
|
||||
|
||||
static
|
||||
{
|
||||
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
|
||||
builder.put("ATK", Skill.ATTACK.getName());
|
||||
builder.put("ATT", Skill.ATTACK.getName());
|
||||
builder.put("DEF", Skill.DEFENCE.getName());
|
||||
builder.put("STR", Skill.STRENGTH.getName());
|
||||
builder.put("HEALTH", Skill.HITPOINTS.getName());
|
||||
builder.put("HIT", Skill.HITPOINTS.getName());
|
||||
builder.put("HITPOINT", Skill.HITPOINTS.getName());
|
||||
builder.put("HP", Skill.HITPOINTS.getName());
|
||||
builder.put("RANGE", Skill.RANGED.getName());
|
||||
builder.put("RANGING", Skill.RANGED.getName());
|
||||
builder.put("RNG", Skill.RANGED.getName());
|
||||
builder.put("PRAY", Skill.PRAYER.getName());
|
||||
builder.put("MAG", Skill.MAGIC.getName());
|
||||
builder.put("MAGE", Skill.MAGIC.getName());
|
||||
builder.put("COOK", Skill.COOKING.getName());
|
||||
builder.put("WC", Skill.WOODCUTTING.getName());
|
||||
builder.put("WOOD", Skill.WOODCUTTING.getName());
|
||||
builder.put("WOODCUT", Skill.WOODCUTTING.getName());
|
||||
builder.put("FLETCH", Skill.FLETCHING.getName());
|
||||
builder.put("FISH", Skill.FISHING.getName());
|
||||
builder.put("FM", Skill.FIREMAKING.getName());
|
||||
builder.put("FIRE", Skill.FIREMAKING.getName());
|
||||
builder.put("CRAFT", Skill.CRAFTING.getName());
|
||||
builder.put("SMITH", Skill.SMITHING.getName());
|
||||
builder.put("MINE", Skill.MINING.getName());
|
||||
builder.put("HL", Skill.HERBLORE.getName());
|
||||
builder.put("HERB", Skill.HERBLORE.getName());
|
||||
builder.put("AGI", Skill.AGILITY.getName());
|
||||
builder.put("AGIL", Skill.AGILITY.getName());
|
||||
builder.put("THIEF", Skill.THIEVING.getName());
|
||||
builder.put("SLAY", Skill.SLAYER.getName());
|
||||
builder.put("FARM", Skill.FARMING.getName());
|
||||
builder.put("RC", Skill.RUNECRAFT.getName());
|
||||
builder.put("RUNE", Skill.RUNECRAFT.getName());
|
||||
builder.put("RUNECRAFTING", Skill.RUNECRAFT.getName());
|
||||
builder.put("HUNT", Skill.HUNTER.getName());
|
||||
builder.put("CON", Skill.CONSTRUCTION.getName());
|
||||
builder.put("CONSTRUCT", Skill.CONSTRUCTION.getName());
|
||||
builder.put("ALL", Skill.OVERALL.getName());
|
||||
builder.put("TOTAL", Skill.OVERALL.getName());
|
||||
MAP = builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a string representing the name of a skill, and if abbreviated,
|
||||
* expands it into its full canonical name. Case-insensitive.
|
||||
*
|
||||
* @param abbrev Skill name that may be abbreviated.
|
||||
* @return Full skill name if recognized, else the original string.
|
||||
*/
|
||||
static String getFullName(String abbrev)
|
||||
{
|
||||
return MAP.getOrDefault(abbrev.toUpperCase(), abbrev);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Magic fTail
|
||||
* Copyright (c) 2019, osrs-music-map <osrs-music-map@users.noreply.github.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.chatfilter;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
import net.runelite.client.config.ConfigSection;
|
||||
|
||||
@ConfigGroup("chatfilter")
|
||||
public interface ChatFilterConfig extends Config
|
||||
{
|
||||
@ConfigSection(
|
||||
name = "Filter Lists",
|
||||
description = "Custom Word, Regex, and Username filter lists",
|
||||
position = 0,
|
||||
closedByDefault = true
|
||||
)
|
||||
String filterLists = "filterLists";
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "filteredWords",
|
||||
name = "Filtered Words",
|
||||
description = "List of filtered words, separated by commas",
|
||||
position = 1,
|
||||
section = filterLists
|
||||
)
|
||||
default String filteredWords()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "filteredRegex",
|
||||
name = "Filtered Regex",
|
||||
description = "List of regular expressions to filter, one per line",
|
||||
position = 2,
|
||||
section = filterLists
|
||||
)
|
||||
default String filteredRegex()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "filteredNames",
|
||||
name = "Filtered Names",
|
||||
description = "List of filtered names, one per line. Accepts regular expressions",
|
||||
position = 3,
|
||||
section = filterLists
|
||||
)
|
||||
default String filteredNames()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "filterType",
|
||||
name = "Filter type",
|
||||
description = "Configures how the messages are filtered",
|
||||
position = 4
|
||||
)
|
||||
default ChatFilterType filterType()
|
||||
{
|
||||
return ChatFilterType.CENSOR_WORDS;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "filterFriends",
|
||||
name = "Filter Friends",
|
||||
description = "Filter your friends' messages",
|
||||
position = 5
|
||||
)
|
||||
default boolean filterFriends()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "filterClan",
|
||||
name = "Filter Friends Chat Members",
|
||||
description = "Filter your friends chat members' messages",
|
||||
position = 6
|
||||
)
|
||||
default boolean filterFriendsChat()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "filterLogin",
|
||||
name = "Filter Logged In/Out Messages",
|
||||
description = "Filter your private chat to remove logged in/out messages",
|
||||
position = 7
|
||||
)
|
||||
default boolean filterLogin()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "filterGameChat",
|
||||
name = "Filter Game Chat",
|
||||
description = "Filter your game chat messages",
|
||||
position = 8
|
||||
)
|
||||
default boolean filterGameChat()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "collapseGameChat",
|
||||
name = "Collapse Game Chat",
|
||||
description = "Collapse duplicate game chat messages into a single line",
|
||||
position = 9
|
||||
)
|
||||
default boolean collapseGameChat()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "collapsePlayerChat",
|
||||
name = "Collapse Player Chat",
|
||||
description = "Collapse duplicate player chat messages into a single line",
|
||||
position = 10
|
||||
)
|
||||
default boolean collapsePlayerChat()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "maxRepeatedPublicChats",
|
||||
name = "Max repeated public chats",
|
||||
description = "Block player chat message if repeated this many times. 0 is off",
|
||||
position = 11
|
||||
)
|
||||
default int maxRepeatedPublicChats()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Magic fTail
|
||||
* Copyright (c) 2019, osrs-music-map <osrs-music-map@users.noreply.github.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.chatfilter;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Provides;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import static net.runelite.api.ChatMessageType.ENGINE;
|
||||
import static net.runelite.api.ChatMessageType.GAMEMESSAGE;
|
||||
import static net.runelite.api.ChatMessageType.ITEM_EXAMINE;
|
||||
import static net.runelite.api.ChatMessageType.MODCHAT;
|
||||
import static net.runelite.api.ChatMessageType.NPC_EXAMINE;
|
||||
import static net.runelite.api.ChatMessageType.OBJECT_EXAMINE;
|
||||
import static net.runelite.api.ChatMessageType.PUBLICCHAT;
|
||||
import static net.runelite.api.ChatMessageType.SPAM;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.MessageNode;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.OverheadTextChanged;
|
||||
import net.runelite.api.events.ScriptCallbackEvent;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.events.ConfigChanged;
|
||||
import net.runelite.client.game.FriendChatManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.util.Text;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Chat Filter",
|
||||
description = "Censor user configurable words or patterns from chat",
|
||||
enabledByDefault = false
|
||||
)
|
||||
public class ChatFilterPlugin extends Plugin
|
||||
{
|
||||
private static final Splitter NEWLINE_SPLITTER = Splitter
|
||||
.on("\n")
|
||||
.omitEmptyStrings()
|
||||
.trimResults();
|
||||
|
||||
@VisibleForTesting
|
||||
static final String CENSOR_MESSAGE = "Hey, everyone, I just tried to say something very silly!";
|
||||
|
||||
private static final Set<ChatMessageType> COLLAPSIBLE_MESSAGETYPES = ImmutableSet.of(
|
||||
ENGINE,
|
||||
GAMEMESSAGE,
|
||||
ITEM_EXAMINE,
|
||||
NPC_EXAMINE,
|
||||
OBJECT_EXAMINE,
|
||||
SPAM,
|
||||
PUBLICCHAT,
|
||||
MODCHAT
|
||||
);
|
||||
|
||||
private final CharMatcher jagexPrintableCharMatcher = Text.JAGEX_PRINTABLE_CHAR_MATCHER;
|
||||
private final List<Pattern> filteredPatterns = new ArrayList<>();
|
||||
private final List<Pattern> filteredNamePatterns = new ArrayList<>();
|
||||
|
||||
private static class Duplicate
|
||||
{
|
||||
int messageId;
|
||||
int count;
|
||||
}
|
||||
|
||||
private final LinkedHashMap<String, Duplicate> duplicateChatCache = new LinkedHashMap<String, Duplicate>()
|
||||
{
|
||||
private static final int MAX_ENTRIES = 100;
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<String, Duplicate> eldest)
|
||||
{
|
||||
return size() > MAX_ENTRIES;
|
||||
}
|
||||
};
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ChatFilterConfig config;
|
||||
|
||||
@Inject
|
||||
private FriendChatManager friendChatManager;
|
||||
|
||||
@Provides
|
||||
ChatFilterConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(ChatFilterConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
updateFilteredPatterns();
|
||||
client.refreshChat();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
filteredPatterns.clear();
|
||||
duplicateChatCache.clear();
|
||||
client.refreshChat();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onScriptCallbackEvent(ScriptCallbackEvent event)
|
||||
{
|
||||
if (!"chatFilterCheck".equals(event.getEventName()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int[] intStack = client.getIntStack();
|
||||
int intStackSize = client.getIntStackSize();
|
||||
String[] stringStack = client.getStringStack();
|
||||
int stringStackSize = client.getStringStackSize();
|
||||
|
||||
final int messageType = intStack[intStackSize - 2];
|
||||
final int messageId = intStack[intStackSize - 1];
|
||||
String message = stringStack[stringStackSize - 1];
|
||||
|
||||
ChatMessageType chatMessageType = ChatMessageType.of(messageType);
|
||||
final MessageNode messageNode = client.getMessages().get(messageId);
|
||||
final String name = messageNode.getName();
|
||||
int duplicateCount = 0;
|
||||
boolean blockMessage = false;
|
||||
|
||||
// Only filter public chat and private messages
|
||||
switch (chatMessageType)
|
||||
{
|
||||
case PUBLICCHAT:
|
||||
case MODCHAT:
|
||||
case AUTOTYPER:
|
||||
case PRIVATECHAT:
|
||||
case MODPRIVATECHAT:
|
||||
case FRIENDSCHAT:
|
||||
if (shouldFilterPlayerMessage(Text.removeTags(name)))
|
||||
{
|
||||
message = censorMessage(name, message);
|
||||
blockMessage = message == null;
|
||||
}
|
||||
break;
|
||||
case GAMEMESSAGE:
|
||||
case ENGINE:
|
||||
case ITEM_EXAMINE:
|
||||
case NPC_EXAMINE:
|
||||
case OBJECT_EXAMINE:
|
||||
case SPAM:
|
||||
if (config.filterGameChat())
|
||||
{
|
||||
message = censorMessage(null, message);
|
||||
blockMessage = message == null;
|
||||
}
|
||||
break;
|
||||
case LOGINLOGOUTNOTIFICATION:
|
||||
if (config.filterLogin())
|
||||
{
|
||||
blockMessage = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
boolean shouldCollapse = chatMessageType == PUBLICCHAT || chatMessageType == MODCHAT
|
||||
? config.collapsePlayerChat()
|
||||
: COLLAPSIBLE_MESSAGETYPES.contains(chatMessageType) && config.collapseGameChat();
|
||||
if (!blockMessage && shouldCollapse)
|
||||
{
|
||||
Duplicate duplicateCacheEntry = duplicateChatCache.get(name + ":" + message);
|
||||
if (duplicateCacheEntry != null)
|
||||
{
|
||||
blockMessage = duplicateCacheEntry.messageId != messageId ||
|
||||
((chatMessageType == PUBLICCHAT || chatMessageType == MODCHAT) &&
|
||||
config.maxRepeatedPublicChats() > 0 && duplicateCacheEntry.count > config.maxRepeatedPublicChats());
|
||||
duplicateCount = duplicateCacheEntry.count;
|
||||
}
|
||||
}
|
||||
|
||||
if (blockMessage)
|
||||
{
|
||||
// Block the message
|
||||
intStack[intStackSize - 3] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Replace the message
|
||||
if (duplicateCount > 1)
|
||||
{
|
||||
message += " (" + duplicateCount + ")";
|
||||
}
|
||||
|
||||
stringStack[stringStackSize - 1] = message;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onOverheadTextChanged(OverheadTextChanged event)
|
||||
{
|
||||
if (!(event.getActor() instanceof Player) || !shouldFilterPlayerMessage(event.getActor().getName()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String message = censorMessage(event.getActor().getName(), event.getOverheadText());
|
||||
|
||||
if (message == null)
|
||||
{
|
||||
message = " ";
|
||||
}
|
||||
|
||||
event.getActor().setOverheadText(message);
|
||||
}
|
||||
|
||||
@Subscribe(priority = -2) // run after ChatMessageManager
|
||||
public void onChatMessage(ChatMessage chatMessage)
|
||||
{
|
||||
if (COLLAPSIBLE_MESSAGETYPES.contains(chatMessage.getType()))
|
||||
{
|
||||
final MessageNode messageNode = chatMessage.getMessageNode();
|
||||
// remove and re-insert into map to move to end of list
|
||||
final String key = messageNode.getName() + ":" + messageNode.getValue();
|
||||
Duplicate duplicate = duplicateChatCache.remove(key);
|
||||
if (duplicate == null)
|
||||
{
|
||||
duplicate = new Duplicate();
|
||||
}
|
||||
|
||||
duplicate.count++;
|
||||
duplicate.messageId = messageNode.getId();
|
||||
duplicateChatCache.put(key, duplicate);
|
||||
}
|
||||
}
|
||||
|
||||
boolean shouldFilterPlayerMessage(String playerName)
|
||||
{
|
||||
boolean isMessageFromSelf = playerName.equals(client.getLocalPlayer().getName());
|
||||
return !isMessageFromSelf &&
|
||||
(config.filterFriends() || !client.isFriended(playerName, false)) &&
|
||||
(config.filterFriendsChat() || !friendChatManager.isMember(playerName));
|
||||
}
|
||||
|
||||
String censorMessage(final String username, final String message)
|
||||
{
|
||||
String strippedMessage = jagexPrintableCharMatcher.retainFrom(message)
|
||||
.replace('\u00A0', ' ');
|
||||
if (username != null && shouldFilterByName(username))
|
||||
{
|
||||
switch (config.filterType())
|
||||
{
|
||||
case CENSOR_WORDS:
|
||||
return StringUtils.repeat('*', strippedMessage.length());
|
||||
case CENSOR_MESSAGE:
|
||||
return CENSOR_MESSAGE;
|
||||
case REMOVE_MESSAGE:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
boolean filtered = false;
|
||||
for (Pattern pattern : filteredPatterns)
|
||||
{
|
||||
Matcher m = pattern.matcher(strippedMessage);
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
while (m.find())
|
||||
{
|
||||
switch (config.filterType())
|
||||
{
|
||||
case CENSOR_WORDS:
|
||||
m.appendReplacement(sb, StringUtils.repeat('*', m.group(0).length()));
|
||||
filtered = true;
|
||||
break;
|
||||
case CENSOR_MESSAGE:
|
||||
return CENSOR_MESSAGE;
|
||||
case REMOVE_MESSAGE:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
m.appendTail(sb);
|
||||
|
||||
strippedMessage = sb.toString();
|
||||
}
|
||||
|
||||
return filtered ? strippedMessage : message;
|
||||
}
|
||||
|
||||
void updateFilteredPatterns()
|
||||
{
|
||||
filteredPatterns.clear();
|
||||
filteredNamePatterns.clear();
|
||||
|
||||
Text.fromCSV(config.filteredWords()).stream()
|
||||
.map(s -> Pattern.compile(Pattern.quote(s), Pattern.CASE_INSENSITIVE))
|
||||
.forEach(filteredPatterns::add);
|
||||
|
||||
NEWLINE_SPLITTER.splitToList(config.filteredRegex()).stream()
|
||||
.map(ChatFilterPlugin::compilePattern)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(filteredPatterns::add);
|
||||
|
||||
NEWLINE_SPLITTER.splitToList(config.filteredNames()).stream()
|
||||
.map(ChatFilterPlugin::compilePattern)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(filteredNamePatterns::add);
|
||||
}
|
||||
|
||||
private static Pattern compilePattern(String pattern)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
catch (PatternSyntaxException ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (!"chatfilter".equals(event.getGroup()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
updateFilteredPatterns();
|
||||
|
||||
//Refresh chat after config change to reflect current rules
|
||||
client.refreshChat();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean shouldFilterByName(final String playerName)
|
||||
{
|
||||
String sanitizedName = Text.standardize(playerName);
|
||||
for (Pattern pattern : filteredNamePatterns)
|
||||
{
|
||||
Matcher m = pattern.matcher(sanitizedName);
|
||||
if (m.find())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Magic fTail
|
||||
* 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.chatfilter;
|
||||
|
||||
public enum ChatFilterType
|
||||
{
|
||||
CENSOR_WORDS,
|
||||
CENSOR_MESSAGE,
|
||||
REMOVE_MESSAGE
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.chathistory;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("chathistory")
|
||||
public interface ChatHistoryConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "retainChatHistory",
|
||||
name = "Retain Chat History",
|
||||
description = "Retains chat history when logging in/out or world hopping",
|
||||
position = 0
|
||||
)
|
||||
default boolean retainChatHistory()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "pmTargetCycling",
|
||||
name = "PM Target Cycling",
|
||||
description = "Pressing Tab while sending a PM will cycle the target username based on PM history",
|
||||
position = 1
|
||||
)
|
||||
default boolean pmTargetCycling()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "copyToClipboard",
|
||||
name = "Copy to clipboard",
|
||||
description = "Add option on chat messages to copy them to clipboard",
|
||||
position = 2
|
||||
)
|
||||
default boolean copyToClipboard()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "clearHistory",
|
||||
name = "Clear history option for all tabs",
|
||||
description = "Add 'Clear history' option chatbox tab buttons",
|
||||
position = 3
|
||||
)
|
||||
default boolean clearHistory()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Tomas Slusny <slusnucky@gmail.com>
|
||||
* Copyright (c) 2020, Anthony <https://github.com/while-loop>
|
||||
* 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.chathistory;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.EvictingQueue;
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.Color;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.Queue;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.ChatLineBuffer;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.MenuAction;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.MessageNode;
|
||||
import net.runelite.api.ScriptID;
|
||||
import net.runelite.api.VarClientInt;
|
||||
import net.runelite.api.VarClientStr;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.MenuEntryAdded;
|
||||
import net.runelite.api.events.MenuOpened;
|
||||
import net.runelite.api.events.MenuOptionClicked;
|
||||
import net.runelite.api.vars.InputType;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import static net.runelite.api.widgets.WidgetInfo.TO_CHILD;
|
||||
import static net.runelite.api.widgets.WidgetInfo.TO_GROUP;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
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.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.ColorUtil;
|
||||
import net.runelite.client.util.Text;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Chat History",
|
||||
description = "Retain your chat history when logging in/out or world hopping",
|
||||
tags = {"chat", "history", "retain", "cycle", "pm"}
|
||||
)
|
||||
public class ChatHistoryPlugin extends Plugin implements KeyListener
|
||||
{
|
||||
private static final String WELCOME_MESSAGE = "Welcome to Old School RuneScape";
|
||||
private static final String CLEAR_HISTORY = "Clear history";
|
||||
private static final String COPY_TO_CLIPBOARD = "Copy to clipboard";
|
||||
private static final int CYCLE_HOTKEY = KeyEvent.VK_TAB;
|
||||
private static final int FRIENDS_MAX_SIZE = 5;
|
||||
|
||||
private Queue<QueuedMessage> messageQueue;
|
||||
private Deque<String> friends;
|
||||
|
||||
private String currentMessage = null;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private ChatHistoryConfig config;
|
||||
|
||||
@Inject
|
||||
private KeyManager keyManager;
|
||||
|
||||
@Inject
|
||||
private ChatMessageManager chatMessageManager;
|
||||
|
||||
@Provides
|
||||
ChatHistoryConfig getConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(ChatHistoryConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp()
|
||||
{
|
||||
messageQueue = EvictingQueue.create(100);
|
||||
friends = new ArrayDeque<>(FRIENDS_MAX_SIZE + 1);
|
||||
keyManager.registerKeyListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
messageQueue.clear();
|
||||
messageQueue = null;
|
||||
friends.clear();
|
||||
friends = null;
|
||||
currentMessage = null;
|
||||
keyManager.unregisterKeyListener(this);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onChatMessage(ChatMessage chatMessage)
|
||||
{
|
||||
// Start sending old messages right after the welcome message, as that is most reliable source
|
||||
// of information that chat history was reset
|
||||
ChatMessageType chatMessageType = chatMessage.getType();
|
||||
if (chatMessageType == ChatMessageType.WELCOME && StringUtils.startsWithIgnoreCase(chatMessage.getMessage(), WELCOME_MESSAGE))
|
||||
{
|
||||
if (!config.retainChatHistory())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QueuedMessage queuedMessage;
|
||||
|
||||
while ((queuedMessage = messageQueue.poll()) != null)
|
||||
{
|
||||
chatMessageManager.queue(queuedMessage);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch (chatMessageType)
|
||||
{
|
||||
case PRIVATECHATOUT:
|
||||
case PRIVATECHAT:
|
||||
case MODPRIVATECHAT:
|
||||
final String name = Text.removeTags(chatMessage.getName());
|
||||
// Remove to ensure uniqueness & its place in history
|
||||
if (!friends.remove(name))
|
||||
{
|
||||
// If the friend didn't previously exist ensure deque capacity doesn't increase by adding them
|
||||
if (friends.size() >= FRIENDS_MAX_SIZE)
|
||||
{
|
||||
friends.remove();
|
||||
}
|
||||
}
|
||||
friends.add(name);
|
||||
// intentional fall-through
|
||||
case PUBLICCHAT:
|
||||
case MODCHAT:
|
||||
case FRIENDSCHAT:
|
||||
case CONSOLE:
|
||||
final QueuedMessage queuedMessage = QueuedMessage.builder()
|
||||
.type(chatMessageType)
|
||||
.name(chatMessage.getName())
|
||||
.sender(chatMessage.getSender())
|
||||
.value(nbsp(chatMessage.getMessage()))
|
||||
.runeLiteFormattedMessage(nbsp(chatMessage.getMessageNode().getRuneLiteFormatMessage()))
|
||||
.timestamp(chatMessage.getTimestamp())
|
||||
.build();
|
||||
|
||||
if (!messageQueue.contains(queuedMessage))
|
||||
{
|
||||
messageQueue.offer(queuedMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMenuOpened(MenuOpened event)
|
||||
{
|
||||
if (event.getMenuEntries().length < 2 || !config.copyToClipboard())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Use second entry as first one can be walk here with transparent chatbox
|
||||
final MenuEntry entry = event.getMenuEntries()[event.getMenuEntries().length - 2];
|
||||
|
||||
if (entry.getType() != MenuAction.CC_OP_LOW_PRIORITY.getId() && entry.getType() != MenuAction.RUNELITE.getId())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final int groupId = TO_GROUP(entry.getParam1());
|
||||
final int childId = TO_CHILD(entry.getParam1());
|
||||
|
||||
if (groupId != WidgetInfo.CHATBOX.getGroupId())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final Widget widget = client.getWidget(groupId, childId);
|
||||
final Widget parent = widget.getParent();
|
||||
|
||||
if (WidgetInfo.CHATBOX_MESSAGE_LINES.getId() != parent.getId())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get child id of first chat message static child so we can substract this offset to link to dynamic child
|
||||
// later
|
||||
final int first = WidgetInfo.CHATBOX_FIRST_MESSAGE.getChildId();
|
||||
|
||||
// Convert current message static widget id to dynamic widget id of message node with message contents
|
||||
// When message is right clicked, we are actually right clicking static widget that contains only sender.
|
||||
// The actual message contents are stored in dynamic widgets that follow same order as static widgets.
|
||||
// Every first dynamic widget is message sender and every second one is message contents.
|
||||
final int dynamicChildId = (childId - first) * 2 + 1;
|
||||
|
||||
// Extract and store message contents when menu is opened because dynamic children can change while right click
|
||||
// menu is open and dynamicChildId will be outdated
|
||||
final Widget messageContents = parent.getChild(dynamicChildId);
|
||||
if (messageContents == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
currentMessage = messageContents.getText();
|
||||
|
||||
final MenuEntry menuEntry = new MenuEntry();
|
||||
menuEntry.setOption(COPY_TO_CLIPBOARD);
|
||||
menuEntry.setTarget(entry.getTarget());
|
||||
menuEntry.setType(MenuAction.RUNELITE.getId());
|
||||
menuEntry.setParam0(entry.getParam0());
|
||||
menuEntry.setParam1(entry.getParam1());
|
||||
menuEntry.setIdentifier(entry.getIdentifier());
|
||||
client.setMenuEntries(ArrayUtils.insert(1, client.getMenuEntries(), menuEntry));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMenuOptionClicked(MenuOptionClicked event)
|
||||
{
|
||||
final String menuOption = event.getMenuOption();
|
||||
|
||||
// The menu option for clear history is "<col=ffff00>Public:</col> Clear history"
|
||||
if (menuOption.endsWith(CLEAR_HISTORY))
|
||||
{
|
||||
clearChatboxHistory(ChatboxTab.of(event.getWidgetId()));
|
||||
}
|
||||
else if (COPY_TO_CLIPBOARD.equals(menuOption) && !Strings.isNullOrEmpty(currentMessage))
|
||||
{
|
||||
final StringSelection stringSelection = new StringSelection(Text.removeTags(currentMessage));
|
||||
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMenuEntryAdded(MenuEntryAdded entry)
|
||||
{
|
||||
final ChatboxTab tab = ChatboxTab.of(entry.getActionParam1());
|
||||
|
||||
if (tab == null || !config.clearHistory() || !Text.removeTags(entry.getOption()).equals(tab.getAfter()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final MenuEntry clearEntry = new MenuEntry();
|
||||
clearEntry.setTarget("");
|
||||
clearEntry.setType(MenuAction.RUNELITE.getId());
|
||||
clearEntry.setParam0(entry.getActionParam0());
|
||||
clearEntry.setParam1(entry.getActionParam1());
|
||||
|
||||
if (tab == ChatboxTab.GAME)
|
||||
{
|
||||
// keep type as the original CC_OP to correctly group "Game: Clear history" with
|
||||
// other tab "Game: *" options.
|
||||
clearEntry.setType(entry.getType());
|
||||
}
|
||||
|
||||
final StringBuilder messageBuilder = new StringBuilder();
|
||||
|
||||
if (tab != ChatboxTab.ALL)
|
||||
{
|
||||
messageBuilder.append(ColorUtil.wrapWithColorTag(tab.getName() + ": ", Color.YELLOW));
|
||||
}
|
||||
|
||||
messageBuilder.append(CLEAR_HISTORY);
|
||||
clearEntry.setOption(messageBuilder.toString());
|
||||
|
||||
final MenuEntry[] menuEntries = client.getMenuEntries();
|
||||
client.setMenuEntries(ArrayUtils.insert(menuEntries.length - 1, menuEntries, clearEntry));
|
||||
}
|
||||
|
||||
private void clearMessageQueue(ChatboxTab tab)
|
||||
{
|
||||
if (tab == ChatboxTab.ALL || tab == ChatboxTab.PRIVATE)
|
||||
{
|
||||
friends.clear();
|
||||
}
|
||||
|
||||
messageQueue.removeIf(e -> ArrayUtils.contains(tab.getMessageTypes(), e.getType()));
|
||||
}
|
||||
|
||||
private void clearChatboxHistory(ChatboxTab tab)
|
||||
{
|
||||
if (tab == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
boolean removed = false;
|
||||
for (ChatMessageType msgType : tab.getMessageTypes())
|
||||
{
|
||||
final ChatLineBuffer lineBuffer = client.getChatLineMap().get(msgType.getType());
|
||||
if (lineBuffer == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final MessageNode[] lines = lineBuffer.getLines().clone();
|
||||
for (final MessageNode line : lines)
|
||||
{
|
||||
if (line != null)
|
||||
{
|
||||
lineBuffer.removeMessageNode(line);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (removed)
|
||||
{
|
||||
clientThread.invoke(() -> client.runScript(ScriptID.BUILD_CHATBOX));
|
||||
}
|
||||
|
||||
clearMessageQueue(tab);
|
||||
}
|
||||
|
||||
/**
|
||||
* Small hack to prevent plugins checking for specific messages to match
|
||||
* @param message message
|
||||
* @return message with nbsp
|
||||
*/
|
||||
private static String nbsp(final String message)
|
||||
{
|
||||
if (message != null)
|
||||
{
|
||||
return message.replace(' ', '\u00A0');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e)
|
||||
{
|
||||
if (e.getKeyCode() != CYCLE_HOTKEY || !config.pmTargetCycling())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (client.getVar(VarClientInt.INPUT_TYPE) != InputType.PRIVATE_MESSAGE.getType())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
clientThread.invoke(() ->
|
||||
{
|
||||
final String target = findPreviousFriend();
|
||||
if (target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final String currentMessage = client.getVar(VarClientStr.INPUT_TEXT);
|
||||
|
||||
client.runScript(ScriptID.OPEN_PRIVATE_MESSAGE_INTERFACE, target);
|
||||
|
||||
client.setVar(VarClientStr.INPUT_TEXT, currentMessage);
|
||||
client.runScript(ScriptID.CHAT_TEXT_INPUT_REBUILD, "");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e)
|
||||
{
|
||||
}
|
||||
|
||||
private String findPreviousFriend()
|
||||
{
|
||||
final String currentTarget = client.getVar(VarClientStr.PRIVATE_MESSAGE_TARGET);
|
||||
if (currentTarget == null || friends.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Iterator<String> it = friends.descendingIterator(); it.hasNext(); )
|
||||
{
|
||||
String friend = it.next();
|
||||
if (friend.equals(currentTarget))
|
||||
{
|
||||
return it.hasNext() ? it.next() : friends.getLast();
|
||||
}
|
||||
}
|
||||
|
||||
return friends.getLast();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Anthony <https://github.com/while-loop>
|
||||
* 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.chathistory;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
|
||||
@Getter
|
||||
enum ChatboxTab
|
||||
{
|
||||
|
||||
ALL("All", "Switch tab", WidgetInfo.CHATBOX_TAB_ALL,
|
||||
ChatMessageType.values()),
|
||||
|
||||
// null 'after' var since we're not adding to menu
|
||||
PRIVATE("Private", null, WidgetInfo.CHATBOX_TAB_PRIVATE,
|
||||
ChatMessageType.PRIVATECHAT, ChatMessageType.PRIVATECHATOUT, ChatMessageType.MODPRIVATECHAT,
|
||||
ChatMessageType.LOGINLOGOUTNOTIFICATION),
|
||||
|
||||
// null 'after' var since we're not adding to menu
|
||||
PUBLIC("Public", null, WidgetInfo.CHATBOX_TAB_PUBLIC,
|
||||
ChatMessageType.PUBLICCHAT, ChatMessageType.AUTOTYPER, ChatMessageType.MODCHAT, ChatMessageType.MODAUTOTYPER),
|
||||
|
||||
GAME("Game", "Game: Filter", WidgetInfo.CHATBOX_TAB_GAME,
|
||||
ChatMessageType.GAMEMESSAGE, ChatMessageType.ENGINE, ChatMessageType.BROADCAST,
|
||||
ChatMessageType.SNAPSHOTFEEDBACK, ChatMessageType.ITEM_EXAMINE, ChatMessageType.NPC_EXAMINE,
|
||||
ChatMessageType.OBJECT_EXAMINE, ChatMessageType.FRIENDNOTIFICATION, ChatMessageType.IGNORENOTIFICATION,
|
||||
ChatMessageType.CONSOLE, ChatMessageType.SPAM, ChatMessageType.PLAYERRELATED, ChatMessageType.TENSECTIMEOUT,
|
||||
ChatMessageType.WELCOME, ChatMessageType.UNKNOWN),
|
||||
|
||||
CLAN("Clan", "Clan: Off", WidgetInfo.CHATBOX_TAB_CLAN,
|
||||
ChatMessageType.FRIENDSCHATNOTIFICATION, ChatMessageType.FRIENDSCHAT, ChatMessageType.CHALREQ_FRIENDSCHAT),
|
||||
|
||||
TRADE("Trade", "Trade: Off", WidgetInfo.CHATBOX_TAB_TRADE,
|
||||
ChatMessageType.TRADE_SENT, ChatMessageType.TRADEREQ, ChatMessageType.TRADE, ChatMessageType.CHALREQ_TRADE),
|
||||
;
|
||||
|
||||
private static final Map<Integer, ChatboxTab> TAB_MESSAGE_TYPES;
|
||||
|
||||
private final String name;
|
||||
@Nullable
|
||||
private final String after;
|
||||
private final int widgetId;
|
||||
private final ChatMessageType[] messageTypes;
|
||||
|
||||
ChatboxTab(String name, String after, WidgetInfo widgetId, ChatMessageType... messageTypes)
|
||||
{
|
||||
this.name = name;
|
||||
this.after = after;
|
||||
this.widgetId = widgetId.getId();
|
||||
this.messageTypes = messageTypes;
|
||||
}
|
||||
|
||||
static
|
||||
{
|
||||
ImmutableMap.Builder<Integer, ChatboxTab> builder = ImmutableMap.builder();
|
||||
for (ChatboxTab t : values())
|
||||
{
|
||||
builder.put(t.widgetId, t);
|
||||
}
|
||||
TAB_MESSAGE_TYPES = builder.build();
|
||||
}
|
||||
|
||||
static ChatboxTab of(int widgetId)
|
||||
{
|
||||
return TAB_MESSAGE_TYPES.get(widgetId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Hydrox6 <ikada@protonmail.ch>
|
||||
* 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.chatnotifications;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("chatnotification")
|
||||
public interface ChatNotificationsConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
position = 0,
|
||||
keyName = "highlightOwnName",
|
||||
name = "Highlight own name",
|
||||
description = "Highlights any instance of your username in chat"
|
||||
)
|
||||
default boolean highlightOwnName()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 1,
|
||||
keyName = "highlightWordsString",
|
||||
name = "Highlight words",
|
||||
description = "Highlights the following words in chat"
|
||||
)
|
||||
default String highlightWordsString()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 2,
|
||||
keyName = "notifyOnOwnName",
|
||||
name = "Notify on own name",
|
||||
description = "Notifies you whenever someone mentions you by name"
|
||||
)
|
||||
default boolean notifyOnOwnName()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 3,
|
||||
keyName = "notifyOnHighlight",
|
||||
name = "Notify on highlight",
|
||||
description = "Notifies you whenever a highlighted word is matched"
|
||||
)
|
||||
default boolean notifyOnHighlight()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 4,
|
||||
keyName = "notifyOnTrade",
|
||||
name = "Notify on trade",
|
||||
description = "Notifies you whenever you are traded"
|
||||
)
|
||||
default boolean notifyOnTrade()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 5,
|
||||
keyName = "notifyOnDuel",
|
||||
name = "Notify on duel",
|
||||
description = "Notifies you whenever you are challenged to a duel"
|
||||
)
|
||||
default boolean notifyOnDuel()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 6,
|
||||
keyName = "notifyOnBroadcast",
|
||||
name = "Notify on broadcast",
|
||||
description = "Notifies you whenever you receive a broadcast message"
|
||||
)
|
||||
default boolean notifyOnBroadcast()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Hydrox6 <ikada@protonmail.ch>
|
||||
* 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.chatnotifications;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.inject.Provides;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.MessageNode;
|
||||
import net.runelite.api.events.ChatMessage;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.client.Notifier;
|
||||
import net.runelite.client.RuneLiteProperties;
|
||||
import net.runelite.client.chat.ChatColorType;
|
||||
import net.runelite.client.chat.ChatMessageManager;
|
||||
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 net.runelite.client.util.Text;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Chat Notifications",
|
||||
description = "Highlight and notify you of chat messages",
|
||||
tags = {"duel", "messages", "notifications", "trade", "username"},
|
||||
enabledByDefault = false
|
||||
)
|
||||
public class ChatNotificationsPlugin extends Plugin
|
||||
{
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ChatNotificationsConfig config;
|
||||
|
||||
@Inject
|
||||
private ChatMessageManager chatMessageManager;
|
||||
|
||||
@Inject
|
||||
private Notifier notifier;
|
||||
|
||||
//Custom Highlights
|
||||
private Pattern usernameMatcher = null;
|
||||
private String usernameReplacer = "";
|
||||
private Pattern highlightMatcher = null;
|
||||
|
||||
@Provides
|
||||
ChatNotificationsConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(ChatNotificationsConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startUp()
|
||||
{
|
||||
updateHighlights();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
switch (event.getGameState())
|
||||
{
|
||||
case LOGIN_SCREEN:
|
||||
case HOPPING:
|
||||
usernameMatcher = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (event.getGroup().equals("chatnotification"))
|
||||
{
|
||||
updateHighlights();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateHighlights()
|
||||
{
|
||||
highlightMatcher = null;
|
||||
|
||||
if (!config.highlightWordsString().trim().equals(""))
|
||||
{
|
||||
List<String> items = Text.fromCSV(config.highlightWordsString());
|
||||
String joined = items.stream()
|
||||
.map(Text::escapeJagex) // we compare these strings to the raw Jagex ones
|
||||
.map(this::quoteAndIgnoreColor) // regex escape and ignore nested colors in the target message
|
||||
.collect(Collectors.joining("|"));
|
||||
// To match <word> \b doesn't work due to <> not being in \w,
|
||||
// so match \b or \s, as well as \A and \z for beginning and end of input respectively
|
||||
highlightMatcher = Pattern.compile("(?:\\b|(?<=\\s)|\\A)(?:" + joined + ")(?:\\b|(?=\\s)|\\z)", Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onChatMessage(ChatMessage chatMessage)
|
||||
{
|
||||
MessageNode messageNode = chatMessage.getMessageNode();
|
||||
boolean update = false;
|
||||
|
||||
switch (chatMessage.getType())
|
||||
{
|
||||
case TRADEREQ:
|
||||
if (chatMessage.getMessage().contains("wishes to trade with you.") && config.notifyOnTrade())
|
||||
{
|
||||
notifier.notify(chatMessage.getMessage());
|
||||
}
|
||||
break;
|
||||
case CHALREQ_TRADE:
|
||||
if (chatMessage.getMessage().contains("wishes to duel with you.") && config.notifyOnDuel())
|
||||
{
|
||||
notifier.notify(chatMessage.getMessage());
|
||||
}
|
||||
break;
|
||||
case BROADCAST:
|
||||
if (config.notifyOnBroadcast())
|
||||
{
|
||||
// Some broadcasts have links attached, notated by `|` followed by a number, while others contain color tags.
|
||||
// We don't want to see either in the printed notification.
|
||||
String broadcast = chatMessage.getMessage();
|
||||
|
||||
int urlTokenIndex = broadcast.lastIndexOf('|');
|
||||
if (urlTokenIndex != -1)
|
||||
{
|
||||
broadcast = broadcast.substring(0, urlTokenIndex);
|
||||
}
|
||||
|
||||
notifier.notify(Text.removeFormattingTags(broadcast));
|
||||
}
|
||||
break;
|
||||
case CONSOLE:
|
||||
// Don't notify for notification messages
|
||||
if (chatMessage.getName().equals(RuneLiteProperties.getTitle()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (usernameMatcher == null && client.getLocalPlayer() != null && client.getLocalPlayer().getName() != null)
|
||||
{
|
||||
String username = client.getLocalPlayer().getName();
|
||||
String pattern = Arrays.stream(username.split(" "))
|
||||
.map(s -> s.isEmpty() ? "" : Pattern.quote(s))
|
||||
.collect(Collectors.joining("[\u00a0\u0020]")); // space or nbsp
|
||||
usernameMatcher = Pattern.compile("\\b" + pattern + "\\b", Pattern.CASE_INSENSITIVE);
|
||||
usernameReplacer = "<col" + ChatColorType.HIGHLIGHT.name() + "><u>" + username + "</u><col" + ChatColorType.NORMAL.name() + ">";
|
||||
}
|
||||
|
||||
if (config.highlightOwnName() && usernameMatcher != null)
|
||||
{
|
||||
Matcher matcher = usernameMatcher.matcher(messageNode.getValue());
|
||||
if (matcher.find())
|
||||
{
|
||||
messageNode.setValue(matcher.replaceAll(usernameReplacer));
|
||||
update = true;
|
||||
if (config.notifyOnOwnName() && (chatMessage.getType() == ChatMessageType.PUBLICCHAT
|
||||
|| chatMessage.getType() == ChatMessageType.PRIVATECHAT
|
||||
|| chatMessage.getType() == ChatMessageType.FRIENDSCHAT
|
||||
|| chatMessage.getType() == ChatMessageType.MODCHAT
|
||||
|| chatMessage.getType() == ChatMessageType.MODPRIVATECHAT))
|
||||
{
|
||||
sendNotification(chatMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (highlightMatcher != null)
|
||||
{
|
||||
String nodeValue = messageNode.getValue();
|
||||
Matcher matcher = highlightMatcher.matcher(nodeValue);
|
||||
boolean found = false;
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
|
||||
while (matcher.find())
|
||||
{
|
||||
String value = matcher.group();
|
||||
|
||||
// Determine the ending color by:
|
||||
// 1) use the color from value if it has one
|
||||
// 2) use the last color from stringBuffer + <content between last match and current match>
|
||||
// To do #2 we just search for the last col tag after calling appendReplacement
|
||||
String endColor = getLastColor(value);
|
||||
|
||||
// Strip color tags from the highlighted region so that it remains highlighted correctly
|
||||
value = stripColor(value);
|
||||
|
||||
matcher.appendReplacement(stringBuffer, "<col" + ChatColorType.HIGHLIGHT + '>' + value);
|
||||
|
||||
if (endColor == null)
|
||||
{
|
||||
endColor = getLastColor(stringBuffer.toString());
|
||||
}
|
||||
|
||||
// Append end color
|
||||
stringBuffer.append(endColor == null ? "<col" + ChatColorType.NORMAL + ">" : endColor);
|
||||
|
||||
update = true;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
matcher.appendTail(stringBuffer);
|
||||
messageNode.setValue(stringBuffer.toString());
|
||||
|
||||
if (config.notifyOnHighlight())
|
||||
{
|
||||
sendNotification(chatMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (update)
|
||||
{
|
||||
messageNode.setRuneLiteFormatMessage(messageNode.getValue());
|
||||
chatMessageManager.update(messageNode);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNotification(ChatMessage message)
|
||||
{
|
||||
String name = Text.removeTags(message.getName());
|
||||
String sender = message.getSender();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
if (!Strings.isNullOrEmpty(sender))
|
||||
{
|
||||
stringBuilder.append('[').append(sender).append("] ");
|
||||
}
|
||||
|
||||
if (!Strings.isNullOrEmpty(name))
|
||||
{
|
||||
stringBuilder.append(name).append(": ");
|
||||
}
|
||||
|
||||
stringBuilder.append(Text.removeTags(message.getMessage()));
|
||||
String notification = stringBuilder.toString();
|
||||
notifier.notify(notification);
|
||||
}
|
||||
|
||||
private String quoteAndIgnoreColor(String str)
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < str.length(); ++i)
|
||||
{
|
||||
char c = str.charAt(i);
|
||||
stringBuilder.append(Pattern.quote(String.valueOf(c)));
|
||||
stringBuilder.append("(?:<col=[^>]*?>)?");
|
||||
}
|
||||
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last color tag from a string, or null if there was none
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
private static String getLastColor(String str)
|
||||
{
|
||||
int colIdx = str.lastIndexOf("<col=");
|
||||
int colEndIdx = str.lastIndexOf("</col>");
|
||||
|
||||
if (colEndIdx > colIdx)
|
||||
{
|
||||
// ends in a </col> which resets the color to normal
|
||||
return "<col" + ChatColorType.NORMAL + ">";
|
||||
}
|
||||
|
||||
if (colIdx == -1)
|
||||
{
|
||||
return null; // no color
|
||||
}
|
||||
|
||||
int closeIdx = str.indexOf('>', colIdx);
|
||||
if (closeIdx == -1)
|
||||
{
|
||||
return null; // unclosed col tag
|
||||
}
|
||||
|
||||
return str.substring(colIdx, closeIdx + 1); // include the >
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip color tags from a string.
|
||||
*
|
||||
* @param str
|
||||
* @return
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static String stripColor(String str)
|
||||
{
|
||||
return str.replaceAll("(<col=[0-9a-f]+>|</col>)", "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Brett Middle <https://github.com/bmiddle>
|
||||
* 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.combatlevel;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("combatlevel")
|
||||
public interface CombatLevelConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "showLevelsUntil",
|
||||
name = "Calculate next level",
|
||||
description = "Mouse over the combat level to calculate what skill levels will increase combat."
|
||||
)
|
||||
default boolean showLevelsUntil()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showPreciseCombatLevel",
|
||||
name = "Show precise combat level",
|
||||
description = "Displays your combat level with accurate decimals."
|
||||
)
|
||||
default boolean showPreciseCombatLevel()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "wildernessAttackLevelRange",
|
||||
name = "Show level range in wilderness",
|
||||
description = "Displays a PVP-world-like attack level range in the wilderness"
|
||||
)
|
||||
default boolean wildernessAttackLevelRange()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Brett Middle <https://github.com/bmiddle>
|
||||
* 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.combatlevel;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Experience;
|
||||
import net.runelite.api.Skill;
|
||||
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.tooltip.Tooltip;
|
||||
import net.runelite.client.ui.overlay.tooltip.TooltipManager;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
import javax.inject.Inject;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Rectangle;
|
||||
|
||||
class CombatLevelOverlay extends Overlay
|
||||
{
|
||||
private static final Color COMBAT_LEVEL_COLOUR = new Color(0xff981f);
|
||||
|
||||
private final Client client;
|
||||
private final CombatLevelConfig config;
|
||||
private final TooltipManager tooltipManager;
|
||||
|
||||
@Inject
|
||||
private CombatLevelOverlay(Client client, CombatLevelConfig config, TooltipManager tooltipManager)
|
||||
{
|
||||
this.client = client;
|
||||
this.config = config;
|
||||
this.tooltipManager = tooltipManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
Widget combatLevelWidget = client.getWidget(WidgetInfo.COMBAT_LEVEL);
|
||||
if (!config.showLevelsUntil()
|
||||
|| client.getLocalPlayer().getCombatLevel() == Experience.MAX_COMBAT_LEVEL
|
||||
|| combatLevelWidget == null || combatLevelWidget.isHidden())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Rectangle combatCanvas = combatLevelWidget.getBounds();
|
||||
|
||||
if (combatCanvas == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (combatCanvas.contains(client.getMouseCanvasPosition().getX(), client.getMouseCanvasPosition().getY()))
|
||||
{
|
||||
tooltipManager.add(new Tooltip(getLevelsUntilTooltip()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
String getLevelsUntilTooltip()
|
||||
{
|
||||
// grab combat skills from player
|
||||
int attackLevel = client.getRealSkillLevel(Skill.ATTACK);
|
||||
int strengthLevel = client.getRealSkillLevel(Skill.STRENGTH);
|
||||
int defenceLevel = client.getRealSkillLevel(Skill.DEFENCE);
|
||||
int hitpointsLevel = client.getRealSkillLevel(Skill.HITPOINTS);
|
||||
int magicLevel = client.getRealSkillLevel(Skill.MAGIC);
|
||||
int rangeLevel = client.getRealSkillLevel(Skill.RANGED);
|
||||
int prayerLevel = client.getRealSkillLevel(Skill.PRAYER);
|
||||
|
||||
// find the needed levels until level up
|
||||
int meleeNeed = Experience.getNextCombatLevelMelee(attackLevel, strengthLevel, defenceLevel, hitpointsLevel,
|
||||
magicLevel, rangeLevel, prayerLevel);
|
||||
int hpDefNeed = Experience.getNextCombatLevelHpDef(attackLevel, strengthLevel, defenceLevel, hitpointsLevel,
|
||||
magicLevel, rangeLevel, prayerLevel);
|
||||
int rangeNeed = Experience.getNextCombatLevelRange(attackLevel, strengthLevel, defenceLevel, hitpointsLevel,
|
||||
magicLevel, rangeLevel, prayerLevel);
|
||||
int magicNeed = Experience.getNextCombatLevelMagic(attackLevel, strengthLevel, defenceLevel, hitpointsLevel,
|
||||
magicLevel, rangeLevel, prayerLevel);
|
||||
int prayerNeed = Experience.getNextCombatLevelPrayer(attackLevel, strengthLevel, defenceLevel, hitpointsLevel,
|
||||
magicLevel, rangeLevel, prayerLevel);
|
||||
|
||||
// create tooltip string
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(ColorUtil.wrapWithColorTag("Next combat level:</br>", COMBAT_LEVEL_COLOUR));
|
||||
|
||||
if ((attackLevel + strengthLevel) < Experience.MAX_REAL_LEVEL * 2)
|
||||
{
|
||||
sb.append(meleeNeed).append(" Attack/Strength</br>");
|
||||
}
|
||||
if ((hitpointsLevel + defenceLevel) < Experience.MAX_REAL_LEVEL * 2)
|
||||
{
|
||||
sb.append(hpDefNeed).append(" Defence/Hitpoints</br>");
|
||||
}
|
||||
if (rangeLevel < Experience.MAX_REAL_LEVEL)
|
||||
{
|
||||
sb.append(rangeNeed).append(" Ranged</br>");
|
||||
}
|
||||
if (magicLevel < Experience.MAX_REAL_LEVEL)
|
||||
{
|
||||
sb.append(magicNeed).append(" Magic</br>");
|
||||
}
|
||||
if (prayerLevel < Experience.MAX_REAL_LEVEL)
|
||||
{
|
||||
sb.append(prayerNeed).append(" Prayer");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Devin French <https://github.com/devinfrench>
|
||||
* 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.combatlevel;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Experience;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.ScriptID;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.WorldType;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.ScriptPostFired;
|
||||
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 net.runelite.client.ui.overlay.OverlayManager;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Combat Level",
|
||||
description = "Show a more accurate combat level in Combat Options panel and other combat level functions",
|
||||
tags = {"wilderness", "attack", "range"}
|
||||
)
|
||||
public class CombatLevelPlugin extends Plugin
|
||||
{
|
||||
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.###");
|
||||
private static final String CONFIG_GROUP = "combatlevel";
|
||||
private static final String ATTACK_RANGE_CONFIG_KEY = "wildernessAttackLevelRange";
|
||||
private static final Pattern WILDERNESS_LEVEL_PATTERN = Pattern.compile("^Level: (\\d+)$");
|
||||
private static final int SKULL_CONTAINER_ADJUSTED_ORIGINAL_Y = 6;
|
||||
private static final int WILDERNESS_LEVEL_TEXT_ADJUSTED_ORIGINAL_Y = 3;
|
||||
private static final int MIN_COMBAT_LEVEL = 3;
|
||||
|
||||
private int originalWildernessLevelTextPosition = -1;
|
||||
private int originalSkullContainerPosition = -1;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
@Inject
|
||||
private CombatLevelConfig config;
|
||||
|
||||
@Inject
|
||||
private CombatLevelOverlay overlay;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Provides
|
||||
CombatLevelConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(CombatLevelConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(overlay);
|
||||
|
||||
if (config.wildernessAttackLevelRange())
|
||||
{
|
||||
appendAttackLevelRangeText();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
overlayManager.remove(overlay);
|
||||
Widget combatLevelWidget = client.getWidget(WidgetInfo.COMBAT_LEVEL);
|
||||
|
||||
if (combatLevelWidget != null)
|
||||
{
|
||||
String widgetText = combatLevelWidget.getText();
|
||||
|
||||
if (widgetText.contains("."))
|
||||
{
|
||||
combatLevelWidget.setText(widgetText.substring(0, widgetText.indexOf(".")));
|
||||
}
|
||||
}
|
||||
|
||||
shutDownAttackLevelRange();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick event)
|
||||
{
|
||||
if (client.getGameState() != GameState.LOGGED_IN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Widget combatLevelWidget = client.getWidget(WidgetInfo.COMBAT_LEVEL);
|
||||
if (combatLevelWidget == null || !config.showPreciseCombatLevel())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double combatLevelPrecise = Experience.getCombatLevelPrecise(
|
||||
client.getRealSkillLevel(Skill.ATTACK),
|
||||
client.getRealSkillLevel(Skill.STRENGTH),
|
||||
client.getRealSkillLevel(Skill.DEFENCE),
|
||||
client.getRealSkillLevel(Skill.HITPOINTS),
|
||||
client.getRealSkillLevel(Skill.MAGIC),
|
||||
client.getRealSkillLevel(Skill.RANGED),
|
||||
client.getRealSkillLevel(Skill.PRAYER)
|
||||
);
|
||||
|
||||
combatLevelWidget.setText("Combat Lvl: " + DECIMAL_FORMAT.format(combatLevelPrecise));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (!CONFIG_GROUP.equals(event.getGroup()) || !ATTACK_RANGE_CONFIG_KEY.equals(event.getKey()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.wildernessAttackLevelRange())
|
||||
{
|
||||
appendAttackLevelRangeText();
|
||||
}
|
||||
else
|
||||
{
|
||||
shutDownAttackLevelRange();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onScriptPostFired(ScriptPostFired scriptPostFired)
|
||||
{
|
||||
if (scriptPostFired.getScriptId() == ScriptID.PVP_WIDGET_BUILDER && config.wildernessAttackLevelRange())
|
||||
{
|
||||
appendAttackLevelRangeText();
|
||||
}
|
||||
}
|
||||
|
||||
private void appendAttackLevelRangeText()
|
||||
{
|
||||
final Widget wildernessLevelWidget = client.getWidget(WidgetInfo.PVP_WILDERNESS_LEVEL);
|
||||
if (wildernessLevelWidget == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final String wildernessLevelText = wildernessLevelWidget.getText();
|
||||
final Matcher m = WILDERNESS_LEVEL_PATTERN.matcher(wildernessLevelText);
|
||||
if (!m.matches()
|
||||
|| WorldType.isPvpWorld(client.getWorldType()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final Widget skullContainer = client.getWidget(WidgetInfo.PVP_SKULL_CONTAINER);
|
||||
if (originalWildernessLevelTextPosition == -1)
|
||||
{
|
||||
originalWildernessLevelTextPosition = wildernessLevelWidget.getOriginalY();
|
||||
}
|
||||
if (originalSkullContainerPosition == -1)
|
||||
{
|
||||
originalSkullContainerPosition = skullContainer.getRelativeY();
|
||||
}
|
||||
|
||||
final int wildernessLevel = Integer.parseInt(m.group(1));
|
||||
final int combatLevel = client.getLocalPlayer().getCombatLevel();
|
||||
|
||||
wildernessLevelWidget.setText(wildernessLevelText + "<br>" + combatAttackRange(combatLevel, wildernessLevel));
|
||||
wildernessLevelWidget.setOriginalY(WILDERNESS_LEVEL_TEXT_ADJUSTED_ORIGINAL_Y);
|
||||
skullContainer.setOriginalY(SKULL_CONTAINER_ADJUSTED_ORIGINAL_Y);
|
||||
|
||||
clientThread.invoke(wildernessLevelWidget::revalidate);
|
||||
clientThread.invoke(skullContainer::revalidate);
|
||||
}
|
||||
|
||||
private void shutDownAttackLevelRange()
|
||||
{
|
||||
if (WorldType.isPvpWorld(client.getWorldType()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final Widget wildernessLevelWidget = client.getWidget(WidgetInfo.PVP_WILDERNESS_LEVEL);
|
||||
if (wildernessLevelWidget != null)
|
||||
{
|
||||
String wildernessLevelText = wildernessLevelWidget.getText();
|
||||
if (wildernessLevelText.contains("<br>"))
|
||||
{
|
||||
wildernessLevelWidget.setText(wildernessLevelText.substring(0, wildernessLevelText.indexOf("<br>")));
|
||||
}
|
||||
wildernessLevelWidget.setOriginalY(originalWildernessLevelTextPosition);
|
||||
clientThread.invoke(wildernessLevelWidget::revalidate);
|
||||
}
|
||||
originalWildernessLevelTextPosition = -1;
|
||||
|
||||
final Widget skullContainer = client.getWidget(WidgetInfo.PVP_SKULL_CONTAINER);
|
||||
if (skullContainer != null)
|
||||
{
|
||||
skullContainer.setOriginalY(originalSkullContainerPosition);
|
||||
clientThread.invoke(skullContainer::revalidate);
|
||||
}
|
||||
originalSkullContainerPosition = -1;
|
||||
}
|
||||
|
||||
private static String combatAttackRange(final int combatLevel, final int wildernessLevel)
|
||||
{
|
||||
return Math.max(MIN_COMBAT_LEVEL, combatLevel - wildernessLevel) + "-" + Math.min(Experience.MAX_COMBAT_LEVEL, combatLevel + wildernessLevel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.mousehighlight;
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("mousehighlight")
|
||||
public interface MouseHighlightConfig extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
position = 0,
|
||||
keyName = "uiTooltip",
|
||||
name = "Interface Tooltips",
|
||||
description = "Whether or not tooltips are shown on interfaces"
|
||||
)
|
||||
default boolean uiTooltip()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 1,
|
||||
keyName = "chatboxTooltip",
|
||||
name = "Chatbox Tooltips",
|
||||
description = "Whether or not tooltips are shown over the chatbox"
|
||||
)
|
||||
default boolean chatboxTooltip()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 2,
|
||||
keyName = "disableSpellbooktooltip",
|
||||
name = "Disable Spellbook Tooltips",
|
||||
description = "Disable Spellbook Tooltips so they don't cover descriptions"
|
||||
)
|
||||
default boolean disableSpellbooktooltip()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Aria <aria@ar1as.space>
|
||||
* 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.mousehighlight;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.MenuAction;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.VarClientInt;
|
||||
import net.runelite.api.widgets.WidgetID;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.tooltip.Tooltip;
|
||||
import net.runelite.client.ui.overlay.tooltip.TooltipManager;
|
||||
|
||||
class MouseHighlightOverlay extends Overlay
|
||||
{
|
||||
/**
|
||||
* Menu types which are on widgets.
|
||||
*/
|
||||
private static final Set<MenuAction> WIDGET_MENU_ACTIONS = ImmutableSet.of(
|
||||
MenuAction.WIDGET_TYPE_1,
|
||||
MenuAction.WIDGET_TYPE_2,
|
||||
MenuAction.WIDGET_TYPE_3,
|
||||
MenuAction.WIDGET_TYPE_4,
|
||||
MenuAction.WIDGET_TYPE_5,
|
||||
MenuAction.WIDGET_TYPE_6,
|
||||
MenuAction.ITEM_USE_ON_WIDGET_ITEM,
|
||||
MenuAction.ITEM_USE_ON_WIDGET,
|
||||
MenuAction.ITEM_FIRST_OPTION,
|
||||
MenuAction.ITEM_SECOND_OPTION,
|
||||
MenuAction.ITEM_THIRD_OPTION,
|
||||
MenuAction.ITEM_FOURTH_OPTION,
|
||||
MenuAction.ITEM_FIFTH_OPTION,
|
||||
MenuAction.ITEM_USE,
|
||||
MenuAction.ITEM_DROP,
|
||||
MenuAction.WIDGET_FIRST_OPTION,
|
||||
MenuAction.WIDGET_SECOND_OPTION,
|
||||
MenuAction.WIDGET_THIRD_OPTION,
|
||||
MenuAction.WIDGET_FOURTH_OPTION,
|
||||
MenuAction.WIDGET_FIFTH_OPTION,
|
||||
MenuAction.EXAMINE_ITEM,
|
||||
MenuAction.SPELL_CAST_ON_WIDGET,
|
||||
MenuAction.CC_OP_LOW_PRIORITY,
|
||||
MenuAction.CC_OP
|
||||
);
|
||||
|
||||
private final TooltipManager tooltipManager;
|
||||
private final Client client;
|
||||
private final MouseHighlightConfig config;
|
||||
|
||||
@Inject
|
||||
MouseHighlightOverlay(Client client, TooltipManager tooltipManager, MouseHighlightConfig config)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
this.client = client;
|
||||
this.tooltipManager = tooltipManager;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (client.isMenuOpen())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
MenuEntry[] menuEntries = client.getMenuEntries();
|
||||
int last = menuEntries.length - 1;
|
||||
|
||||
if (last < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
MenuEntry menuEntry = menuEntries[last];
|
||||
String target = menuEntry.getTarget();
|
||||
String option = menuEntry.getOption();
|
||||
MenuAction type = MenuAction.of(menuEntry.getType());
|
||||
|
||||
if (type == MenuAction.RUNELITE_OVERLAY || type == MenuAction.CC_OP_LOW_PRIORITY)
|
||||
{
|
||||
// These are always right click only
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(option))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Trivial options that don't need to be highlighted, add more as they appear.
|
||||
switch (option)
|
||||
{
|
||||
case "Walk here":
|
||||
case "Cancel":
|
||||
case "Continue":
|
||||
return null;
|
||||
case "Move":
|
||||
// Hide overlay on sliding puzzle boxes
|
||||
if (target.contains("Sliding piece"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (WIDGET_MENU_ACTIONS.contains(type))
|
||||
{
|
||||
final int widgetId = menuEntry.getParam1();
|
||||
final int groupId = WidgetInfo.TO_GROUP(widgetId);
|
||||
|
||||
if (!config.uiTooltip())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!config.chatboxTooltip() && groupId == WidgetInfo.CHATBOX.getGroupId())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (config.disableSpellbooktooltip() && groupId == WidgetID.SPELLBOOK_GROUP_ID)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// If this varc is set, a tooltip will be displayed soon
|
||||
int tooltipTimeout = client.getVar(VarClientInt.TOOLTIP_TIMEOUT);
|
||||
if (tooltipTimeout > client.getGameCycle())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If this varc is set, a tooltip is already being displayed
|
||||
int tooltipDisplayed = client.getVar(VarClientInt.TOOLTIP_VISIBLE);
|
||||
if (tooltipDisplayed == 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
tooltipManager.addFront(new Tooltip(option + (Strings.isNullOrEmpty(target) ? "" : " " + target)));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Aria <aria@ar1as.space>
|
||||
* 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.mousehighlight;
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Mouse Tooltips",
|
||||
description = "Render default actions as a tooltip",
|
||||
tags = {"actions", "overlay"}
|
||||
)
|
||||
public class MouseHighlightPlugin extends Plugin
|
||||
{
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private MouseHighlightOverlay overlay;
|
||||
|
||||
@Provides
|
||||
MouseHighlightConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(MouseHighlightConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
overlayManager.add(overlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
overlayManager.remove(overlay);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user