playerscouter: adds a discord-webhook based Player Scouter (#1020)
* Add Player Scouter and all of its goodies. * Unsubscribe from eventbus on shutdown. * Add broken item value mapping, and implement to scouter.
This commit is contained in:
@@ -285,7 +285,7 @@ public class ItemManager
|
|||||||
/**
|
/**
|
||||||
* Look up an item's price
|
* Look up an item's price
|
||||||
*
|
*
|
||||||
* @param itemID item id
|
* @param itemID item id
|
||||||
* @param ignoreUntradeableMap should the price returned ignore the {@link UntradeableItemMapping}
|
* @param ignoreUntradeableMap should the price returned ignore the {@link UntradeableItemMapping}
|
||||||
* @return item price
|
* @return item price
|
||||||
*/
|
*/
|
||||||
@@ -350,6 +350,18 @@ public class ItemManager
|
|||||||
return (int) Math.max(1, getItemDefinition(itemID).getPrice() * HIGH_ALCHEMY_MULTIPLIER);
|
return (int) Math.max(1, getItemDefinition(itemID).getPrice() * HIGH_ALCHEMY_MULTIPLIER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getBrokenValue(int itemId)
|
||||||
|
{
|
||||||
|
PvPValueBrokenItem b = PvPValueBrokenItem.of(itemId);
|
||||||
|
|
||||||
|
if (b != null)
|
||||||
|
{
|
||||||
|
return (int) (b.getValue() * (75.0f / 100.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look up an item's stats
|
* Look up an item's stats
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* 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.game;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.runelite.api.ItemID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some non tradeable items are kept on death inside low level wilderness (1-20) but are turned into a broken variant.
|
||||||
|
* <p>
|
||||||
|
* The non-broken variant will be shown inside the interface.
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum PvPValueBrokenItem
|
||||||
|
{
|
||||||
|
// Capes
|
||||||
|
FIRE_CAPE(ItemID.FIRE_CAPE, 50000),
|
||||||
|
FIRE_MAX_CAPE(ItemID.FIRE_MAX_CAPE, 50000),
|
||||||
|
INFERNAL_CAPE(ItemID.INFERNAL_CAPE, 50000),
|
||||||
|
INFERNAL_MAX_CAPE(ItemID.INFERNAL_MAX_CAPE, 50000),
|
||||||
|
AVAS_ASSEMBLER(ItemID.AVAS_ASSEMBLER, 75000),
|
||||||
|
ASSEMBLER_MAX_CAPE(ItemID.ASSEMBLER_MAX_CAPE, 75000),
|
||||||
|
|
||||||
|
// Defenders
|
||||||
|
BRONZE_DEFENDER(ItemID.BRONZE_DEFENDER, 1000),
|
||||||
|
IRON_DEFENDER(ItemID.IRON_DEFENDER, 2000),
|
||||||
|
STEEL_DEFENDER(ItemID.STEEL_DEFENDER, 2500),
|
||||||
|
BLACK_DEFENDER(ItemID.BLACK_DEFENDER, 5000),
|
||||||
|
MITHRIL_DEFENDER(ItemID.MITHRIL_DEFENDER, 15000),
|
||||||
|
ADAMANT_DEFENDER(ItemID.ADAMANT_DEFENDER, 25000),
|
||||||
|
RUNE_DEFENDER(ItemID.RUNE_DEFENDER, 35000),
|
||||||
|
DRAGON_DEFENDER(ItemID.DRAGON_DEFENDER, 40000),
|
||||||
|
AVERNIC_DEFENDER(ItemID.AVERNIC_DEFENDER, 1000000),
|
||||||
|
|
||||||
|
// Void
|
||||||
|
VOID_MAGE_HELM(ItemID.VOID_MAGE_HELM, 40000),
|
||||||
|
VOID_RANGER_HELM(ItemID.VOID_RANGER_HELM, 40000),
|
||||||
|
VOID_MELEE_HELM(ItemID.VOID_MELEE_HELM, 40000),
|
||||||
|
VOID_KNIGHT_TOP(ItemID.VOID_KNIGHT_TOP, 45000),
|
||||||
|
VOID_KNIGHT_ROBE(ItemID.VOID_KNIGHT_ROBE, 45000),
|
||||||
|
VOID_KNIGHT_GLOVES(ItemID.VOID_KNIGHT_GLOVES, 30000),
|
||||||
|
ELITE_VOID_TOP(ItemID.ELITE_VOID_TOP, 50000),
|
||||||
|
ELITE_VOID_ROBE(ItemID.ELITE_VOID_ROBE, 50000),
|
||||||
|
|
||||||
|
// Barb Assault
|
||||||
|
FIGHTER_HAT(ItemID.FIGHTER_HAT, 45000),
|
||||||
|
RANGER_HAT(ItemID.RANGER_HAT, 45000),
|
||||||
|
HEALER_HAT(ItemID.HEALER_HAT, 45000),
|
||||||
|
FIGHTER_TORSO(ItemID.FIGHTER_TORSO, 50000),
|
||||||
|
PENANCE_SKIRT(ItemID.PENANCE_SKIRT, 20000),
|
||||||
|
|
||||||
|
// Castle Wars
|
||||||
|
SARADOMIN_HALO(ItemID.SARADOMIN_HALO, 25000),
|
||||||
|
ZAMORAK_HALO(ItemID.ZAMORAK_HALO, 25000),
|
||||||
|
GUTHIX_HALO(ItemID.GUTHIX_HALO, 25000),
|
||||||
|
DECORATIVE_MAGIC_HAT(ItemID.DECORATIVE_ARMOUR_11898, 5000),
|
||||||
|
DECORATIVE_MAGIC_ROBE_TOP(ItemID.DECORATIVE_ARMOUR_11896, 5000),
|
||||||
|
DECORATIVE_MAGIC_ROBE_LEGS(ItemID.DECORATIVE_ARMOUR_11897, 5000),
|
||||||
|
DECORATIVE_RANGE_TOP(ItemID.DECORATIVE_ARMOUR_11899, 5000),
|
||||||
|
DECORATIVE_RANGE_BOTTOM(ItemID.DECORATIVE_ARMOUR_11900, 5000),
|
||||||
|
DECORATIVE_RANGE_QUIVER(ItemID.DECORATIVE_ARMOUR_11901, 5000),
|
||||||
|
GOLD_DECORATIVE_HELM(ItemID.DECORATIVE_HELM_4511, 5000),
|
||||||
|
GOLD_DECORATIVE_BODY(ItemID.DECORATIVE_ARMOUR_4509, 5000),
|
||||||
|
GOLD_DECORATIVE_LEGS(ItemID.DECORATIVE_ARMOUR_4510, 5000),
|
||||||
|
GOLD_DECORATIVE_SKIRT(ItemID.DECORATIVE_ARMOUR_11895, 5000),
|
||||||
|
GOLD_DECORATIVE_SHIELD(ItemID.DECORATIVE_SHIELD_4512, 5000),
|
||||||
|
GOLD_DECORATIVE_SWORD(ItemID.DECORATIVE_SWORD_4508, 5000);
|
||||||
|
|
||||||
|
private static final ImmutableMap<Integer, PvPValueBrokenItem> idMap;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
ImmutableMap.Builder<Integer, PvPValueBrokenItem> builder = ImmutableMap.builder();
|
||||||
|
|
||||||
|
for (PvPValueBrokenItem items : values())
|
||||||
|
{
|
||||||
|
builder.put(items.itemID, items);
|
||||||
|
}
|
||||||
|
|
||||||
|
idMap = builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final int itemID;
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static PvPValueBrokenItem of(int itemId)
|
||||||
|
{
|
||||||
|
return idMap.get(itemId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean breaksOnDeath(int itemId)
|
||||||
|
{
|
||||||
|
return idMap.containsKey(itemId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, ganom <https://github.com/Ganom>
|
||||||
|
* 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.playerscouter;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.runelite.api.Prayer;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum AttackStyle
|
||||||
|
{
|
||||||
|
MAGE("Mage", Color.CYAN, Prayer.PROTECT_FROM_MAGIC),
|
||||||
|
RANGE("Range", Color.GREEN, Prayer.PROTECT_FROM_MISSILES),
|
||||||
|
MELEE("Melee", Color.RED, Prayer.PROTECT_FROM_MELEE),
|
||||||
|
UNKNOWN("Unknown", Color.WHITE, null);
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private Color color;
|
||||||
|
private Prayer prayer;
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, ganom <https://github.com/Ganom>
|
||||||
|
* 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.playerscouter;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import net.runelite.client.graphics.ModelOutlineRenderer;
|
||||||
|
import net.runelite.client.ui.overlay.Overlay;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class AttackerOverlay extends Overlay
|
||||||
|
{
|
||||||
|
private static final Color TRANSPARENT = new Color(0, 0, 0, 0);
|
||||||
|
|
||||||
|
private final PlayerScouter plugin;
|
||||||
|
private final ModelOutlineRenderer outlineRenderer;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public AttackerOverlay(final PlayerScouter plugin, final ModelOutlineRenderer outlineRenderer)
|
||||||
|
{
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.outlineRenderer = outlineRenderer;
|
||||||
|
setPosition(OverlayPosition.DYNAMIC);
|
||||||
|
setLayer(OverlayLayer.ABOVE_SCENE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension render(Graphics2D graphics)
|
||||||
|
{
|
||||||
|
if (!plugin.isOverlayEnabled() || plugin.getPlayerContainer().isEmpty())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.getPlayerContainer().forEach(player ->
|
||||||
|
{
|
||||||
|
if (!player.isTarget())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final AttackStyle attackStyle = player.getAttackStyle();
|
||||||
|
|
||||||
|
if (attackStyle.getPrayer() == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
outlineRenderer.drawOutline(player.getPlayer(), 2, attackStyle.getColor(), TRANSPARENT);
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, ganom <https://github.com/Ganom>
|
||||||
|
* 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.playerscouter;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
import net.runelite.api.Player;
|
||||||
|
import net.runelite.api.Prayer;
|
||||||
|
import net.runelite.http.api.hiscore.Skill;
|
||||||
|
|
||||||
|
/*
|
||||||
|
You may be asking, why in the fuck is there so much information
|
||||||
|
being gathered? The answer is, why not. Always plan for the future.
|
||||||
|
Better to have too much than to have too little.
|
||||||
|
*/
|
||||||
|
@Getter(AccessLevel.PACKAGE)
|
||||||
|
@Setter(AccessLevel.PACKAGE)
|
||||||
|
@ToString(exclude = "player")
|
||||||
|
class PlayerContainer
|
||||||
|
{
|
||||||
|
private AttackStyle attackStyle;
|
||||||
|
private AttackStyle weakness;
|
||||||
|
private boolean attacking;
|
||||||
|
private boolean logging;
|
||||||
|
private boolean scouted;
|
||||||
|
private boolean target;
|
||||||
|
private double drainRate;
|
||||||
|
private double estimatedPrayer;
|
||||||
|
private int magicAttack;
|
||||||
|
private int magicDefence;
|
||||||
|
private int meleeAttack;
|
||||||
|
private int meleeDefence;
|
||||||
|
private int prayerBonus;
|
||||||
|
private int rangeAttack;
|
||||||
|
private int rangeDefence;
|
||||||
|
private int risk;
|
||||||
|
private int scoutTimer;
|
||||||
|
private int timer;
|
||||||
|
private int weapon;
|
||||||
|
private int wildyLevel;
|
||||||
|
private LinkedHashMap<Integer, Integer> gear;
|
||||||
|
private LinkedHashMap<Integer, Integer> riskedGear;
|
||||||
|
private Player player;
|
||||||
|
private Prayer overhead;
|
||||||
|
private Prayer predictedPrayer;
|
||||||
|
private Skill prayer;
|
||||||
|
private String estimatedPrayerString;
|
||||||
|
private String location;
|
||||||
|
private String name;
|
||||||
|
private String targetString;
|
||||||
|
|
||||||
|
PlayerContainer(Player player, Skill prayer)
|
||||||
|
{
|
||||||
|
this.attacking = false;
|
||||||
|
this.attackStyle = AttackStyle.UNKNOWN;
|
||||||
|
this.drainRate = 0;
|
||||||
|
this.estimatedPrayer = 0;
|
||||||
|
this.estimatedPrayerString = "";
|
||||||
|
this.gear = new LinkedHashMap<>();
|
||||||
|
this.location = "N/A";
|
||||||
|
this.logging = false;
|
||||||
|
this.magicAttack = 0;
|
||||||
|
this.magicDefence = 0;
|
||||||
|
this.meleeAttack = 0;
|
||||||
|
this.meleeDefence = 0;
|
||||||
|
this.name = player.getName();
|
||||||
|
this.overhead = null;
|
||||||
|
this.player = player;
|
||||||
|
this.prayer = prayer;
|
||||||
|
this.prayerBonus = 0;
|
||||||
|
this.predictedPrayer = null;
|
||||||
|
this.rangeAttack = 0;
|
||||||
|
this.rangeDefence = 0;
|
||||||
|
this.risk = 0;
|
||||||
|
this.riskedGear = new LinkedHashMap<>();
|
||||||
|
this.scouted = false;
|
||||||
|
this.scoutTimer = 500;
|
||||||
|
this.target = false;
|
||||||
|
this.targetString = "";
|
||||||
|
this.timer = 0;
|
||||||
|
this.weakness = AttackStyle.UNKNOWN;
|
||||||
|
this.weapon = 0;
|
||||||
|
this.wildyLevel = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,334 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, ganom <https://github.com/Ganom>
|
||||||
|
* 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.playerscouter;
|
||||||
|
|
||||||
|
import com.google.inject.Provides;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.runelite.api.Actor;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.GameState;
|
||||||
|
import net.runelite.api.Player;
|
||||||
|
import net.runelite.api.Varbits;
|
||||||
|
import net.runelite.api.WorldType;
|
||||||
|
import net.runelite.api.coords.WorldArea;
|
||||||
|
import net.runelite.api.events.AnimationChanged;
|
||||||
|
import net.runelite.api.events.ConfigChanged;
|
||||||
|
import net.runelite.api.events.GameStateChanged;
|
||||||
|
import net.runelite.api.events.GameTick;
|
||||||
|
import net.runelite.api.events.InteractingChanged;
|
||||||
|
import net.runelite.api.events.PlayerDespawned;
|
||||||
|
import net.runelite.api.events.PlayerSpawned;
|
||||||
|
import net.runelite.client.callback.ClientThread;
|
||||||
|
import net.runelite.client.config.ConfigManager;
|
||||||
|
import net.runelite.client.eventbus.EventBus;
|
||||||
|
import net.runelite.client.game.ItemManager;
|
||||||
|
import net.runelite.client.plugins.Plugin;
|
||||||
|
import net.runelite.client.plugins.PluginDescriptor;
|
||||||
|
import net.runelite.client.plugins.PluginType;
|
||||||
|
import net.runelite.client.ui.overlay.OverlayManager;
|
||||||
|
import net.runelite.client.util.PvPUtil;
|
||||||
|
import net.runelite.client.util.WildernessLocation;
|
||||||
|
import net.runelite.http.api.discord.DiscordClient;
|
||||||
|
import net.runelite.http.api.hiscore.HiscoreClient;
|
||||||
|
import net.runelite.http.api.hiscore.HiscoreSkill;
|
||||||
|
import net.runelite.http.api.hiscore.SingleHiscoreSkillResult;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
|
||||||
|
@PluginDescriptor(
|
||||||
|
name = "Player Scouter",
|
||||||
|
description = "Scout players and output them to your discord channel!",
|
||||||
|
type = PluginType.PVP
|
||||||
|
)
|
||||||
|
@Slf4j
|
||||||
|
public class PlayerScouter extends Plugin
|
||||||
|
{
|
||||||
|
private static final DiscordClient DISCORD_CLIENT = new DiscordClient();
|
||||||
|
private static final String ICONBASEURL = "https://www.osrsbox.com/osrsbox-db/items-icons/"; // Add item id + ".png"
|
||||||
|
private static final HiscoreClient HISCORE_CLIENT = new HiscoreClient();
|
||||||
|
private static final Map<WorldArea, String> WILD_LOCS = getLocationMap();
|
||||||
|
@Inject
|
||||||
|
private Client client;
|
||||||
|
@Inject
|
||||||
|
private ItemManager itemManager;
|
||||||
|
@Inject
|
||||||
|
private AttackerOverlay attackerOverlay;
|
||||||
|
@Inject
|
||||||
|
private OverlayManager overlayManager;
|
||||||
|
@Inject
|
||||||
|
private ClientThread clientThread;
|
||||||
|
@Inject
|
||||||
|
private PlayerScouterConfig config;
|
||||||
|
@Inject
|
||||||
|
private EventBus eventBus;
|
||||||
|
@Getter(AccessLevel.PACKAGE)
|
||||||
|
private Set<PlayerContainer> playerContainer = new HashSet<>();
|
||||||
|
@Getter(AccessLevel.PACKAGE)
|
||||||
|
private boolean overlayEnabled;
|
||||||
|
private boolean onlyWildy;
|
||||||
|
private ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
|
||||||
|
private Map<String, Integer> blacklist = new HashMap<>();
|
||||||
|
private int reset;
|
||||||
|
private HttpUrl url;
|
||||||
|
private int minimumRisk;
|
||||||
|
private int minimumValue;
|
||||||
|
private int timeout;
|
||||||
|
|
||||||
|
private static Map<WorldArea, String> getLocationMap()
|
||||||
|
{
|
||||||
|
Map<WorldArea, String> hashMap = new HashMap<>();
|
||||||
|
Arrays.stream(WildernessLocation.values()).forEach(wildernessLocation ->
|
||||||
|
hashMap.put(wildernessLocation.getWorldArea(), wildernessLocation.getName()));
|
||||||
|
return hashMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
PlayerScouterConfig provideConfig(ConfigManager configManager)
|
||||||
|
{
|
||||||
|
return configManager.getConfig(PlayerScouterConfig.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void startUp()
|
||||||
|
{
|
||||||
|
overlayManager.add(attackerOverlay);
|
||||||
|
blacklist.clear();
|
||||||
|
updateConfig();
|
||||||
|
addSubscriptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void shutDown()
|
||||||
|
{
|
||||||
|
overlayManager.remove(attackerOverlay);
|
||||||
|
playerContainer.clear();
|
||||||
|
blacklist.clear();
|
||||||
|
eventBus.unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addSubscriptions()
|
||||||
|
{
|
||||||
|
eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
|
||||||
|
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
|
||||||
|
eventBus.subscribe(InteractingChanged.class, this, this::onInteractingChanged);
|
||||||
|
eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged);
|
||||||
|
eventBus.subscribe(PlayerSpawned.class, this, this::onPlayerSpawned);
|
||||||
|
eventBus.subscribe(PlayerDespawned.class, this, this::onPlayerDespawned);
|
||||||
|
eventBus.subscribe(GameTick.class, this, this::onGameTick);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onGameStateChanged(GameStateChanged event)
|
||||||
|
{
|
||||||
|
if (event.getGameState() == GameState.LOGGED_IN)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
blacklist.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onConfigChanged(ConfigChanged event)
|
||||||
|
{
|
||||||
|
if (!event.getGroup().equals("playerscouter"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onInteractingChanged(InteractingChanged event)
|
||||||
|
{
|
||||||
|
if ((event.getSource() instanceof Player) && (event.getTarget() instanceof Player))
|
||||||
|
{
|
||||||
|
final Player source = (Player) event.getSource();
|
||||||
|
final Player target = (Player) event.getTarget();
|
||||||
|
|
||||||
|
if (source == client.getLocalPlayer())
|
||||||
|
{
|
||||||
|
if (!PvPUtil.isAttackable(client, target))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
playerContainer.forEach(player ->
|
||||||
|
{
|
||||||
|
if (player.getPlayer() == target)
|
||||||
|
{
|
||||||
|
player.setTimer(16);
|
||||||
|
player.setTarget(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (target == client.getLocalPlayer())
|
||||||
|
{
|
||||||
|
if (!PvPUtil.isAttackable(client, source))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
playerContainer.forEach(player ->
|
||||||
|
{
|
||||||
|
if (player.getPlayer() == source)
|
||||||
|
{
|
||||||
|
player.setTimer(16);
|
||||||
|
player.setTarget(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onAnimationChanged(AnimationChanged event)
|
||||||
|
{
|
||||||
|
final Actor actor = event.getActor();
|
||||||
|
|
||||||
|
if (actor.getInteracting() != client.getLocalPlayer())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(actor instanceof Player))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PvPUtil.isAttackable(client, (Player) actor) && actor.getAnimation() != -1)
|
||||||
|
{
|
||||||
|
playerContainer.forEach(player ->
|
||||||
|
{
|
||||||
|
if (player.getPlayer() == actor)
|
||||||
|
{
|
||||||
|
player.setTimer(16);
|
||||||
|
player.setTarget(true);
|
||||||
|
player.setAttacking(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPlayerSpawned(PlayerSpawned event)
|
||||||
|
{
|
||||||
|
final Player player = event.getPlayer();
|
||||||
|
|
||||||
|
if (player == client.getLocalPlayer())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blacklist.isEmpty() && blacklist.keySet().contains(player.getName()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
executorService.submit(() ->
|
||||||
|
{
|
||||||
|
SingleHiscoreSkillResult result;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
result = HISCORE_CLIENT.lookup(player.getName(), HiscoreSkill.PRAYER);
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
log.warn("Error fetching Hiscore data " + ex.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
playerContainer.add(new PlayerContainer(player, result.getSkill()));
|
||||||
|
blacklist.put(player.getName(), client.getTickCount() + this.timeout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onPlayerDespawned(PlayerDespawned event)
|
||||||
|
{
|
||||||
|
final Player player = event.getPlayer();
|
||||||
|
|
||||||
|
playerContainer.removeIf(p -> p.getPlayer() == player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onGameTick(GameTick event)
|
||||||
|
{
|
||||||
|
resetBlacklist();
|
||||||
|
|
||||||
|
if (!checkWildy() || playerContainer.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
playerContainer.forEach(player ->
|
||||||
|
{
|
||||||
|
Utils.reset(player);
|
||||||
|
Utils.update(player, itemManager, 6, WILD_LOCS);
|
||||||
|
|
||||||
|
if (player.getRisk() > this.minimumRisk)
|
||||||
|
{
|
||||||
|
Utils.scoutPlayer(player, url, DISCORD_CLIENT, itemManager, client, this.minimumValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetBlacklist()
|
||||||
|
{
|
||||||
|
if (blacklist.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
blacklist.forEach((k, v) ->
|
||||||
|
{
|
||||||
|
if (v == client.getTickCount())
|
||||||
|
{
|
||||||
|
blacklist.remove(k, v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateConfig()
|
||||||
|
{
|
||||||
|
this.url = HttpUrl.parse(config.webhook());
|
||||||
|
this.minimumRisk = config.minimumRisk();
|
||||||
|
this.minimumValue = config.minimumValue();
|
||||||
|
this.overlayEnabled = config.overlayEnabled();
|
||||||
|
this.timeout = config.timeout();
|
||||||
|
this.onlyWildy = config.onlyWildy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkWildy()
|
||||||
|
{
|
||||||
|
if (!this.onlyWildy)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return client.getVar(Varbits.IN_WILDERNESS) == 1 || WorldType.isPvpWorld(client.getWorldType());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, ganom <https://github.com/Ganom>
|
||||||
|
* 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.playerscouter;
|
||||||
|
|
||||||
|
import net.runelite.client.config.Config;
|
||||||
|
import net.runelite.client.config.ConfigGroup;
|
||||||
|
import net.runelite.client.config.ConfigItem;
|
||||||
|
|
||||||
|
@ConfigGroup("playerscouter")
|
||||||
|
public interface PlayerScouterConfig extends Config
|
||||||
|
{
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "webhook",
|
||||||
|
name = "Webhook Url",
|
||||||
|
description = "Input the url for your webhook.",
|
||||||
|
position = 0
|
||||||
|
)
|
||||||
|
default String webhook()
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "overlayEnabled",
|
||||||
|
name = "Attacker Overlay",
|
||||||
|
description = "This will highlight your attacker.",
|
||||||
|
position = 1
|
||||||
|
)
|
||||||
|
default boolean overlayEnabled()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "onlyWildy",
|
||||||
|
name = "Only Scout in Wildy",
|
||||||
|
description = "This will only scout players in the wilderness.",
|
||||||
|
position = 1
|
||||||
|
)
|
||||||
|
default boolean onlyWildy()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "minimumRisk",
|
||||||
|
name = "Minimum Risk",
|
||||||
|
description = "Minimum risk for the player to be scouted.",
|
||||||
|
position = 2
|
||||||
|
)
|
||||||
|
default int minimumRisk()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "minimumValue",
|
||||||
|
name = "Minimum Value",
|
||||||
|
description = "Minimum value for the item to be posted on discord.",
|
||||||
|
position = 3
|
||||||
|
)
|
||||||
|
default int minimumValue()
|
||||||
|
{
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "timeout",
|
||||||
|
name = "Timeout",
|
||||||
|
description = "Minimum amount of ticks before the player can be scouted again. (1 tick = 600ms)",
|
||||||
|
position = 4
|
||||||
|
)
|
||||||
|
default int timeout()
|
||||||
|
{
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,698 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, ganom <https://github.com/Ganom>
|
||||||
|
* 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.playerscouter;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.runelite.api.Actor;
|
||||||
|
import net.runelite.api.Client;
|
||||||
|
import net.runelite.api.ItemDefinition;
|
||||||
|
import net.runelite.api.NPC;
|
||||||
|
import net.runelite.api.Player;
|
||||||
|
import net.runelite.api.Prayer;
|
||||||
|
import net.runelite.api.coords.WorldArea;
|
||||||
|
import net.runelite.api.coords.WorldPoint;
|
||||||
|
import net.runelite.api.kit.KitType;
|
||||||
|
import net.runelite.client.game.ItemManager;
|
||||||
|
import net.runelite.client.game.PvPValueBrokenItem;
|
||||||
|
import net.runelite.client.util.StackFormatter;
|
||||||
|
import net.runelite.http.api.discord.DiscordClient;
|
||||||
|
import net.runelite.http.api.discord.DiscordEmbed;
|
||||||
|
import net.runelite.http.api.discord.DiscordMessage;
|
||||||
|
import net.runelite.http.api.discord.embed.AuthorEmbed;
|
||||||
|
import net.runelite.http.api.discord.embed.FieldEmbed;
|
||||||
|
import net.runelite.http.api.discord.embed.FooterEmbed;
|
||||||
|
import net.runelite.http.api.discord.embed.ThumbnailEmbed;
|
||||||
|
import net.runelite.http.api.item.ItemEquipmentStats;
|
||||||
|
import net.runelite.http.api.item.ItemStats;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
This class is pretty useless, its not called anywhere else,
|
||||||
|
but I mainly have it so its pretty obvious whats happening
|
||||||
|
in the main class. Pretty much stuffing the ugly in here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
class Utils
|
||||||
|
{
|
||||||
|
private static final double INCREASE = 0.60;
|
||||||
|
private static final String ICONBASEURL = "https://www.osrsbox.com/osrsbox-db/items-icons/"; // Add item id + ".png"
|
||||||
|
private static final SimpleDateFormat SDF = new SimpleDateFormat("MMM dd h:mm a z");
|
||||||
|
|
||||||
|
static void reset(PlayerContainer player)
|
||||||
|
{
|
||||||
|
player.setMeleeAttack(0);
|
||||||
|
player.setMagicAttack(0);
|
||||||
|
player.setRangeAttack(0);
|
||||||
|
player.setMeleeDefence(0);
|
||||||
|
player.setMagicDefence(0);
|
||||||
|
player.setRangeDefence(0);
|
||||||
|
player.setRisk(0);
|
||||||
|
player.setPrayerBonus(0);
|
||||||
|
player.setDrainRate(0);
|
||||||
|
player.setOverhead(Utils.iconToPrayer(player.getPlayer()));
|
||||||
|
player.setAttackStyle(AttackStyle.UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update(PlayerContainer player, ItemManager itemManager, int restores, Map<WorldArea, String> map)
|
||||||
|
{
|
||||||
|
updatePlayerGear(player, itemManager);
|
||||||
|
updateAttackStyle(player);
|
||||||
|
updateWeakness(player);
|
||||||
|
player.setPredictedPrayer(predictOffensivePrayer(player.getPrayer().getLevel(), player.getAttackStyle()));
|
||||||
|
updatePrayerDrainRate(player);
|
||||||
|
updateEstimatedPrayer(player, restores);
|
||||||
|
player.setLocation(location(player, map));
|
||||||
|
player.setWildyLevel(getWildernessLevelFrom(player.getPlayer().getWorldLocation()));
|
||||||
|
player.setTargetString(targetStringBuilder(player));
|
||||||
|
if (player.isScouted())
|
||||||
|
{
|
||||||
|
player.setScoutTimer(player.getScoutTimer() - 1);
|
||||||
|
|
||||||
|
if (player.getScoutTimer() <= 0)
|
||||||
|
{
|
||||||
|
player.setScouted(false);
|
||||||
|
player.setScoutTimer(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.debug(player.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
//pvputil is private, so atm i can't grab from it.
|
||||||
|
private static int getWildernessLevelFrom(WorldPoint point)
|
||||||
|
{
|
||||||
|
int y = point.getY();
|
||||||
|
|
||||||
|
int underLevel = ((y - 9920) / 8) + 1;
|
||||||
|
int upperLevel = ((y - 3520) / 8) + 1;
|
||||||
|
|
||||||
|
return y > 6400 ? underLevel : upperLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updatePlayerGear(PlayerContainer player, ItemManager itemManager)
|
||||||
|
{
|
||||||
|
Map<Integer, Integer> prices = new HashMap<>();
|
||||||
|
|
||||||
|
if (player.getPlayer().getPlayerAppearance() != null)
|
||||||
|
{
|
||||||
|
for (KitType kitType : KitType.values())
|
||||||
|
{
|
||||||
|
if (kitType.equals(KitType.RING) || kitType.equals(KitType.AMMUNITION))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int id = player.getPlayer().getPlayerAppearance().getEquipmentId(kitType);
|
||||||
|
|
||||||
|
if (id == -1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kitType.equals(KitType.WEAPON))
|
||||||
|
{
|
||||||
|
player.setWeapon(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ItemStats item = itemManager.getItemStats(id, false);
|
||||||
|
final ItemDefinition itemDefinition = itemManager.getItemDefinition(id);
|
||||||
|
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
log.debug("Item is null: {}", id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemDefinition == null)
|
||||||
|
{
|
||||||
|
log.debug("Item Def is null: {}", id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PvPValueBrokenItem.breaksOnDeath(id))
|
||||||
|
{
|
||||||
|
prices.put(id, itemManager.getBrokenValue(id));
|
||||||
|
log.debug("Item has a broken value: Id {}, Value {}", id, itemManager.getBrokenValue(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!itemDefinition.isTradeable() && !PvPValueBrokenItem.breaksOnDeath(id))
|
||||||
|
{
|
||||||
|
prices.put(id, itemDefinition.getPrice());
|
||||||
|
}
|
||||||
|
else if (itemDefinition.isTradeable())
|
||||||
|
{
|
||||||
|
prices.put(id, itemManager.getItemPrice(id, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemEquipmentStats stats = item.getEquipment();
|
||||||
|
|
||||||
|
if (stats == null)
|
||||||
|
{
|
||||||
|
log.debug("Stats are null: {}", item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.setMeleeAttack(player.getMeleeAttack() + ((stats.getAcrush() + stats.getAslash() + stats.getAstab()) / 3));
|
||||||
|
player.setMagicAttack(player.getMagicAttack() + stats.getAmagic());
|
||||||
|
player.setRangeAttack(player.getRangeAttack() + stats.getArange());
|
||||||
|
player.setMeleeDefence(player.getMeleeDefence() + ((stats.getDcrush() + stats.getDslash() + stats.getDstab()) / 3));
|
||||||
|
player.setMagicDefence(player.getMagicDefence() + stats.getDmagic());
|
||||||
|
player.setRangeDefence(player.getRangeDefence() + stats.getDrange());
|
||||||
|
player.setPrayerBonus(player.getPrayerBonus() + stats.getPrayer());
|
||||||
|
}
|
||||||
|
updateGear(player, prices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updateTarget(PlayerContainer player)
|
||||||
|
{
|
||||||
|
if (player.isTarget())
|
||||||
|
{
|
||||||
|
if (player.getTimer() > 0)
|
||||||
|
{
|
||||||
|
player.setTimer(player.getTimer() - 1);
|
||||||
|
}
|
||||||
|
else if (player.getTimer() == 0)
|
||||||
|
{
|
||||||
|
player.setTarget(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updateDefence(PlayerContainer player)
|
||||||
|
{
|
||||||
|
if (player.getOverhead() != null)
|
||||||
|
{
|
||||||
|
//yeah i know this is a shit way to do it :shrug:
|
||||||
|
switch (player.getOverhead())
|
||||||
|
{
|
||||||
|
case PROTECT_FROM_MELEE:
|
||||||
|
player.setMeleeDefence((int) (player.getMeleeDefence() / INCREASE));
|
||||||
|
log.debug("Melee Overhead, Defence Increased");
|
||||||
|
break;
|
||||||
|
case PROTECT_FROM_MAGIC:
|
||||||
|
player.setMagicDefence((int) (player.getMagicDefence() / INCREASE));
|
||||||
|
log.debug("Magic Overhead, Defence Increased");
|
||||||
|
break;
|
||||||
|
case PROTECT_FROM_MISSILES:
|
||||||
|
player.setRangeDefence((int) (player.getRangeDefence() / INCREASE));
|
||||||
|
log.debug("Range Overhead, Defence Increased");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateGear(PlayerContainer player, Map<Integer, Integer> prices)
|
||||||
|
{
|
||||||
|
player.setGear(prices.entrySet()
|
||||||
|
.stream()
|
||||||
|
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new)));
|
||||||
|
|
||||||
|
player.setRiskedGear(prices.entrySet()
|
||||||
|
.stream()
|
||||||
|
.sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new)));
|
||||||
|
|
||||||
|
if (player.getPlayer().getSkullIcon() == null)
|
||||||
|
{
|
||||||
|
removeEntries(player.getRiskedGear(), player.getPrayer().getLevel() <= 25 ? 3 : 4);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
removeEntries(player.getRiskedGear(), player.getPrayer().getLevel() <= 25 ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
player.getRiskedGear().values().forEach(price -> player.setRisk(player.getRisk() + price));
|
||||||
|
prices.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateAttackStyle(PlayerContainer player)
|
||||||
|
{
|
||||||
|
if (player.getMagicAttack() >= player.getRangeAttack() && player.getMagicAttack() >= player.getMeleeAttack())
|
||||||
|
{
|
||||||
|
player.setAttackStyle(AttackStyle.MAGE);
|
||||||
|
}
|
||||||
|
else if (player.getRangeAttack() >= player.getMagicAttack() && player.getRangeAttack() >= player.getMeleeAttack())
|
||||||
|
{
|
||||||
|
player.setAttackStyle(AttackStyle.RANGE);
|
||||||
|
}
|
||||||
|
else if (player.getMeleeAttack() >= player.getMagicAttack() && player.getMeleeAttack() >= player.getRangeAttack())
|
||||||
|
{
|
||||||
|
player.setAttackStyle(AttackStyle.MELEE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateWeakness(PlayerContainer player)
|
||||||
|
{
|
||||||
|
if (player.getMagicDefence() <= player.getRangeDefence() && player.getMagicDefence() <= player.getMeleeDefence())
|
||||||
|
{
|
||||||
|
player.setWeakness(AttackStyle.MAGE);
|
||||||
|
}
|
||||||
|
else if (player.getRangeDefence() <= player.getMagicDefence() && player.getRangeDefence() <= player.getMeleeDefence())
|
||||||
|
{
|
||||||
|
player.setWeakness(AttackStyle.RANGE);
|
||||||
|
}
|
||||||
|
else if (player.getMeleeAttack() <= player.getRangeDefence() && player.getMeleeAttack() <= player.getMagicDefence())
|
||||||
|
{
|
||||||
|
player.setWeakness(AttackStyle.MELEE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updateEstimatedPrayer(PlayerContainer player, int restores)
|
||||||
|
{
|
||||||
|
player.setEstimatedPrayerString(getEstimatedPrayerRemaining(player, restores));
|
||||||
|
|
||||||
|
if (player.getOverhead() == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.getEstimatedPrayer() >= 0)
|
||||||
|
{
|
||||||
|
player.setEstimatedPrayer(player.getEstimatedPrayer() - player.getDrainRate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void updatePrayerDrainRate(PlayerContainer player)
|
||||||
|
{
|
||||||
|
double drainRate = 0.0;
|
||||||
|
|
||||||
|
if (player.getOverhead() != null)
|
||||||
|
{
|
||||||
|
drainRate += player.getOverhead().getDrainRate();
|
||||||
|
}
|
||||||
|
if (player.getPredictedPrayer() != null)
|
||||||
|
{
|
||||||
|
drainRate += player.getPredictedPrayer().getDrainRate();
|
||||||
|
}
|
||||||
|
if (player.getPrayer().getLevel() >= 25)
|
||||||
|
{
|
||||||
|
drainRate += Prayer.PROTECT_ITEM.getDrainRate();
|
||||||
|
}
|
||||||
|
drainRate = (((drainRate / 100)) / (1.0 + (player.getPrayerBonus() / 30.0)));
|
||||||
|
|
||||||
|
player.setDrainRate(drainRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getEstimatedPrayerRemaining(PlayerContainer player, int restores)
|
||||||
|
{
|
||||||
|
final double drainRate = player.getDrainRate();
|
||||||
|
|
||||||
|
if (drainRate == 0)
|
||||||
|
{
|
||||||
|
return "N/A";
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("Drain Rate: " + drainRate);
|
||||||
|
|
||||||
|
final int prayerLevel = player.getPrayer().getLevel();
|
||||||
|
final int restoreValue = (int) (4 * (prayerLevel * 0.25) + 8);
|
||||||
|
final double estimatedTotalPrayer = prayerLevel + (restoreValue * restores);
|
||||||
|
final double estimatedPrayer = player.getEstimatedPrayer();
|
||||||
|
final int restoreValueLeft = (int) Math.round(estimatedPrayer - player.getPrayer().getLevel());
|
||||||
|
|
||||||
|
if (player.getEstimatedPrayer() == 0)
|
||||||
|
{
|
||||||
|
player.setEstimatedPrayer(estimatedTotalPrayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (restoreValueLeft > 0)
|
||||||
|
{
|
||||||
|
return player.getPrayer().getLevel() + "(" + restoreValueLeft + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Integer.toString((int) Math.round(estimatedPrayer));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeEntries(LinkedHashMap<Integer, Integer> map, int quantity)
|
||||||
|
{
|
||||||
|
if (map.size() < quantity)
|
||||||
|
{
|
||||||
|
log.debug("Size is lower than removal quantity.");
|
||||||
|
}
|
||||||
|
for (int i = 0; i < quantity; i++)
|
||||||
|
{
|
||||||
|
if (!map.entrySet().iterator().hasNext())
|
||||||
|
{
|
||||||
|
log.debug("Attempted to remove entries, but there was not enough to remove.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.debug("Entry Removed: " + map.entrySet().iterator().next());
|
||||||
|
map.entrySet().remove(map.entrySet().iterator().next());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map.Entry getEntry(LinkedHashMap<Integer, Integer> map)
|
||||||
|
{
|
||||||
|
if (!map.isEmpty())
|
||||||
|
{
|
||||||
|
Iterator<Map.Entry<Integer, Integer>> entry = map.entrySet().iterator();
|
||||||
|
|
||||||
|
for (int i = 0; i < 1; i++)
|
||||||
|
{
|
||||||
|
entry.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry.next();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Prayer iconToPrayer(Player player)
|
||||||
|
{
|
||||||
|
if (player.getOverheadIcon() != null)
|
||||||
|
{
|
||||||
|
switch (player.getOverheadIcon())
|
||||||
|
{
|
||||||
|
case RANGED:
|
||||||
|
return Prayer.PROTECT_FROM_MISSILES;
|
||||||
|
case MAGIC:
|
||||||
|
return Prayer.PROTECT_FROM_MAGIC;
|
||||||
|
case MELEE:
|
||||||
|
return Prayer.PROTECT_FROM_MELEE;
|
||||||
|
case SMITE:
|
||||||
|
return Prayer.SMITE;
|
||||||
|
case REDEMPTION:
|
||||||
|
return Prayer.REDEMPTION;
|
||||||
|
case RETRIBUTION:
|
||||||
|
return Prayer.RETRIBUTION;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Prayer predictOffensivePrayer(int prayerLevel, AttackStyle attackStyle)
|
||||||
|
{
|
||||||
|
switch (attackStyle)
|
||||||
|
{
|
||||||
|
case MELEE:
|
||||||
|
if (prayerLevel <= 12 && prayerLevel >= 1)
|
||||||
|
{
|
||||||
|
return Prayer.BURST_OF_STRENGTH;
|
||||||
|
}
|
||||||
|
else if (prayerLevel <= 30 && prayerLevel >= 13)
|
||||||
|
{
|
||||||
|
return Prayer.SUPERHUMAN_STRENGTH;
|
||||||
|
}
|
||||||
|
else if (prayerLevel <= 59 && prayerLevel >= 31)
|
||||||
|
{
|
||||||
|
return Prayer.ULTIMATE_STRENGTH;
|
||||||
|
}
|
||||||
|
else if (prayerLevel <= 69 && prayerLevel >= 60)
|
||||||
|
{
|
||||||
|
return Prayer.CHIVALRY;
|
||||||
|
}
|
||||||
|
else if (prayerLevel >= 70)
|
||||||
|
{
|
||||||
|
return Prayer.PIETY;
|
||||||
|
}
|
||||||
|
case RANGE:
|
||||||
|
if (prayerLevel <= 8 && prayerLevel >= 1)
|
||||||
|
{
|
||||||
|
return Prayer.SHARP_EYE;
|
||||||
|
}
|
||||||
|
else if (prayerLevel <= 43 && prayerLevel >= 26)
|
||||||
|
{
|
||||||
|
return Prayer.HAWK_EYE;
|
||||||
|
}
|
||||||
|
else if (prayerLevel <= 73 && prayerLevel >= 44)
|
||||||
|
{
|
||||||
|
return Prayer.EAGLE_EYE;
|
||||||
|
}
|
||||||
|
else if (prayerLevel >= 74)
|
||||||
|
{
|
||||||
|
return Prayer.RIGOUR;
|
||||||
|
}
|
||||||
|
case MAGE:
|
||||||
|
if (prayerLevel <= 26 && prayerLevel >= 9)
|
||||||
|
{
|
||||||
|
return Prayer.MYSTIC_WILL;
|
||||||
|
}
|
||||||
|
else if (prayerLevel <= 44 && prayerLevel >= 27)
|
||||||
|
{
|
||||||
|
return Prayer.MYSTIC_LORE;
|
||||||
|
}
|
||||||
|
else if (prayerLevel <= 76 && prayerLevel >= 45)
|
||||||
|
{
|
||||||
|
return Prayer.MYSTIC_MIGHT;
|
||||||
|
}
|
||||||
|
else if (prayerLevel >= 77)
|
||||||
|
{
|
||||||
|
return Prayer.AUGURY;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return Prayer.PROTECT_ITEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String targetStringBuilder(PlayerContainer player)
|
||||||
|
{
|
||||||
|
if (player.getPlayer().getInteracting() != null)
|
||||||
|
{
|
||||||
|
Actor actor = player.getPlayer().getInteracting();
|
||||||
|
if (actor instanceof Player)
|
||||||
|
{
|
||||||
|
return "(Player) " + actor.getName();
|
||||||
|
}
|
||||||
|
else if (actor instanceof NPC)
|
||||||
|
{
|
||||||
|
return "(NPC) " + actor.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "No Target Detected";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scoutPlayer(PlayerContainer player, HttpUrl url, DiscordClient discordClient, ItemManager itemManager, Client client, int minimumValue)
|
||||||
|
{
|
||||||
|
if (player.isScouted())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<FieldEmbed> fieldList = new ArrayList<>();
|
||||||
|
//green
|
||||||
|
String color = "8388352";
|
||||||
|
|
||||||
|
if (player.getRisk() < 1000000 && player.getRisk() > 150000)
|
||||||
|
{
|
||||||
|
//blue
|
||||||
|
color = "32767";
|
||||||
|
}
|
||||||
|
else if (player.getRisk() > 1000000)
|
||||||
|
{
|
||||||
|
//orange
|
||||||
|
color = "16744448";
|
||||||
|
}
|
||||||
|
|
||||||
|
ThumbnailEmbed image = ThumbnailEmbed.builder()
|
||||||
|
.url("https://oldschool.runescape.wiki/images/a/a1/Skull_(status)_icon.png")
|
||||||
|
.height(50)
|
||||||
|
.width(50)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
if (player.getPlayer().getSkullIcon() == null)
|
||||||
|
{
|
||||||
|
image = ThumbnailEmbed.builder()
|
||||||
|
.url(ICONBASEURL + player.getWeapon() + ".png")
|
||||||
|
.height(100)
|
||||||
|
.width(100)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldList.add(FieldEmbed.builder()
|
||||||
|
.name("Risk")
|
||||||
|
.value(StackFormatter.quantityToRSDecimalStack(player.getRisk()))
|
||||||
|
.inline(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
fieldList.add(FieldEmbed.builder()
|
||||||
|
.name("World")
|
||||||
|
.value(Integer.toString(client.getWorld()))
|
||||||
|
.inline(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
fieldList.add(FieldEmbed.builder()
|
||||||
|
.name("Combat Level")
|
||||||
|
.value(Integer.toString(player.getPlayer().getCombatLevel()))
|
||||||
|
.inline(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
fieldList.add(FieldEmbed.builder()
|
||||||
|
.name("Wildy Level")
|
||||||
|
.value(Integer.toString(player.getWildyLevel()))
|
||||||
|
.inline(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
fieldList.add(FieldEmbed.builder()
|
||||||
|
.name("Location")
|
||||||
|
.value(player.getLocation())
|
||||||
|
.inline(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
fieldList.add(FieldEmbed.builder()
|
||||||
|
.name("Target")
|
||||||
|
.value(player.getTargetString())
|
||||||
|
.inline(true)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
fieldList.add(FieldEmbed.builder()
|
||||||
|
.name("Risked Items Sorted by Value")
|
||||||
|
.value("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
final int[] items = {0};
|
||||||
|
|
||||||
|
player.getRiskedGear().forEach((gear, value) ->
|
||||||
|
{
|
||||||
|
if (value <= 0 || value <= minimumValue)
|
||||||
|
{
|
||||||
|
items[0]++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStats item = itemManager.getItemStats(gear, false);
|
||||||
|
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
log.error("Item is Null: {}", gear);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldList.add(FieldEmbed.builder()
|
||||||
|
.name(item.getName())
|
||||||
|
.value("Value: " + StackFormatter.quantityToRSDecimalStack(value))
|
||||||
|
.inline(true)
|
||||||
|
.build());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (items[0] > 0)
|
||||||
|
{
|
||||||
|
fieldList.add(FieldEmbed.builder()
|
||||||
|
.name("Items below value: " + minimumValue)
|
||||||
|
.value(Integer.toString(items[0]))
|
||||||
|
.inline(true)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
message(player.getPlayer().getName(), " ", ICONBASEURL + Objects.requireNonNull(getEntry(player.getGear())).getKey() + ".png", image, fieldList, url, discordClient, color);
|
||||||
|
player.setScouted(true);
|
||||||
|
fieldList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void message(String name, String description, String iconUrl, ThumbnailEmbed thumbnail, List<FieldEmbed> fields, HttpUrl url, DiscordClient discordClient, String color)
|
||||||
|
{
|
||||||
|
log.debug("Message Contents: {}, {}, {}, {}, {}", name, description, thumbnail, Arrays.toString(fields.toArray()), url);
|
||||||
|
log.debug("Fields: {}", fields);
|
||||||
|
|
||||||
|
if (name.isEmpty() || fields.isEmpty())
|
||||||
|
{
|
||||||
|
log.error("Discord message will fail with a missing name/description/field");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Date currentTime = new Date(System.currentTimeMillis());
|
||||||
|
|
||||||
|
DiscordEmbed discordEmbed = DiscordEmbed.builder()
|
||||||
|
.author(AuthorEmbed.builder()
|
||||||
|
.icon_url(iconUrl) // Icon of npc / player
|
||||||
|
.name(name)
|
||||||
|
.build())
|
||||||
|
.thumbnail(thumbnail)
|
||||||
|
.description(description)
|
||||||
|
.fields(fields)
|
||||||
|
.footer(FooterEmbed.builder()
|
||||||
|
.icon_url("https://raw.githubusercontent.com/runelite/runelite/master/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/ultimate_ironman.png")
|
||||||
|
.text("Gabon Scouter | Time: " + SDF.format(currentTime))
|
||||||
|
.build())
|
||||||
|
.color(color)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
DiscordMessage discordMessage = new DiscordMessage("Gabon Scouter", "", "https://i.imgur.com/2A6dr7q.png");
|
||||||
|
discordMessage.getEmbeds().add(discordEmbed);
|
||||||
|
discordClient.message(url, discordMessage);
|
||||||
|
fields.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String location(PlayerContainer player, Map<WorldArea, String> map)
|
||||||
|
{
|
||||||
|
final WorldPoint wl = player.getPlayer().getWorldLocation();
|
||||||
|
int dist = 10000;
|
||||||
|
String s = "";
|
||||||
|
WorldArea closestArea = null;
|
||||||
|
for (Map.Entry<WorldArea, String> entry : map.entrySet())
|
||||||
|
{
|
||||||
|
WorldArea worldArea = entry.getKey();
|
||||||
|
|
||||||
|
if (worldArea.toWorldPointList().contains(wl))
|
||||||
|
{
|
||||||
|
s = entry.getValue();
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
int distTo = worldArea.distanceTo(wl);
|
||||||
|
if (distTo < dist)
|
||||||
|
{
|
||||||
|
dist = distTo;
|
||||||
|
closestArea = worldArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wl.getY() > (Objects.requireNonNull(closestArea).toWorldPoint().getY() + closestArea.getHeight()))
|
||||||
|
{
|
||||||
|
s = s + "N";
|
||||||
|
}
|
||||||
|
if (wl.getY() < closestArea.toWorldPoint().getY())
|
||||||
|
{
|
||||||
|
s = s + "S";
|
||||||
|
}
|
||||||
|
if (wl.getX() < closestArea.toWorldPoint().getX())
|
||||||
|
{
|
||||||
|
s = s + "W";
|
||||||
|
}
|
||||||
|
if (wl.getX() > (closestArea.toWorldPoint().getX() + closestArea.getWidth()))
|
||||||
|
{
|
||||||
|
s = s + "E";
|
||||||
|
}
|
||||||
|
s = s + " of ";
|
||||||
|
s = s + map.get(closestArea);
|
||||||
|
if (s.startsWith(" of "))
|
||||||
|
{
|
||||||
|
s = s.substring(3);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user