diff --git a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java index b3520cf609..8250c85ae6 100644 --- a/runelite-api/src/main/java/net/runelite/api/VarPlayer.java +++ b/runelite-api/src/main/java/net/runelite/api/VarPlayer.java @@ -236,6 +236,16 @@ public enum VarPlayer SETTINGS_TRANSPARENT_CHAT_TRADE_REQUEST(3006), SETTINGS_TRANSPARENT_CHAT_CHALLENGE_REQUEST(3007), + /** + * The difference, measured in minutes, between the time home teleport spell was last used and midnight, January 1, 1970 UTC. + */ + LAST_HOME_TELEPORT(892), + + /** + * The difference, measured in minutes, between the time minigame teleport was last used and midnight, January 1, 1970 UTC. + */ + LAST_MINIGAME_TELEPORT(888), + ; private final int id; diff --git a/runelite-client/src/main/java/net/runelite/client/events/PartyChanged.java b/runelite-client/src/main/java/net/runelite/client/events/PartyChanged.java index 0cb8a94c3e..be8bd63a9b 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/PartyChanged.java +++ b/runelite-client/src/main/java/net/runelite/client/events/PartyChanged.java @@ -30,5 +30,6 @@ import lombok.Value; @Value public class PartyChanged { + private final String passphrase; private final UUID partyId; } diff --git a/runelite-client/src/main/java/net/runelite/client/game/FishingSpot.java b/runelite-client/src/main/java/net/runelite/client/game/FishingSpot.java index 6ab929d173..7fcb4fbf7a 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/FishingSpot.java +++ b/runelite-client/src/main/java/net/runelite/client/game/FishingSpot.java @@ -94,8 +94,8 @@ public enum FishingSpot SACRED_EEL("Sacred eel", ItemID.SACRED_EEL, FISHING_SPOT_6488 ), - CAVE_EEL("Cave eel", ItemID.RAW_CAVE_EEL, - FISHING_SPOT_1497, FISHING_SPOT_1498, FISHING_SPOT_1499 + CAVE_EEL("Frog spawn, Cave eel", ItemID.RAW_CAVE_EEL, + FISHING_SPOT_1497, FISHING_SPOT_1498, FISHING_SPOT_1499, FISHING_SPOT_1500 ), SLIMY_EEL("Slimy eel", ItemID.RAW_SLIMY_EEL, FISHING_SPOT_2653, FISHING_SPOT_2654, FISHING_SPOT_2655 diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java index 031ba10941..58cf1cad70 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordPlugin.java @@ -37,7 +37,6 @@ import java.time.temporal.ChronoUnit; import java.util.EnumSet; import java.util.HashMap; import java.util.Map; -import java.util.UUID; import javax.imageio.ImageIO; import javax.inject.Named; import lombok.extern.slf4j.Slf4j; @@ -50,11 +49,8 @@ import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.StatChanged; import net.runelite.client.config.ConfigManager; import net.runelite.client.discord.DiscordService; -import net.runelite.client.discord.events.DiscordJoinGame; -import net.runelite.client.discord.events.DiscordReady; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; -import net.runelite.client.events.PartyChanged; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.task.Schedule; @@ -65,8 +61,7 @@ import net.runelite.client.util.LinkBrowser; import net.runelite.client.ws.PartyMember; import net.runelite.client.ws.PartyService; import net.runelite.client.ws.WSClient; -import net.runelite.http.api.ws.messages.party.UserJoin; -import net.runelite.http.api.ws.messages.party.UserPart; +import net.runelite.discord.DiscordUser; import net.runelite.http.api.ws.messages.party.UserSync; import okhttp3.Call; import okhttp3.Callback; @@ -137,11 +132,6 @@ public class DiscordPlugin extends Plugin checkForGameStateUpdate(); checkForAreaUpdate(); - if (discordService.getCurrentUser() != null) - { - partyService.setUsername(discordService.getCurrentUser().username + "#" + discordService.getCurrentUser().discriminator); - } - wsClient.registerMessage(DiscordUserInfo.class); } @@ -150,7 +140,6 @@ public class DiscordPlugin extends Plugin { clientToolbar.removeNavigation(discordButton); resetState(); - partyService.changeParty(null); wsClient.unregisterMessage(DiscordUserInfo.class); } @@ -209,30 +198,9 @@ public class DiscordPlugin extends Plugin } } - @Subscribe - public void onDiscordReady(DiscordReady event) - { - partyService.setUsername(event.getUsername() + "#" + event.getDiscriminator()); - } - - @Subscribe - public void onDiscordJoinGame(DiscordJoinGame joinGame) - { - UUID partyId = UUID.fromString(joinGame.getJoinSecret()); - partyService.changeParty(partyId); - updatePresence(); - } - @Subscribe public void onDiscordUserInfo(final DiscordUserInfo event) { - final PartyMember memberById = partyService.getMemberById(event.getMemberId()); - - if (memberById == null || memberById.getAvatar() != null) - { - return; - } - final CharMatcher matcher = CharMatcher.anyOf("abcdef0123456789"); // animated avatars contain a_ as prefix so we need to get rid of that first to check against matcher @@ -246,13 +214,7 @@ public class DiscordPlugin extends Plugin if (Strings.isNullOrEmpty(event.getAvatarId())) { - final String[] split = memberById.getName().split("#", 2); - if (split.length != 2) - { - return; - } - - int disc = Integer.parseInt(split[1]); + int disc = Integer.parseInt(event.getDiscriminator()); int avatarId = disc % 5; url = "https://cdn.discordapp.com/embed/avatars/" + avatarId + ".png"; } @@ -292,7 +254,7 @@ public class DiscordPlugin extends Plugin image = ImageIO.read(inputStream); } - partyService.setPartyMemberAvatar(memberById.getMemberId(), image); + partyService.setPartyMemberAvatar(event.getMemberId(), image); } finally { @@ -302,12 +264,6 @@ public class DiscordPlugin extends Plugin }); } - @Subscribe - public void onUserJoin(final UserJoin event) - { - updatePresence(); - } - @Subscribe public void onUserSync(final UserSync event) { @@ -315,30 +271,21 @@ public class DiscordPlugin extends Plugin if (localMember != null) { - if (discordService.getCurrentUser() != null) + final DiscordUser discordUser = discordService.getCurrentUser(); + if (discordUser != null) { final DiscordUserInfo userInfo = new DiscordUserInfo( - discordService.getCurrentUser().userId, - discordService.getCurrentUser().avatar); - + discordUser.userId, + discordUser.username, + discordUser.discriminator, + discordUser.avatar + ); userInfo.setMemberId(localMember.getMemberId()); wsClient.send(userInfo); } } } - @Subscribe - public void onUserPart(final UserPart event) - { - updatePresence(); - } - - @Subscribe - public void onPartyChanged(final PartyChanged event) - { - updatePresence(); - } - @Schedule( period = 1, unit = ChronoUnit.MINUTES @@ -348,11 +295,6 @@ public class DiscordPlugin extends Plugin discordState.checkForTimeout(); } - private void updatePresence() - { - discordState.refresh(); - } - private void resetState() { discordState.reset(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordState.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordState.java index 0fc41574cb..e2740e3c5e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordState.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordState.java @@ -38,8 +38,6 @@ import javax.inject.Named; import lombok.Data; import net.runelite.client.discord.DiscordPresence; import net.runelite.client.discord.DiscordService; -import net.runelite.client.ws.PartyService; -import static net.runelite.client.ws.PartyService.PARTY_MAX; /** * This class contains data about currently active discord state. @@ -57,7 +55,6 @@ class DiscordState private final List events = new ArrayList<>(); private final DiscordService discordService; private final DiscordConfig config; - private final PartyService party; private final String runeliteTitle; private final String runeliteVersion; private DiscordPresence lastPresence; @@ -66,14 +63,12 @@ class DiscordState private DiscordState( final DiscordService discordService, final DiscordConfig config, - final PartyService party, @Named("runelite.title") final String runeliteTitle, @Named("runelite.version") final String runeliteVersion ) { this.discordService = discordService; this.config = config; - this.party = party; this.runeliteTitle = runeliteTitle; this.runeliteVersion = runeliteVersion; } @@ -88,30 +83,6 @@ class DiscordState lastPresence = null; } - /** - * Force refresh discord presence - */ - void refresh() - { - if (lastPresence == null) - { - return; - } - - final DiscordPresence.DiscordPresenceBuilder presenceBuilder = DiscordPresence.builder() - .state(lastPresence.getState()) - .details(lastPresence.getDetails()) - .largeImageText(lastPresence.getLargeImageText()) - .startTimestamp(lastPresence.getStartTimestamp()) - .smallImageKey(lastPresence.getSmallImageKey()) - .partyMax(lastPresence.getPartyMax()); - - - setPresencePartyInfo(presenceBuilder); - - discordService.updatePresence(presenceBuilder.build()); - } - /** * Trigger new discord state update. * @@ -197,8 +168,7 @@ class DiscordState .state(MoreObjects.firstNonNull(state, "")) .details(MoreObjects.firstNonNull(details, "")) .largeImageText(runeliteTitle + " v" + versionShortHand) - .smallImageKey(imageKey) - .partyMax(PARTY_MAX); + .smallImageKey(imageKey); final Instant startTime; switch (config.elapsedTimeType()) @@ -225,8 +195,6 @@ class DiscordState presenceBuilder.startTimestamp(startTime); - setPresencePartyInfo(presenceBuilder); - final DiscordPresence presence = presenceBuilder.build(); // This is to reduce amount of RPC calls @@ -263,16 +231,4 @@ class DiscordState updatePresenceWithLatestEvent(); } } - - private void setPresencePartyInfo(DiscordPresence.DiscordPresenceBuilder presenceBuilder) - { - if (party.isInParty()) - { - presenceBuilder.partySize(party.getMembers().size()); - - // Set public party id and secret - presenceBuilder.partyId(party.getPublicPartyId().toString()); - presenceBuilder.joinSecret(party.getPartyId().toString()); - } - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordUserInfo.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordUserInfo.java index 360d58652e..1f96e67051 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordUserInfo.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordUserInfo.java @@ -33,5 +33,7 @@ import net.runelite.http.api.ws.messages.party.PartyMemberMessage; class DiscordUserInfo extends PartyMemberMessage { private final String userId; + private final String username; + private final String discriminator; private final String avatarId; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/dpscounter/DpsCounterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/dpscounter/DpsCounterPlugin.java index d158b151a7..ecd962ce85 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/dpscounter/DpsCounterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/dpscounter/DpsCounterPlugin.java @@ -208,7 +208,7 @@ public class DpsCounterPlugin extends Plugin } // If not in a party, user local player name - final String name = localMember == null ? player.getName() : localMember.getName(); + final String name = localMember == null ? player.getName() : localMember.getDisplayName(); DpsMember dpsMember = members.computeIfAbsent(name, DpsMember::new); dpsMember.addDamage(hit); @@ -240,7 +240,7 @@ public class DpsCounterPlugin extends Plugin return; } - String name = partyService.getMemberById(dpsUpdate.getMemberId()).getName(); + String name = partyService.getMemberById(dpsUpdate.getMemberId()).getDisplayName(); if (name == null) { return; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java index 4c5a3c310a..22813a8c43 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java @@ -76,9 +76,6 @@ public class HunterPlugin extends Plugin @Getter private final Map traps = new HashMap<>(); - @Getter - private Instant lastActionTime = Instant.ofEpochMilli(0); - private WorldPoint lastTickLocalPlayerLocation; @Provides @@ -98,7 +95,6 @@ public class HunterPlugin extends Plugin protected void shutDown() throws Exception { overlayManager.remove(overlay); - lastActionTime = Instant.ofEpochMilli(0); traps.clear(); } @@ -124,7 +120,6 @@ public class HunterPlugin extends Plugin { log.debug("Trap placed by \"{}\" on {}", localPlayer.getName(), trapLocation); traps.put(trapLocation, new HunterTrap(gameObject)); - lastActionTime = Instant.now(); } break; @@ -140,7 +135,6 @@ public class HunterPlugin extends Plugin { log.debug("Trap placed by \"{}\" on {}", localPlayer.getName(), localPlayer.getWorldLocation()); traps.put(trapLocation, new HunterTrap(gameObject)); - lastActionTime = Instant.now(); } break; @@ -168,7 +162,6 @@ public class HunterPlugin extends Plugin log.debug("Trap placed by \"{}\" on {} facing {}", localPlayer.getName(), translatedTrapLocation, trapOrientation); traps.put(translatedTrapLocation, new HunterTrap(gameObject)); - lastActionTime = Instant.now(); } break; @@ -201,7 +194,6 @@ public class HunterPlugin extends Plugin { myTrap.setState(HunterTrap.State.FULL); myTrap.resetTimer(); - lastActionTime = Instant.now(); if (config.maniacalMonkeyNotify() && myTrap.getObjectId() == ObjectID.MONKEY_TRAP) { @@ -222,7 +214,6 @@ public class HunterPlugin extends Plugin { myTrap.setState(HunterTrap.State.EMPTY); myTrap.resetTimer(); - lastActionTime = Instant.now(); } break; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/Builders.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/Builders.java index e5cee44b35..ff2d65faef 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/Builders.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/Builders.java @@ -40,11 +40,6 @@ public class Builders return new Food(p); } - public static Effect combo(int primaries, SingleEffect... effect) - { - return new Combo(primaries, effect); - } - public static Effect combo(SingleEffect... effect) { return new Combo(effect); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TeleportWidget.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/CappedStatBoost.java similarity index 53% rename from runelite-client/src/main/java/net/runelite/client/plugins/timers/TeleportWidget.java rename to runelite-client/src/main/java/net/runelite/client/plugins/itemstats/CappedStatBoost.java index 762d1d19be..99aa241a44 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TeleportWidget.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/CappedStatBoost.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Jordan Atwood + * Copyright (c) 2022 Hydrox6 * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,47 +22,41 @@ * (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.timers; +package net.runelite.client.plugins.itemstats; -import com.google.common.collect.ImmutableList; -import java.util.Collection; -import javax.annotation.Nullable; -import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.Client; +import net.runelite.client.plugins.itemstats.delta.DeltaCalculator; +import net.runelite.client.plugins.itemstats.stats.Stat; -enum TeleportWidget +/** + * A stat boost using the real stat level, that can only boost a certain amount above the stat level. + */ +public class CappedStatBoost extends StatBoost { - HOME_TELEPORT, - MINIGAME_TELEPORT, - TRAILBLAZER_AREA_TELEPORT, - ; + private final DeltaCalculator deltaCalculator; + private final DeltaCalculator capCalculator; - private static final Collection HOME_TELEPORT_IDS = ImmutableList.of( - WidgetInfo.SPELL_LUMBRIDGE_HOME_TELEPORT.getId(), - WidgetInfo.SPELL_EDGEVILLE_HOME_TELEPORT.getId(), - WidgetInfo.SPELL_LUNAR_HOME_TELEPORT.getId(), - WidgetInfo.SPELL_ARCEUUS_HOME_TELEPORT.getId(), - WidgetInfo.SPELL_KOUREND_HOME_TELEPORT.getId(), - WidgetInfo.SPELL_CATHERBY_HOME_TELEPORT.getId() - ); - private static final Collection MINIGAME_TELEPORT_IDS = ImmutableList.of( - WidgetInfo.MINIGAME_TELEPORT_BUTTON.getId() - ); - - @Nullable - static TeleportWidget of(int widgetId) + public CappedStatBoost(Stat stat, DeltaCalculator deltaCalculator, DeltaCalculator capCalculator) { - if (HOME_TELEPORT_IDS.contains(widgetId)) - { - return HOME_TELEPORT; - } - else if (MINIGAME_TELEPORT_IDS.contains(widgetId)) - { - return MINIGAME_TELEPORT; - } - else if (widgetId == WidgetInfo.TRAILBLAZER_AREA_TELEPORT.getId()) - { - return TRAILBLAZER_AREA_TELEPORT; - } - return null; + super(stat, true); + this.deltaCalculator = deltaCalculator; + this.capCalculator = capCalculator; } + + @Override + public int heals(Client client) + { + final int current = getStat().getValue(client); + final int max = getStat().getMaximum(client); + final int delta = deltaCalculator.calculateDelta(max); + final int cap = capCalculator.calculateDelta(max); + + if (delta + current <= max + cap) + { + return delta; + } + + return max + cap - current; + } + } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/Combo.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/Combo.java index 9749f1a17b..9248b0d202 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/Combo.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/Combo.java @@ -24,36 +24,23 @@ */ package net.runelite.client.plugins.itemstats; +import lombok.AllArgsConstructor; import net.runelite.api.Client; +@AllArgsConstructor public class Combo implements Effect { private final SingleEffect[] calcs; - private final int numPrimaries; - - public Combo(SingleEffect[] calcs) - { - this(1, calcs); - } - - public Combo(int numPrimaries, SingleEffect[] calcs) - { - this.numPrimaries = numPrimaries; - this.calcs = calcs; - } @Override public StatsChanges calculate(Client client) { StatsChanges out = new StatsChanges(calcs.length); StatChange[] statChanges = out.getStatChanges(); + Positivity positivity = Positivity.NO_CHANGE; for (int i = 0; i < calcs.length; i++) { statChanges[i] = calcs[i].effect(client); - } - Positivity positivity = Positivity.NO_CHANGE; - for (int i = 0; i < numPrimaries; i++) - { if (positivity.ordinal() < statChanges[i].getPositivity().ordinal()) { positivity = statChanges[i].getPositivity(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java index bd97701644..dd322cd3fc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java @@ -32,8 +32,11 @@ import java.util.Map; import lombok.extern.slf4j.Slf4j; import static net.runelite.api.ItemID.*; import static net.runelite.client.plugins.itemstats.Builders.*; +import net.runelite.client.plugins.itemstats.delta.DeltaPercentage; import net.runelite.client.plugins.itemstats.food.Anglerfish; +import net.runelite.client.plugins.itemstats.potions.AncientBrew; import net.runelite.client.plugins.itemstats.potions.GauntletPotion; +import net.runelite.client.plugins.itemstats.potions.MixedPotion; import net.runelite.client.plugins.itemstats.potions.PrayerPotion; import net.runelite.client.plugins.itemstats.potions.SaradominBrew; import net.runelite.client.plugins.itemstats.potions.StaminaPotion; @@ -62,7 +65,8 @@ public class ItemStatChanges PINEAPPLE_RING, PINEAPPLE_CHUNKS, SPICY_SAUCE, CHEESE, SPINACH_ROLL, LEMON, LEMON_CHUNKS, LEMON_SLICES, LIME, LIME_CHUNKS, LIME_SLICES, DWELLBERRIES, KING_WORM, MINCED_MEAT, SPICY_TOMATO, WHITE_PEARL); add(food(3), SHRIMPS, COOKED_MEAT, COOKED_CHICKEN, ROE, CHOCOLATE_BAR, UGTHANKI_MEAT, TOADS_LEGS, ONION__TOMATO, - SPICY_MINCED_MEAT, SLICE_OF_BIRTHDAY_CAKE, LOCUST_MEAT); + SPICY_MINCED_MEAT, SLICE_OF_BIRTHDAY_CAKE, LOCUST_MEAT, RELICYMS_MIX1, RELICYMS_MIX2, ANTIPOISON_MIX1, + ANTIPOISON_MIX2); add(food(4), SARDINE, CAKE, _23_CAKE, SLICE_OF_CAKE, CHOCOLATEY_MILK, BAKED_POTATO, EDIBLE_SEAWEED, MOONLIGHT_MEAD, MOONLIGHT_MEAD4, MOONLIGHT_MEAD3, MOONLIGHT_MEAD2, MOONLIGHT_MEAD1, MONKEY_NUTS); add(food(5), BREAD, HERRING, CHOCOLATE_CAKE, _23_CHOCOLATE_CAKE, CHOCOLATE_SLICE, COOKED_RABBIT, CHILLI_CON_CARNE, @@ -70,7 +74,9 @@ public class ItemStatChanges SCRAMBLED_EGG, MONKEY_BAR, TCHIKI_MONKEY_NUTS, TCHIKI_NUT_PASTE, RED_BANANA, SLICED_RED_BANANA); add(food(6), CHOCICE, MACKEREL, MEAT_PIE, HALF_A_MEAT_PIE, GUANIC_BAT_0, ROAST_BIRD_MEAT, SQUARE_SANDWICH, ROLL, BAGUETTE, TRIANGLE_SANDWICH, GIANT_CARP, MOONLIGHT_MEADM, MOONLIGHT_MEADM4, MOONLIGHT_MEADM3, MOONLIGHT_MEADM2, - MOONLIGHT_MEADM1, STEAK_SANDWICH, GIANT_FROG_LEGS); + MOONLIGHT_MEADM1, STEAK_SANDWICH, GIANT_FROG_LEGS, ANTIFIRE_MIX1, ANTIFIRE_MIX2, EXTENDED_ANTIFIRE_MIX1, + EXTENDED_ANTIFIRE_MIX2, SUPER_ANTIFIRE_MIX1, SUPER_ANTIFIRE_MIX2, EXTENDED_SUPER_ANTIFIRE_MIX1, + EXTENDED_SUPER_ANTIFIRE_MIX2, ANTIPOISON_SUPERMIX1, ANTIPOISON_SUPERMIX2, ANTIDOTE_MIX1, ANTIDOTE_MIX2); add(food(7), TROUT, COD, PLAIN_PIZZA, _12_PLAIN_PIZZA, APPLE_PIE, HALF_AN_APPLE_PIE, ROAST_RABBIT, PREMADE_CH_CRUNCH, CHOCCHIP_CRUNCHIES, PREMADE_SY_CRUNCH, SPICY_CRUNCHIES); add(food(8), PIKE, ROAST_BEAST_MEAT, MEAT_PIZZA, _12_MEAT_PIZZA, PREMADE_WM_CRUN, WORM_CRUNCHIES, PREMADE_TD_CRUNCH, @@ -100,9 +106,9 @@ public class ItemStatChanges add(food(maxHP -> (int) Math.ceil(maxHP * .05)), WATERMELON_SLICE); add(food(perc(.1, 1)), COOKED_SWEETCORN, SWEETCORN_7088 /* Bowl of cooked sweetcorn */); add(combo(food(1), boost(DEFENCE, perc(.02, 1))), CABBAGE_1967 /* Draynor Manor */); - add(combo(2, food(8), heal(RUN_ENERGY, 5)), PAPAYA_FRUIT); - add(combo(2, food(3), boost(ATTACK, perc(.02, 2))), CUP_OF_TEA_1978 /* Standard tea */); - add(combo(2, food(3), new NettleTeaRunEnergy()), NETTLE_TEA, NETTLE_TEA_4240 /* Milky */, CUP_OF_TEA_4242 /* Nettle */, CUP_OF_TEA_4243 /* Milky nettle */); + add(combo(food(8), heal(RUN_ENERGY, 5)), PAPAYA_FRUIT); + add(combo(food(3), boost(ATTACK, perc(.02, 2))), CUP_OF_TEA_1978 /* Standard tea */); + add(combo(food(3), new NettleTeaRunEnergy()), NETTLE_TEA, NETTLE_TEA_4240 /* Milky */, CUP_OF_TEA_4242 /* Nettle */, CUP_OF_TEA_4243 /* Milky nettle */); add(range(food(5), food(7)), THIN_SNAIL_MEAT); add(range(food(5), food(8)), LEAN_SNAIL_MEAT); add(range(food(7), food(9)), FAT_SNAIL_MEAT); @@ -122,39 +128,39 @@ public class ItemStatChanges add(combo(food(11), dec(ATTACK, 2)), JUG_OF_WINE); add(combo(food(14), dec(ATTACK, 3)), BOTTLE_OF_WINE); add(combo(food(7), dec(ATTACK, 2)), HALF_FULL_WINE_JUG); - add(combo(dec(ATTACK, 3)), JUG_OF_BAD_WINE); - add(combo(3, food(5), new SimpleStatBoost(STRENGTH, true, perc(.05, 1)), new BoostedStatBoost(ATTACK, false, perc(.02, -3))), PREMADE_SGG, SHORT_GREEN_GUY, BRANDY, GIN, VODKA, WHISKY); - add(combo(3, food(7), new SimpleStatBoost(STRENGTH, true, perc(.05, 2)), new BoostedStatBoost(ATTACK, false, perc(.02, -3))), PREMADE_BLURB_SP, BLURBERRY_SPECIAL); - add(combo(3, food(5), new SimpleStatBoost(STRENGTH, true, perc(.05, 2)), new BoostedStatBoost(ATTACK, false, perc(.02, -3))), PREMADE_DR_DRAGON, DRUNK_DRAGON, PREMADE_CHOC_SDY, CHOC_SATURDAY); - add(combo(3, food(5), new SimpleStatBoost(STRENGTH, true, perc(.06, 1)), new BoostedStatBoost(ATTACK, false, perc(.02, -3))), PREMADE_WIZ_BLZD, WIZARD_BLIZZARD); - add(combo(3, food(3), new SimpleStatBoost(STRENGTH, true, perc(.04, 1)), new BoostedStatBoost(ATTACK, false, perc(.05, -3))), GROG); - add(combo(3, food(1), boost(STRENGTH, perc(.02, 1)), new BoostedStatBoost(ATTACK, false, perc(.06, -1))), BEER, BEER_7740); - add(combo(3, food(4), boost(STRENGTH, perc(.04, 2)), new BoostedStatBoost(ATTACK, false, perc(.1, -2))), BEER_TANKARD); - add(combo(3, food(15), boost(STRENGTH, perc(.1, 2)), new BoostedStatBoost(ATTACK, false, perc(.5, -4))), KEG_OF_BEER_3801 /* Non-quest version */); - add(combo(4, boost(ATTACK, 5), boost(STRENGTH, 5), heal(MAGIC, -5), heal(PRAYER, -5)), BLOOD_PINT); - add(combo(3, food(1), boost(STRENGTH, 2), new BoostedStatBoost(ATTACK, false, perc(.05, -2))), ASGARNIAN_ALE, ASGARNIAN_ALE1, ASGARNIAN_ALE2, ASGARNIAN_ALE3, ASGARNIAN_ALE4, ASGARNIAN_ALE_7744); - add(combo(3, food(1), boost(STRENGTH, 3), new BoostedStatBoost(ATTACK, false, perc(.05, -3))), ASGARNIAN_ALEM, ASGARNIAN_ALEM1, ASGARNIAN_ALEM2, ASGARNIAN_ALEM3, ASGARNIAN_ALEM4); - add(combo(4, food(1), boost(WOODCUTTING, 1), new BoostedStatBoost(ATTACK, false, perc(.02, -2)), new BoostedStatBoost(STRENGTH, false, perc(.02, -2))), AXEMANS_FOLLY, AXEMANS_FOLLY1, AXEMANS_FOLLY2, AXEMANS_FOLLY3, AXEMANS_FOLLY4); - add(combo(4, food(2), boost(WOODCUTTING, 2), new BoostedStatBoost(ATTACK, false, perc(.02, -3)), new BoostedStatBoost(STRENGTH, false, perc(.02, -3))), AXEMANS_FOLLYM, AXEMANS_FOLLYM1, AXEMANS_FOLLYM2, AXEMANS_FOLLYM3, AXEMANS_FOLLYM4); - add(combo(5, food(1), boost(THIEVING, 1), boost(ATTACK, 1), new BoostedStatBoost(DEFENCE, false, perc(.06, -3)), new BoostedStatBoost(STRENGTH, false, perc(.06, -3))), BANDITS_BREW); - add(combo(4, food(1), new SimpleStatBoost(COOKING, true, perc(.05, 1)), new BoostedStatBoost(ATTACK, false, perc(.05, -2)), new BoostedStatBoost(STRENGTH, false, perc(.05, -2))), CHEFS_DELIGHT, CHEFS_DELIGHT1, CHEFS_DELIGHT2, CHEFS_DELIGHT3, CHEFS_DELIGHT4, CHEFS_DELIGHT_7754); - add(combo(4, food(2), new SimpleStatBoost(COOKING, true, perc(.05, 2)), new BoostedStatBoost(ATTACK, false, perc(.05, -3)), new BoostedStatBoost(STRENGTH, false, perc(.05, -3))), CHEFS_DELIGHTM, CHEFS_DELIGHTM1, CHEFS_DELIGHTM2, CHEFS_DELIGHTM3, CHEFS_DELIGHTM4); - add(combo(4, food(1), boost(FARMING, 1), new BoostedStatBoost(ATTACK, false, perc(.02, -2)), new BoostedStatBoost(STRENGTH, false, perc(.02, -2))), CIDER, CIDER1, CIDER2, CIDER3, CIDER4, CIDER_7752); - add(combo(4, food(2), boost(FARMING, 2), new BoostedStatBoost(ATTACK, false, perc(.02, -3)), new BoostedStatBoost(STRENGTH, false, perc(.02, -3))), MATURE_CIDER, CIDERM1, CIDERM2, CIDERM3, CIDERM4); - add(combo(3, food(1), boost(STRENGTH, 2), new BoostedStatBoost(ATTACK, false, perc(.05, -2))), DRAGON_BITTER, DRAGON_BITTER1, DRAGON_BITTER2, DRAGON_BITTER3, DRAGON_BITTER4, DRAGON_BITTER_7748); - add(combo(3, food(2), boost(STRENGTH, 3), new BoostedStatBoost(ATTACK, false, perc(.05, -2))), DRAGON_BITTERM, DRAGON_BITTERM1, DRAGON_BITTERM2, DRAGON_BITTERM3, DRAGON_BITTERM4); - add(combo(6, food(1), boost(MINING, 1), boost(SMITHING, 1), new BoostedStatBoost(ATTACK, false, perc(.04, -2)), new BoostedStatBoost(DEFENCE, false, perc(.04, -2)), new BoostedStatBoost(STRENGTH, false, perc(.04, -2))), DWARVEN_STOUT, DWARVEN_STOUT1, DWARVEN_STOUT2, DWARVEN_STOUT3, DWARVEN_STOUT4); - add(combo(6, food(2), boost(MINING, 2), boost(SMITHING, 2), new BoostedStatBoost(ATTACK, false, perc(.04, -3)), new BoostedStatBoost(DEFENCE, false, perc(.04, -3)), new BoostedStatBoost(STRENGTH, false, perc(.04, -3))), DWARVEN_STOUTM, DWARVEN_STOUTM1, DWARVEN_STOUTM2, DWARVEN_STOUTM3, DWARVEN_STOUTM4); - add(combo(5, food(1), boost(HERBLORE, 1), new BoostedStatBoost(ATTACK, false, perc(.04, -2)), new BoostedStatBoost(DEFENCE, false, perc(.04, -2)), new BoostedStatBoost(STRENGTH, false, perc(.04, -2))), GREENMANS_ALE, GREENMANS_ALE1, GREENMANS_ALE2, GREENMANS_ALE3, GREENMANS_ALE4, GREENMANS_ALE_7746); - add(combo(5, food(2), boost(HERBLORE, 2), new BoostedStatBoost(ATTACK, false, perc(.04, -3)), new BoostedStatBoost(DEFENCE, false, perc(.04, -3)), new BoostedStatBoost(STRENGTH, false, perc(.04, -3))), GREENMANS_ALEM, GREENMANS_ALEM1, GREENMANS_ALEM2, GREENMANS_ALEM3, GREENMANS_ALEM4); - add(combo(5, food(1), boost(SLAYER, 2), new BoostedStatBoost(ATTACK, false, perc(.02, -2)), new BoostedStatBoost(DEFENCE, false, perc(.02, -2)), new BoostedStatBoost(STRENGTH, false, perc(.02, -2))), SLAYERS_RESPITE, SLAYERS_RESPITE1, SLAYERS_RESPITE2, SLAYERS_RESPITE3, SLAYERS_RESPITE4); - add(combo(5, food(2), boost(SLAYER, 4), new BoostedStatBoost(ATTACK, false, perc(.02, -3)), new BoostedStatBoost(DEFENCE, false, perc(.02, -3)), new BoostedStatBoost(STRENGTH, false, perc(.02, -3))), SLAYERS_RESPITEM, SLAYERS_RESPITEM1, SLAYERS_RESPITEM2, SLAYERS_RESPITEM3, SLAYERS_RESPITEM4); - add(combo(5, food(1), new SimpleStatBoost(MAGIC, true, perc(.02, 2)), new BoostedStatBoost(ATTACK, false, perc(.05, -1)), new BoostedStatBoost(DEFENCE, false, perc(.05, -1)), new BoostedStatBoost(STRENGTH, false, perc(.05, -1))), WIZARDS_MIND_BOMB, MIND_BOMB1, MIND_BOMB2, MIND_BOMB3, MIND_BOMB4); - add(combo(5, food(2), new SimpleStatBoost(MAGIC, true, perc(.02, 3)), new BoostedStatBoost(ATTACK, false, perc(.05, -2)), new BoostedStatBoost(DEFENCE, false, perc(.05, -2)), new BoostedStatBoost(STRENGTH, false, perc(.05, -2))), MATURE_WMB, MIND_BOMBM1, MIND_BOMBM2, MIND_BOMBM3, MIND_BOMBM4); - add(combo(10, food(14), boost(STRENGTH, 3), boost(MINING, 1), heal(PRAYER, perc(.06, -1)), new BoostedStatBoost(AGILITY, false, perc(.09, -3)), new BoostedStatBoost(ATTACK, false, perc(.06, -1)), new BoostedStatBoost(DEFENCE, false, perc(.08, -2)), new BoostedStatBoost(HERBLORE, false, perc(.06, -1)), new BoostedStatBoost(MAGIC, false, perc(.05, -1)), new BoostedStatBoost(RANGED, false, perc(.06, -1))), BRAINDEATH_RUM); - add(combo(2, food(2), heal(PRAYER, perc(.04, -2))), BLOODY_BRACER); - add(combo(3, food(1), boost(AGILITY, 1), heal(STRENGTH, -1)), ELVEN_DAWN); - add(combo(3, boost(RANGED, 4), new BoostedStatBoost(STRENGTH, false, perc(.04, -2)), new BoostedStatBoost(MAGIC, false, perc(.04, -2))), LIZARDKICKER); + add(dec(ATTACK, 3), JUG_OF_BAD_WINE); + add(combo(food(5), new SimpleStatBoost(STRENGTH, true, perc(.05, 1)), new BoostedStatBoost(ATTACK, false, perc(.02, -3))), PREMADE_SGG, SHORT_GREEN_GUY, BRANDY, GIN, VODKA, WHISKY); + add(combo(food(7), new SimpleStatBoost(STRENGTH, true, perc(.05, 2)), new BoostedStatBoost(ATTACK, false, perc(.02, -3))), PREMADE_BLURB_SP, BLURBERRY_SPECIAL); + add(combo(food(5), new SimpleStatBoost(STRENGTH, true, perc(.05, 2)), new BoostedStatBoost(ATTACK, false, perc(.02, -3))), PREMADE_DR_DRAGON, DRUNK_DRAGON, PREMADE_CHOC_SDY, CHOC_SATURDAY); + add(combo(food(5), new SimpleStatBoost(STRENGTH, true, perc(.06, 1)), new BoostedStatBoost(ATTACK, false, perc(.02, -3))), PREMADE_WIZ_BLZD, WIZARD_BLIZZARD); + add(combo(food(3), new SimpleStatBoost(STRENGTH, true, perc(.04, 1)), new BoostedStatBoost(ATTACK, false, perc(.05, -3))), GROG); + add(combo(food(1), boost(STRENGTH, perc(.02, 1)), new BoostedStatBoost(ATTACK, false, perc(.06, -1))), BEER, BEER_7740); + add(combo(food(4), boost(STRENGTH, perc(.04, 2)), new BoostedStatBoost(ATTACK, false, perc(.1, -2))), BEER_TANKARD); + add(combo(food(15), boost(STRENGTH, perc(.1, 2)), new BoostedStatBoost(ATTACK, false, perc(.5, -4))), KEG_OF_BEER_3801 /* Non-quest version */); + add(combo(boost(ATTACK, 5), boost(STRENGTH, 5), heal(MAGIC, -5), heal(PRAYER, -5)), BLOOD_PINT); + add(combo(food(1), boost(STRENGTH, 2), new BoostedStatBoost(ATTACK, false, perc(.05, -2))), ASGARNIAN_ALE, ASGARNIAN_ALE1, ASGARNIAN_ALE2, ASGARNIAN_ALE3, ASGARNIAN_ALE4, ASGARNIAN_ALE_7744); + add(combo(food(1), boost(STRENGTH, 3), new BoostedStatBoost(ATTACK, false, perc(.05, -3))), ASGARNIAN_ALEM, ASGARNIAN_ALEM1, ASGARNIAN_ALEM2, ASGARNIAN_ALEM3, ASGARNIAN_ALEM4); + add(combo(food(1), boost(WOODCUTTING, 1), new BoostedStatBoost(ATTACK, false, perc(.02, -2)), new BoostedStatBoost(STRENGTH, false, perc(.02, -2))), AXEMANS_FOLLY, AXEMANS_FOLLY1, AXEMANS_FOLLY2, AXEMANS_FOLLY3, AXEMANS_FOLLY4); + add(combo(food(2), boost(WOODCUTTING, 2), new BoostedStatBoost(ATTACK, false, perc(.02, -3)), new BoostedStatBoost(STRENGTH, false, perc(.02, -3))), AXEMANS_FOLLYM, AXEMANS_FOLLYM1, AXEMANS_FOLLYM2, AXEMANS_FOLLYM3, AXEMANS_FOLLYM4); + add(combo(food(1), boost(THIEVING, 1), boost(ATTACK, 1), new BoostedStatBoost(DEFENCE, false, perc(.06, -3)), new BoostedStatBoost(STRENGTH, false, perc(.06, -3))), BANDITS_BREW); + add(combo(food(1), new SimpleStatBoost(COOKING, true, perc(.05, 1)), new BoostedStatBoost(ATTACK, false, perc(.05, -2)), new BoostedStatBoost(STRENGTH, false, perc(.05, -2))), CHEFS_DELIGHT, CHEFS_DELIGHT1, CHEFS_DELIGHT2, CHEFS_DELIGHT3, CHEFS_DELIGHT4, CHEFS_DELIGHT_7754); + add(combo(food(2), new SimpleStatBoost(COOKING, true, perc(.05, 2)), new BoostedStatBoost(ATTACK, false, perc(.05, -3)), new BoostedStatBoost(STRENGTH, false, perc(.05, -3))), CHEFS_DELIGHTM, CHEFS_DELIGHTM1, CHEFS_DELIGHTM2, CHEFS_DELIGHTM3, CHEFS_DELIGHTM4); + add(combo(food(1), boost(FARMING, 1), new BoostedStatBoost(ATTACK, false, perc(.02, -2)), new BoostedStatBoost(STRENGTH, false, perc(.02, -2))), CIDER, CIDER1, CIDER2, CIDER3, CIDER4, CIDER_7752); + add(combo(food(2), boost(FARMING, 2), new BoostedStatBoost(ATTACK, false, perc(.02, -3)), new BoostedStatBoost(STRENGTH, false, perc(.02, -3))), MATURE_CIDER, CIDERM1, CIDERM2, CIDERM3, CIDERM4); + add(combo(food(1), boost(STRENGTH, 2), new BoostedStatBoost(ATTACK, false, perc(.05, -2))), DRAGON_BITTER, DRAGON_BITTER1, DRAGON_BITTER2, DRAGON_BITTER3, DRAGON_BITTER4, DRAGON_BITTER_7748); + add(combo(food(2), boost(STRENGTH, 3), new BoostedStatBoost(ATTACK, false, perc(.05, -2))), DRAGON_BITTERM, DRAGON_BITTERM1, DRAGON_BITTERM2, DRAGON_BITTERM3, DRAGON_BITTERM4); + add(combo(food(1), boost(MINING, 1), boost(SMITHING, 1), new BoostedStatBoost(ATTACK, false, perc(.04, -2)), new BoostedStatBoost(DEFENCE, false, perc(.04, -2)), new BoostedStatBoost(STRENGTH, false, perc(.04, -2))), DWARVEN_STOUT, DWARVEN_STOUT1, DWARVEN_STOUT2, DWARVEN_STOUT3, DWARVEN_STOUT4); + add(combo(food(2), boost(MINING, 2), boost(SMITHING, 2), new BoostedStatBoost(ATTACK, false, perc(.04, -3)), new BoostedStatBoost(DEFENCE, false, perc(.04, -3)), new BoostedStatBoost(STRENGTH, false, perc(.04, -3))), DWARVEN_STOUTM, DWARVEN_STOUTM1, DWARVEN_STOUTM2, DWARVEN_STOUTM3, DWARVEN_STOUTM4); + add(combo(food(1), boost(HERBLORE, 1), new BoostedStatBoost(ATTACK, false, perc(.04, -2)), new BoostedStatBoost(DEFENCE, false, perc(.04, -2)), new BoostedStatBoost(STRENGTH, false, perc(.04, -2))), GREENMANS_ALE, GREENMANS_ALE1, GREENMANS_ALE2, GREENMANS_ALE3, GREENMANS_ALE4, GREENMANS_ALE_7746); + add(combo(food(2), boost(HERBLORE, 2), new BoostedStatBoost(ATTACK, false, perc(.04, -3)), new BoostedStatBoost(DEFENCE, false, perc(.04, -3)), new BoostedStatBoost(STRENGTH, false, perc(.04, -3))), GREENMANS_ALEM, GREENMANS_ALEM1, GREENMANS_ALEM2, GREENMANS_ALEM3, GREENMANS_ALEM4); + add(combo(food(1), boost(SLAYER, 2), new BoostedStatBoost(ATTACK, false, perc(.02, -2)), new BoostedStatBoost(DEFENCE, false, perc(.02, -2)), new BoostedStatBoost(STRENGTH, false, perc(.02, -2))), SLAYERS_RESPITE, SLAYERS_RESPITE1, SLAYERS_RESPITE2, SLAYERS_RESPITE3, SLAYERS_RESPITE4); + add(combo(food(2), boost(SLAYER, 4), new BoostedStatBoost(ATTACK, false, perc(.02, -3)), new BoostedStatBoost(DEFENCE, false, perc(.02, -3)), new BoostedStatBoost(STRENGTH, false, perc(.02, -3))), SLAYERS_RESPITEM, SLAYERS_RESPITEM1, SLAYERS_RESPITEM2, SLAYERS_RESPITEM3, SLAYERS_RESPITEM4); + add(combo(food(1), new SimpleStatBoost(MAGIC, true, perc(.02, 2)), new BoostedStatBoost(ATTACK, false, perc(.05, -1)), new BoostedStatBoost(DEFENCE, false, perc(.05, -1)), new BoostedStatBoost(STRENGTH, false, perc(.05, -1))), WIZARDS_MIND_BOMB, MIND_BOMB1, MIND_BOMB2, MIND_BOMB3, MIND_BOMB4); + add(combo(food(2), new SimpleStatBoost(MAGIC, true, perc(.02, 3)), new BoostedStatBoost(ATTACK, false, perc(.05, -2)), new BoostedStatBoost(DEFENCE, false, perc(.05, -2)), new BoostedStatBoost(STRENGTH, false, perc(.05, -2))), MATURE_WMB, MIND_BOMBM1, MIND_BOMBM2, MIND_BOMBM3, MIND_BOMBM4); + add(combo(food(14), boost(STRENGTH, 3), boost(MINING, 1), heal(PRAYER, perc(.06, -1)), new BoostedStatBoost(AGILITY, false, perc(.09, -3)), new BoostedStatBoost(ATTACK, false, perc(.06, -1)), new BoostedStatBoost(DEFENCE, false, perc(.08, -2)), new BoostedStatBoost(HERBLORE, false, perc(.06, -1)), new BoostedStatBoost(MAGIC, false, perc(.05, -1)), new BoostedStatBoost(RANGED, false, perc(.06, -1))), BRAINDEATH_RUM); + add(combo(food(2), heal(PRAYER, perc(.04, -2))), BLOODY_BRACER); + add(combo(food(1), boost(AGILITY, 1), heal(STRENGTH, -1)), ELVEN_DAWN); + add(combo(boost(RANGED, 4), new BoostedStatBoost(STRENGTH, false, perc(.04, -2)), new BoostedStatBoost(MAGIC, false, perc(.04, -2))), LIZARDKICKER); // Sq'irk Juice add(heal(RUN_ENERGY, 5), WINTER_SQIRKJUICE); @@ -163,86 +169,141 @@ public class ItemStatChanges add(combo(heal(RUN_ENERGY, 20), boost(THIEVING, 3)), SUMMER_SQIRKJUICE); // Combat potions - add(boost(ATTACK, perc(.10, 3)), ATTACK_POTION1, ATTACK_POTION2, ATTACK_POTION3, ATTACK_POTION4); - add(boost(STRENGTH, perc(.10, 3)), STRENGTH_POTION1, STRENGTH_POTION2, STRENGTH_POTION3, STRENGTH_POTION4); - add(boost(DEFENCE, perc(.10, 3)), DEFENCE_POTION1, DEFENCE_POTION2, DEFENCE_POTION3, DEFENCE_POTION4); - add(boost(MAGIC, 4), MAGIC_POTION1, MAGIC_POTION2, MAGIC_POTION3, MAGIC_POTION4); - add(boost(RANGED, perc(.10, 4)), RANGING_POTION1, RANGING_POTION2, RANGING_POTION3, RANGING_POTION4, + final SingleEffect attackPot = boost(ATTACK, perc(.10, 3)); + final SingleEffect strengthPot = boost(STRENGTH, perc(.10, 3)); + final SingleEffect defencePot = boost(DEFENCE, perc(.10, 3)); + final Effect combatPot = combo(attackPot, strengthPot); + final Effect magicEssence = boost(MAGIC, 3); + final SingleEffect magicPot = boost(MAGIC, 4); + final SingleEffect imbuedHeart = boost(MAGIC, perc(.10, 1)); + final SingleEffect rangingPot = boost(RANGED, perc(.10, 4)); + final SingleEffect superAttackPot = boost(ATTACK, perc(.15, 5)); + final SingleEffect superStrengthPot = boost(STRENGTH, perc(.15, 5)); + final SingleEffect superDefencePot = boost(DEFENCE, perc(.15, 5)); + final SingleEffect superMagicPot = boost(MAGIC, perc(.15, 5)); + final SingleEffect superRangingPot = boost(RANGED, perc(.15, 5)); + final SingleEffect divinePot = heal(HITPOINTS, -10); + final Effect zamorakBrew = combo(boost(ATTACK, perc(.20, 2)), boost(STRENGTH, perc(.12, 2)), heal(PRAYER, perc(.10, 0)), new BoostedStatBoost(DEFENCE, false, perc(.10, -2)), new BoostedStatBoost(HITPOINTS, false, perc(-.12, 0))); + final Effect ancientBrew = new AncientBrew(); + add(attackPot, ATTACK_POTION1, ATTACK_POTION2, ATTACK_POTION3, ATTACK_POTION4); + add(strengthPot, STRENGTH_POTION1, STRENGTH_POTION2, STRENGTH_POTION3, STRENGTH_POTION4); + add(defencePot, DEFENCE_POTION1, DEFENCE_POTION2, DEFENCE_POTION3, DEFENCE_POTION4); + add(magicPot, MAGIC_POTION1, MAGIC_POTION2, MAGIC_POTION3, MAGIC_POTION4); + add(rangingPot, RANGING_POTION1, RANGING_POTION2, RANGING_POTION3, RANGING_POTION4, RANGING_POTION4_23551, RANGING_POTION3_23553, RANGING_POTION2_23555, RANGING_POTION1_23557 /* LMS */); - add(combo(2, boost(ATTACK, perc(.10, 3)), boost(STRENGTH, perc(.10, 3))), - COMBAT_POTION1, COMBAT_POTION2, COMBAT_POTION3, COMBAT_POTION4, + add(combatPot, COMBAT_POTION1, COMBAT_POTION2, COMBAT_POTION3, COMBAT_POTION4, COMBAT_POTION4_26150, COMBAT_POTION3_26151, COMBAT_POTION2_26152, COMBAT_POTION1_26153 /* Deadman starter pack */); - add(boost(ATTACK, perc(.15, 5)), SUPER_ATTACK1, SUPER_ATTACK2, SUPER_ATTACK3, SUPER_ATTACK4); - add(boost(STRENGTH, perc(.15, 5)), SUPER_STRENGTH1, SUPER_STRENGTH2, SUPER_STRENGTH3, SUPER_STRENGTH4); - add(boost(DEFENCE, perc(.15, 5)), SUPER_DEFENCE1, SUPER_DEFENCE2, SUPER_DEFENCE3, SUPER_DEFENCE4); - add(boost(MAGIC, 3), MAGIC_ESSENCE1, MAGIC_ESSENCE2, MAGIC_ESSENCE3, MAGIC_ESSENCE4); - add(combo(3, boost(ATTACK, perc(.15, 5)), boost(STRENGTH, perc(.15, 5)), boost(DEFENCE, perc(.15, 5))), SUPER_COMBAT_POTION1, SUPER_COMBAT_POTION2, SUPER_COMBAT_POTION3, SUPER_COMBAT_POTION4); - add(combo(3, boost(ATTACK, perc(.20, 2)), boost(STRENGTH, perc(.12, 2)), heal(PRAYER, perc(.10, 0)), new BoostedStatBoost(DEFENCE, false, perc(.10, -2)), new BoostedStatBoost(HITPOINTS, false, perc(-.12, 0))), ZAMORAK_BREW1, ZAMORAK_BREW2, ZAMORAK_BREW3, ZAMORAK_BREW4); + add(superAttackPot, SUPER_ATTACK1, SUPER_ATTACK2, SUPER_ATTACK3, SUPER_ATTACK4); + add(superStrengthPot, SUPER_STRENGTH1, SUPER_STRENGTH2, SUPER_STRENGTH3, SUPER_STRENGTH4); + add(superDefencePot, SUPER_DEFENCE1, SUPER_DEFENCE2, SUPER_DEFENCE3, SUPER_DEFENCE4); + add(magicEssence, MAGIC_ESSENCE1, MAGIC_ESSENCE2, MAGIC_ESSENCE3, MAGIC_ESSENCE4); + add(combo(superAttackPot, superStrengthPot, superDefencePot), SUPER_COMBAT_POTION1, SUPER_COMBAT_POTION2, SUPER_COMBAT_POTION3, SUPER_COMBAT_POTION4); + add(zamorakBrew, ZAMORAK_BREW1, ZAMORAK_BREW2, ZAMORAK_BREW3, ZAMORAK_BREW4); add(new SaradominBrew(0.15, 0.2, 0.1, 2, 2), SARADOMIN_BREW1, SARADOMIN_BREW2, SARADOMIN_BREW3, SARADOMIN_BREW4, SARADOMIN_BREW4_23575, SARADOMIN_BREW3_23577, SARADOMIN_BREW2_23579, SARADOMIN_BREW1_23581 /* LMS */); - add(boost(RANGED, perc(.15, 5)), SUPER_RANGING_1, SUPER_RANGING_2, SUPER_RANGING_3, SUPER_RANGING_4); - add(boost(MAGIC, perc(.15, 5)), SUPER_MAGIC_POTION_1, SUPER_MAGIC_POTION_2, SUPER_MAGIC_POTION_3, SUPER_MAGIC_POTION_4); - add(combo(2, boost(RANGED, perc(0.1, 4)), boost(DEFENCE, perc(0.15, 5))), BASTION_POTION1, BASTION_POTION2, BASTION_POTION3, BASTION_POTION4); - add(combo(2, boost(MAGIC, 4), boost(DEFENCE, perc(0.15, 5))), BATTLEMAGE_POTION1, BATTLEMAGE_POTION2, BATTLEMAGE_POTION3, BATTLEMAGE_POTION4); - add(combo(boost(MAGIC, 4), heal(HITPOINTS, -10)), DIVINE_MAGIC_POTION1, DIVINE_MAGIC_POTION2, DIVINE_MAGIC_POTION3, DIVINE_MAGIC_POTION4); - add(combo(boost(RANGED, perc(.10, 4)), heal(HITPOINTS, -10)), DIVINE_RANGING_POTION1, DIVINE_RANGING_POTION2, DIVINE_RANGING_POTION3, DIVINE_RANGING_POTION4); - add(combo(boost(ATTACK, perc(.15, 5)), heal(HITPOINTS, -10)), DIVINE_SUPER_ATTACK_POTION1, DIVINE_SUPER_ATTACK_POTION2, DIVINE_SUPER_ATTACK_POTION3, DIVINE_SUPER_ATTACK_POTION4); - add(combo(boost(STRENGTH, perc(.15, 5)), heal(HITPOINTS, -10)), DIVINE_SUPER_STRENGTH_POTION1, DIVINE_SUPER_STRENGTH_POTION2, DIVINE_SUPER_STRENGTH_POTION3, DIVINE_SUPER_STRENGTH_POTION4); - add(combo(boost(DEFENCE, perc(.15, 5)), heal(HITPOINTS, -10)), DIVINE_SUPER_DEFENCE_POTION1, DIVINE_SUPER_DEFENCE_POTION2, DIVINE_SUPER_DEFENCE_POTION3, DIVINE_SUPER_DEFENCE_POTION4); - add(combo(3, boost(ATTACK, perc(.15, 5)), boost(STRENGTH, perc(.15, 5)), boost(DEFENCE, perc(.15, 5)), heal(HITPOINTS, -10)), DIVINE_SUPER_COMBAT_POTION1, DIVINE_SUPER_COMBAT_POTION2, DIVINE_SUPER_COMBAT_POTION3, DIVINE_SUPER_COMBAT_POTION4); - add(combo(2, boost(RANGED, perc(0.1, 4)), boost(DEFENCE, perc(0.15, 5)), heal(HITPOINTS, -10)), DIVINE_BASTION_POTION1, DIVINE_BASTION_POTION2, DIVINE_BASTION_POTION3, DIVINE_BASTION_POTION4); - add(combo(2, boost(MAGIC, 4), boost(DEFENCE, perc(0.15, 5)), heal(HITPOINTS, -10)), DIVINE_BATTLEMAGE_POTION1, DIVINE_BATTLEMAGE_POTION2, DIVINE_BATTLEMAGE_POTION3, DIVINE_BATTLEMAGE_POTION4); - add(combo(5, boost(ATTACK, perc(0.15, 5)), boost(STRENGTH, perc(0.15, 5)), boost(DEFENCE, perc(0.15, 5)), boost(RANGED, perc(0.1, 4)), boost(MAGIC, perc(0.1, 1))), + add(superRangingPot, SUPER_RANGING_1, SUPER_RANGING_2, SUPER_RANGING_3, SUPER_RANGING_4); + add(superMagicPot, SUPER_MAGIC_POTION_1, SUPER_MAGIC_POTION_2, SUPER_MAGIC_POTION_3, SUPER_MAGIC_POTION_4); + add(combo(rangingPot, superDefencePot), BASTION_POTION1, BASTION_POTION2, BASTION_POTION3, BASTION_POTION4); + add(combo(magicPot, superDefencePot), BATTLEMAGE_POTION1, BATTLEMAGE_POTION2, BATTLEMAGE_POTION3, BATTLEMAGE_POTION4); + add(combo(magicPot, divinePot), DIVINE_MAGIC_POTION1, DIVINE_MAGIC_POTION2, DIVINE_MAGIC_POTION3, DIVINE_MAGIC_POTION4); + add(combo(rangingPot, divinePot), DIVINE_RANGING_POTION1, DIVINE_RANGING_POTION2, DIVINE_RANGING_POTION3, DIVINE_RANGING_POTION4); + add(combo(superAttackPot, divinePot), DIVINE_SUPER_ATTACK_POTION1, DIVINE_SUPER_ATTACK_POTION2, DIVINE_SUPER_ATTACK_POTION3, DIVINE_SUPER_ATTACK_POTION4); + add(combo(superStrengthPot, divinePot), DIVINE_SUPER_STRENGTH_POTION1, DIVINE_SUPER_STRENGTH_POTION2, DIVINE_SUPER_STRENGTH_POTION3, DIVINE_SUPER_STRENGTH_POTION4); + add(combo(superDefencePot, divinePot), DIVINE_SUPER_DEFENCE_POTION1, DIVINE_SUPER_DEFENCE_POTION2, DIVINE_SUPER_DEFENCE_POTION3, DIVINE_SUPER_DEFENCE_POTION4); + add(combo(superAttackPot, superStrengthPot, superDefencePot, divinePot), DIVINE_SUPER_COMBAT_POTION1, DIVINE_SUPER_COMBAT_POTION2, DIVINE_SUPER_COMBAT_POTION3, DIVINE_SUPER_COMBAT_POTION4); + add(combo(rangingPot, superDefencePot, divinePot), DIVINE_BASTION_POTION1, DIVINE_BASTION_POTION2, DIVINE_BASTION_POTION3, DIVINE_BASTION_POTION4); + add(combo(magicPot, superDefencePot, divinePot), DIVINE_BATTLEMAGE_POTION1, DIVINE_BATTLEMAGE_POTION2, DIVINE_BATTLEMAGE_POTION3, DIVINE_BATTLEMAGE_POTION4); + add(combo(superAttackPot, superStrengthPot, superDefencePot, rangingPot, imbuedHeart), CASTLEWARS_BREW4, CASTLEWARS_BREW3, CASTLEWARS_BREW2, CASTLEWARS_BREW1); - add(combo(2, boost(ATTACK, perc(0.15, 5)), boost(STRENGTH, perc(0.15, 5))), + add(combo(superAttackPot, superStrengthPot), SUPER_COMBAT_POTION4_23543, SUPER_COMBAT_POTION3_23545, SUPER_COMBAT_POTION2_23547, SUPER_COMBAT_POTION1_23549 /* LMS */); + add(ancientBrew, ANCIENT_BREW1, ANCIENT_BREW2, ANCIENT_BREW3, ANCIENT_BREW4); + + // Mixed combat potions + add(new MixedPotion(3, attackPot), ATTACK_MIX1, ATTACK_MIX2); + add(new MixedPotion(3, strengthPot), STRENGTH_MIX1, STRENGTH_MIX2); + add(new MixedPotion(6, defencePot), DEFENCE_MIX1, DEFENCE_MIX2); + add(new MixedPotion(6, magicPot), MAGIC_MIX1, MAGIC_MIX2); + add(new MixedPotion(6, rangingPot), RANGING_MIX1, RANGING_MIX2); + add(new MixedPotion(6, combatPot), COMBAT_MIX1, COMBAT_MIX2); + add(new MixedPotion(6, superAttackPot), SUPERATTACK_MIX1, SUPERATTACK_MIX2); + add(new MixedPotion(6, superStrengthPot), SUPER_STR_MIX1, SUPER_STR_MIX2); + add(new MixedPotion(6, superDefencePot), SUPER_DEF_MIX1, SUPER_DEF_MIX2); + add(new MixedPotion(6, magicEssence), MAGIC_ESSENCE_MIX1, MAGIC_ESSENCE_MIX2); + add(new MixedPotion(6, zamorakBrew), ZAMORAK_MIX1, ZAMORAK_MIX2); + add(new MixedPotion(6, ancientBrew), ANCIENT_MIX1, ANCIENT_MIX2); // Regular overload (NMZ) - add(combo(5, boost(ATTACK, perc(.15, 5)), boost(STRENGTH, perc(.15, 5)), boost(DEFENCE, perc(.15, 5)), boost(RANGED, perc(.15, 5)), boost(MAGIC, perc(.15, 5)), heal(HITPOINTS, -50)), OVERLOAD_1, OVERLOAD_2, OVERLOAD_3, OVERLOAD_4); + add(combo(superAttackPot, superStrengthPot, superDefencePot, superRangingPot, superMagicPot, heal(HITPOINTS, -50)), OVERLOAD_1, OVERLOAD_2, OVERLOAD_3, OVERLOAD_4); // Bandages (Castle Wars) add(new CastleWarsBandage(), BANDAGES); // Bandages (Theatre of Blood entry mode) - add(combo(8, food(20), heal(PRAYER, perc(0.25, 5)), heal(RUN_ENERGY, 20), boost(ATTACK, perc(0.15, 4)), boost(STRENGTH, perc(0.15, 4)), boost(DEFENCE, perc(0.15, 4)), boost(RANGED, perc(0.1, 4)), boost(MAGIC, 4)), BANDAGES_25730); + add(combo(food(20), heal(PRAYER, perc(0.25, 5)), heal(RUN_ENERGY, 20), boost(ATTACK, perc(0.15, 4)), boost(STRENGTH, perc(0.15, 4)), boost(DEFENCE, perc(0.15, 4)), rangingPot, magicPot), BANDAGES_25730); // Recovery potions - add(combo(5, heal(ATTACK, perc(.30, 10)), heal(STRENGTH, perc(.30, 10)), heal(DEFENCE, perc(.30, 10)), heal(RANGED, perc(.30, 10)), heal(MAGIC, perc(.30, 10))), RESTORE_POTION1, RESTORE_POTION2, RESTORE_POTION3, RESTORE_POTION4); - add(heal(RUN_ENERGY, 10), ENERGY_POTION1, ENERGY_POTION2, ENERGY_POTION3, ENERGY_POTION4); - add(new PrayerPotion(7), PRAYER_POTION1, PRAYER_POTION2, PRAYER_POTION3, PRAYER_POTION4); - add(heal(RUN_ENERGY, 20), SUPER_ENERGY1, SUPER_ENERGY2, SUPER_ENERGY3, SUPER_ENERGY4); - add(new SuperRestore(.25, 8), SUPER_RESTORE1, SUPER_RESTORE2, SUPER_RESTORE3, SUPER_RESTORE4, + final Effect restorePot = combo(heal(ATTACK, perc(.30, 10)), heal(STRENGTH, perc(.30, 10)), heal(DEFENCE, perc(.30, 10)), heal(RANGED, perc(.30, 10)), heal(MAGIC, perc(.30, 10))); + final Effect energyPot = heal(RUN_ENERGY, 10); + final Effect prayerPot = new PrayerPotion(7); + final Effect superEnergyPot = heal(RUN_ENERGY, 20); + final Effect superRestorePot = new SuperRestore(.25, 8); + final Effect staminaPot = new StaminaPotion(); + add(restorePot, RESTORE_POTION1, RESTORE_POTION2, RESTORE_POTION3, RESTORE_POTION4); + add(energyPot, ENERGY_POTION1, ENERGY_POTION2, ENERGY_POTION3, ENERGY_POTION4); + add(prayerPot, PRAYER_POTION1, PRAYER_POTION2, PRAYER_POTION3, PRAYER_POTION4); + add(superEnergyPot, SUPER_ENERGY1, SUPER_ENERGY2, SUPER_ENERGY3, SUPER_ENERGY4); + add(superRestorePot, SUPER_RESTORE1, SUPER_RESTORE2, SUPER_RESTORE3, SUPER_RESTORE4, BLIGHTED_SUPER_RESTORE1, BLIGHTED_SUPER_RESTORE2, BLIGHTED_SUPER_RESTORE3, BLIGHTED_SUPER_RESTORE4, SUPER_RESTORE4_23567, SUPER_RESTORE3_23569, SUPER_RESTORE2_23571, SUPER_RESTORE1_23573 /* LMS */); add(new SuperRestore(.30, 4), SANFEW_SERUM1, SANFEW_SERUM2, SANFEW_SERUM3, SANFEW_SERUM4, SANFEW_SERUM4_23559, SANFEW_SERUM3_23561, SANFEW_SERUM2_23563, SANFEW_SERUM1_23565 /* LMS */); - add(new StaminaPotion(), STAMINA_POTION1, STAMINA_POTION2, STAMINA_POTION3, STAMINA_POTION4); + add(staminaPot, STAMINA_POTION1, STAMINA_POTION2, STAMINA_POTION3, STAMINA_POTION4); + + // Mixed recovery potions + add(new MixedPotion(3, restorePot), RESTORE_MIX1, RESTORE_MIX2); + add(new MixedPotion(6, energyPot), ENERGY_MIX1, ENERGY_MIX2); + add(new MixedPotion(6, prayerPot), PRAYER_MIX1, PRAYER_MIX2); + add(new MixedPotion(6, superEnergyPot), SUPER_ENERGY_MIX1, SUPER_ENERGY_MIX2); + add(new MixedPotion(6, superRestorePot), SUPER_RESTORE_MIX1, SUPER_RESTORE_MIX2); + add(new MixedPotion(6, staminaPot), STAMINA_MIX1, STAMINA_MIX2); // Raids potions (+) - add(combo(5, boost(ATTACK, perc(.16, 6)), boost(STRENGTH, perc(.16, 6)), boost(DEFENCE, perc(.16, 6)), boost(RANGED, perc(.16, 6)), boost(MAGIC, perc(.16, 6)), heal(HITPOINTS, -50)), OVERLOAD_1_20993, OVERLOAD_2_20994, OVERLOAD_3_20995, OVERLOAD_4_20996); - add(combo(3, boost(ATTACK, perc(.16, 6)), boost(STRENGTH, perc(.16, 6)), boost(DEFENCE, perc(.16, 6))), ELDER_1_20921, ELDER_2_20922, ELDER_3_20923, ELDER_4_20924); - add(combo(2, boost(RANGED, perc(.16, 6)), boost(DEFENCE, perc(.16, 6))), TWISTED_1_20933, TWISTED_2_20934, TWISTED_3_20935, TWISTED_4_20936); - add(combo(2, boost(MAGIC, perc(.16, 6)), boost(DEFENCE, perc(.16, 6))), KODAI_1_20945, KODAI_2_20946, KODAI_3_20947, KODAI_4_20948); + final DeltaPercentage coxPlusPotionBoost = perc(.16, 6); + add(combo(boost(ATTACK, coxPlusPotionBoost), boost(STRENGTH, coxPlusPotionBoost), boost(DEFENCE, coxPlusPotionBoost), boost(RANGED, coxPlusPotionBoost), boost(MAGIC, coxPlusPotionBoost), heal(HITPOINTS, -50)), OVERLOAD_1_20993, OVERLOAD_2_20994, OVERLOAD_3_20995, OVERLOAD_4_20996); + add(combo(boost(ATTACK, coxPlusPotionBoost), boost(STRENGTH, coxPlusPotionBoost), boost(DEFENCE, coxPlusPotionBoost)), ELDER_1_20921, ELDER_2_20922, ELDER_3_20923, ELDER_4_20924); + add(combo(boost(RANGED, coxPlusPotionBoost), boost(DEFENCE, coxPlusPotionBoost)), TWISTED_1_20933, TWISTED_2_20934, TWISTED_3_20935, TWISTED_4_20936); + add(combo(boost(MAGIC, coxPlusPotionBoost), boost(DEFENCE, coxPlusPotionBoost)), KODAI_1_20945, KODAI_2_20946, KODAI_3_20947, KODAI_4_20948); add(new SuperRestore(.30, 11), REVITALISATION_1_20957, REVITALISATION_2_20958, REVITALISATION_3_20959, REVITALISATION_4_20960); add(new SaradominBrew(0.15, 0.2, 0.1, 5, 4), XERICS_AID_1_20981, XERICS_AID_2_20982, XERICS_AID_3_20983, XERICS_AID_4_20984); // Raids potions - add(combo(5, boost(ATTACK, perc(.13, 5)), boost(STRENGTH, perc(.13, 5)), boost(DEFENCE, perc(.13, 5)), boost(RANGED, perc(.13, 5)), boost(MAGIC, perc(.13, 5)), heal(HITPOINTS, -50)), OVERLOAD_1_20989, OVERLOAD_2_20990, OVERLOAD_3_20991, OVERLOAD_4_20992); - add(combo(3, boost(ATTACK, perc(.13, 5)), boost(STRENGTH, perc(.13, 5)), boost(DEFENCE, perc(.13, 5))), ELDER_POTION_1, ELDER_POTION_2, ELDER_POTION_3, ELDER_POTION_4); - add(combo(2, boost(RANGED, perc(.13, 5)), boost(DEFENCE, perc(.13, 5))), TWISTED_POTION_1, TWISTED_POTION_2, TWISTED_POTION_3, TWISTED_POTION_4); - add(combo(2, boost(MAGIC, perc(.13, 5)), boost(DEFENCE, perc(.13, 5))), KODAI_POTION_1, KODAI_POTION_2, KODAI_POTION_3, KODAI_POTION_4); + final DeltaPercentage coxPotionBoost = perc(.13, 5); + add(combo(boost(ATTACK, coxPotionBoost), boost(STRENGTH, coxPotionBoost), boost(DEFENCE, coxPotionBoost), boost(RANGED, coxPotionBoost), boost(MAGIC, coxPotionBoost), heal(HITPOINTS, -50)), OVERLOAD_1_20989, OVERLOAD_2_20990, OVERLOAD_3_20991, OVERLOAD_4_20992); + add(combo(boost(ATTACK, coxPotionBoost), boost(STRENGTH, coxPotionBoost), boost(DEFENCE, coxPotionBoost)), ELDER_POTION_1, ELDER_POTION_2, ELDER_POTION_3, ELDER_POTION_4); + add(combo(boost(RANGED, coxPotionBoost), boost(DEFENCE, coxPotionBoost)), TWISTED_POTION_1, TWISTED_POTION_2, TWISTED_POTION_3, TWISTED_POTION_4); + add(combo(boost(MAGIC, coxPotionBoost), boost(DEFENCE, coxPotionBoost)), KODAI_POTION_1, KODAI_POTION_2, KODAI_POTION_3, KODAI_POTION_4); // Raids potions (-) - add(combo(5, boost(ATTACK, perc(.10, 4)), boost(STRENGTH, perc(.10, 4)), boost(DEFENCE, perc(.10, 4)), boost(RANGED, perc(.10, 4)), boost(MAGIC, perc(.10, 4)), heal(HITPOINTS, -50)), OVERLOAD_1_20985, OVERLOAD_2_20986, OVERLOAD_3_20987, OVERLOAD_4_20988); - add(combo(3, boost(ATTACK, perc(.10, 4)), boost(STRENGTH, perc(.10, 4)), boost(DEFENCE, perc(.10, 4))), ELDER_1, ELDER_2, ELDER_3, ELDER_4); - add(combo(3, boost(RANGED, perc(.10, 4)), boost(DEFENCE, perc(.10, 4))), TWISTED_1, TWISTED_2, TWISTED_3, TWISTED_4); - add(combo(3, boost(MAGIC, perc(.10, 4)), boost(DEFENCE, perc(.10, 4))), KODAI_1, KODAI_2, KODAI_3, KODAI_4); + final DeltaPercentage coxMinusPotionBoost = perc(.10, 4); + add(combo(boost(ATTACK, coxMinusPotionBoost), boost(STRENGTH, coxMinusPotionBoost), boost(DEFENCE, coxMinusPotionBoost), boost(RANGED, coxMinusPotionBoost), boost(MAGIC, coxMinusPotionBoost), heal(HITPOINTS, -50)), OVERLOAD_1_20985, OVERLOAD_2_20986, OVERLOAD_3_20987, OVERLOAD_4_20988); + add(combo(boost(ATTACK, coxMinusPotionBoost), boost(STRENGTH, coxMinusPotionBoost), boost(DEFENCE, coxMinusPotionBoost)), ELDER_1, ELDER_2, ELDER_3, ELDER_4); + add(combo(boost(RANGED, coxMinusPotionBoost), boost(DEFENCE, coxMinusPotionBoost)), TWISTED_1, TWISTED_2, TWISTED_3, TWISTED_4); + add(combo(boost(MAGIC, coxMinusPotionBoost), boost(DEFENCE, coxMinusPotionBoost)), KODAI_1, KODAI_2, KODAI_3, KODAI_4); // Skill potions - add(boost(AGILITY, 3), AGILITY_POTION1, AGILITY_POTION2, AGILITY_POTION3, AGILITY_POTION4); - add(boost(FISHING, 3), FISHING_POTION1, FISHING_POTION2, FISHING_POTION3, FISHING_POTION4); - add(boost(HUNTER, 3), HUNTER_POTION1, HUNTER_POTION2, HUNTER_POTION3, HUNTER_POTION4); - add(combo(2, boost(HITPOINTS, 5), heal(RUN_ENERGY, 5)), GUTHIX_REST1, GUTHIX_REST2, GUTHIX_REST3, GUTHIX_REST4); + final Effect agilityPot = boost(AGILITY, 3); + final Effect fishingPot = boost(FISHING, 3); + final Effect hunterPot = boost(HUNTER, 3); + add(agilityPot, AGILITY_POTION1, AGILITY_POTION2, AGILITY_POTION3, AGILITY_POTION4); + add(fishingPot, FISHING_POTION1, FISHING_POTION2, FISHING_POTION3, FISHING_POTION4); + add(hunterPot, HUNTER_POTION1, HUNTER_POTION2, HUNTER_POTION3, HUNTER_POTION4); + add(combo(boost(HITPOINTS, 5), heal(RUN_ENERGY, 5)), GUTHIX_REST1, GUTHIX_REST2, GUTHIX_REST3, GUTHIX_REST4); + + // Mixed skill potions + add(new MixedPotion(6, agilityPot), AGILITY_MIX1, AGILITY_MIX2); + add(new MixedPotion(6, fishingPot), FISHING_MIX1, FISHING_MIX2); + add(new MixedPotion(6, hunterPot), HUNTING_MIX1, HUNTING_MIX2); // Misc/run energy add(combo(food(3), range(heal(RUN_ENERGY, 5), heal(RUN_ENERGY, 10))), WHITE_TREE_FRUIT); @@ -251,19 +312,19 @@ public class ItemStatChanges add(combo(food(12), heal(RUN_ENERGY, 50)), GOUT_TUBER); // Pies - add(combo(2, heal(HITPOINTS, 6), boost(FARMING, 3)), GARDEN_PIE, HALF_A_GARDEN_PIE); - add(combo(2, heal(HITPOINTS, 6), boost(FISHING, 3)), FISH_PIE, HALF_A_FISH_PIE); - add(combo(2, heal(HITPOINTS, 7), boost(HERBLORE, 4)), BOTANICAL_PIE, HALF_A_BOTANICAL_PIE); - add(combo(2, heal(HITPOINTS, 8), boost(CRAFTING, 4)), MUSHROOM_PIE, HALF_A_MUSHROOM_PIE); - add(combo(2, heal(HITPOINTS, 8), boost(FISHING, 5)), ADMIRAL_PIE, HALF_AN_ADMIRAL_PIE); - add(combo(2, heal(HITPOINTS, 11), boost(SLAYER, 5), boost(RANGED, 4)), WILD_PIE, HALF_A_WILD_PIE); - add(combo(2, heal(HITPOINTS, 11), boost(AGILITY, 5), heal(RUN_ENERGY, 10)), SUMMER_PIE, HALF_A_SUMMER_PIE); - add(combo(2, heal(HITPOINTS, 10), boost(FLETCHING, 4)), DRAGONFRUIT_PIE, HALF_A_DRAGONFRUIT_PIE); + add(combo(heal(HITPOINTS, 6), boost(FARMING, 3)), GARDEN_PIE, HALF_A_GARDEN_PIE); + add(combo(heal(HITPOINTS, 6), boost(FISHING, 3)), FISH_PIE, HALF_A_FISH_PIE); + add(combo(heal(HITPOINTS, 7), boost(HERBLORE, 4)), BOTANICAL_PIE, HALF_A_BOTANICAL_PIE); + add(combo(heal(HITPOINTS, 8), boost(CRAFTING, 4)), MUSHROOM_PIE, HALF_A_MUSHROOM_PIE); + add(combo(heal(HITPOINTS, 8), boost(FISHING, 5)), ADMIRAL_PIE, HALF_AN_ADMIRAL_PIE); + add(combo(heal(HITPOINTS, 11), boost(SLAYER, 5), boost(RANGED, 4)), WILD_PIE, HALF_A_WILD_PIE); + add(combo(heal(HITPOINTS, 11), boost(AGILITY, 5), heal(RUN_ENERGY, 10)), SUMMER_PIE, HALF_A_SUMMER_PIE); + add(combo(heal(HITPOINTS, 10), boost(FLETCHING, 4)), DRAGONFRUIT_PIE, HALF_A_DRAGONFRUIT_PIE); // Other add(combo(range(food(1), food(3)), heal(RUN_ENERGY, 10)), PURPLE_SWEETS_10476); add(new SpicyStew(), SPICY_STEW); - add(boost(MAGIC, perc(.10, 1)), IMBUED_HEART); + add(imbuedHeart, IMBUED_HEART); add(combo(boost(ATTACK, 2), boost(STRENGTH, 1), heal(PRAYER, 1), heal(DEFENCE, -1)), JANGERBERRIES); add(new CaveNightshade(), CAVE_NIGHTSHADE); @@ -273,8 +334,8 @@ public class ItemStatChanges add(new GauntletPotion(), EGNIOL_POTION_1, EGNIOL_POTION_2, EGNIOL_POTION_3, EGNIOL_POTION_4); // Soul Wars - add(combo(2, heal(HITPOINTS, perc(.15, 1)), heal(RUN_ENERGY, 100)), BANDAGES_25202); - add(combo(6, boost(ATTACK, perc(.15, 5)), boost(STRENGTH, perc(.15, 5)), boost(DEFENCE, perc(.15, 5)), boost(RANGED, perc(.15, 5)), boost(MAGIC, perc(.15, 5)), heal(PRAYER, perc(.25, 8))), POTION_OF_POWER1, POTION_OF_POWER2, POTION_OF_POWER3, POTION_OF_POWER4); + add(combo(heal(HITPOINTS, perc(.15, 1)), heal(RUN_ENERGY, 100)), BANDAGES_25202); + add(combo(boost(ATTACK, perc(.15, 5)), boost(STRENGTH, perc(.15, 5)), boost(DEFENCE, perc(.15, 5)), boost(RANGED, perc(.15, 5)), boost(MAGIC, perc(.15, 5)), heal(PRAYER, perc(.25, 8))), POTION_OF_POWER1, POTION_OF_POWER2, POTION_OF_POWER3, POTION_OF_POWER4); log.debug("{} items; {} behaviours loaded", effects.size(), new HashSet<>(effects.values()).size()); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/AncientBrew.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/AncientBrew.java new file mode 100644 index 0000000000..08e705dcb0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/AncientBrew.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 Hydrox6 + * 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.itemstats.potions; + +import lombok.NoArgsConstructor; +import net.runelite.api.Client; +import net.runelite.client.plugins.itemstats.BoostedStatBoost; +import static net.runelite.client.plugins.itemstats.Builders.perc; +import net.runelite.client.plugins.itemstats.CappedStatBoost; +import net.runelite.client.plugins.itemstats.Effect; +import net.runelite.client.plugins.itemstats.SimpleStatBoost; +import net.runelite.client.plugins.itemstats.StatChange; +import net.runelite.client.plugins.itemstats.StatsChanges; +import net.runelite.client.plugins.itemstats.stats.Stat; +import static net.runelite.client.plugins.itemstats.stats.Stats.ATTACK; +import static net.runelite.client.plugins.itemstats.stats.Stats.DEFENCE; +import static net.runelite.client.plugins.itemstats.stats.Stats.MAGIC; +import static net.runelite.client.plugins.itemstats.stats.Stats.PRAYER; +import static net.runelite.client.plugins.itemstats.stats.Stats.STRENGTH; +import java.util.Comparator; +import java.util.stream.Stream; + +@NoArgsConstructor +public class AncientBrew implements Effect +{ + private static final Stat[] LOWERED_STATS = { + ATTACK, STRENGTH, DEFENCE + }; + private static final CappedStatBoost PRAYER_BOOST = new CappedStatBoost(PRAYER, perc(.1, 2), perc(.05, 0)); + private static final SimpleStatBoost MAGIC_BOOST = new SimpleStatBoost(MAGIC, true, perc(.05, 2)); + private static final BoostedStatBoost MELEE_DRAIN = new BoostedStatBoost(null, false, perc(.1, -2)); + + @Override + public StatsChanges calculate(Client client) + { + StatsChanges changes = new StatsChanges(0); + changes.setStatChanges(Stream.of( + Stream.of(PRAYER_BOOST.effect(client)), + Stream.of(MAGIC_BOOST.effect(client)), + Stream.of(LOWERED_STATS) + .filter(stat -> 1 < stat.getValue(client)) + .map(stat -> + { + MELEE_DRAIN.setStat(stat); + return MELEE_DRAIN.effect(client); + })) + .reduce(Stream::concat) + .orElseGet(Stream::empty) + .toArray(StatChange[]::new)); + changes.setPositivity(Stream.of(changes.getStatChanges()) + .map(StatChange::getPositivity) + .max(Comparator.naturalOrder()).get()); + return changes; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/MixedPotion.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/MixedPotion.java new file mode 100644 index 0000000000..e56f575315 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/MixedPotion.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2022, Jordan Atwood + * 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.itemstats.potions; + +import java.util.Comparator; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import lombok.RequiredArgsConstructor; +import net.runelite.api.Client; +import static net.runelite.client.plugins.itemstats.Builders.food; +import net.runelite.client.plugins.itemstats.Effect; +import net.runelite.client.plugins.itemstats.Positivity; +import net.runelite.client.plugins.itemstats.StatChange; +import static net.runelite.client.plugins.itemstats.stats.Stats.HITPOINTS; +import net.runelite.client.plugins.itemstats.StatsChanges; +import org.apache.commons.lang3.ArrayUtils; + +@RequiredArgsConstructor +public class MixedPotion implements Effect +{ + private final int heal; + + @Nonnull + private final Effect potion; + + @Override + public StatsChanges calculate(Client client) + { + final StatsChanges changes = new StatsChanges(0); + final StatChange mixedPotionHpBoost = food(heal).effect(client); + final StatsChanges potionChanges = potion.calculate(client); + final int mixedPotionHitpointsHealing = mixedPotionHpBoost.getRelative(); + + if (Stream.of(potionChanges.getStatChanges()).anyMatch(statChange -> statChange.getStat() == HITPOINTS)) + { + changes.setStatChanges(Stream.of(potionChanges.getStatChanges()) + .map(change -> + { + /* + * Mixed potions do not exist ingame for all types of potions. In fact, at the time of writing, the + * Zamorak mix is the only mixed potion which includes base potion effects which affect a player's + * Hitpoints. Working from what we know of how these behave, this code assumes that mixed potions + * including Hitpoints changes will only include negative HP effects as the + * absolute/relative/theoretical end values cannot be determined otherwise. For this reason, potions + * with positive HP effects will not have their stat changes affected here. + */ + if (change.getStat() != HITPOINTS || mixedPotionHitpointsHealing == 0 || change.getTheoretical() >= 0) + { + return change; + } + + /* + * Mixed potions apply two hitpoints changes, both based on the current hitpoints value. Because of + * this, the two effects are calculated independently of each other, both against the same starting + * hitpoints value and later combined. These effects are: + * 1. A food effect of `heal` amount + * 2. Deduct hitpoints equal to the potion's boost amount + */ + + final int max = HITPOINTS.getMaximum(client); + final int absolute = change.getAbsolute(); + final int relative = change.getRelative(); + + if (absolute + mixedPotionHitpointsHealing > max) + { + change.setPositivity(Positivity.BETTER_CAPPED); + } + else if (relative + mixedPotionHitpointsHealing > 0) + { + change.setPositivity(Positivity.BETTER_UNCAPPED); + } + else if (relative + mixedPotionHitpointsHealing == 0) + { + change.setPositivity(Positivity.NO_CHANGE); + } + else + { + change.setPositivity(Positivity.WORSE); + } + + change.setAbsolute(Math.min(max, absolute + mixedPotionHitpointsHealing)); + change.setRelative(change.getRelative() + mixedPotionHitpointsHealing); + change.setTheoretical(change.getTheoretical() + mixedPotionHitpointsHealing); + + return change; + }) + .toArray(StatChange[]::new)); + } + else + { + changes.setStatChanges(ArrayUtils.addAll(new StatChange[] { mixedPotionHpBoost }, potionChanges.getStatChanges())); + } + + changes.setPositivity(Stream.of(changes.getStatChanges()) + .map(StatChange::getPositivity) + .max(Comparator.naturalOrder()).get()); + + return changes; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/SaradominBrew.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/SaradominBrew.java index 2eaea550f4..5baa696909 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/SaradominBrew.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/potions/SaradominBrew.java @@ -57,9 +57,8 @@ public class SaradominBrew implements Effect SimpleStatBoost hitpoints = new SimpleStatBoost(HITPOINTS, true, perc(percH, deltaB)); SimpleStatBoost defence = new SimpleStatBoost(DEFENCE, true, perc(percD, deltaB)); BoostedStatBoost calc = new BoostedStatBoost(null, false, perc(percSD, -deltaR)); - changes.setStatChanges(Stream.concat( + changes.setStatChanges(Stream.of( Stream.of(hitpoints.effect(client)), - Stream.concat( Stream.of(defence.effect(client)), Stream.of(saradominBrewStats) .filter(stat -> 1 < stat.getValue(client)) @@ -67,12 +66,13 @@ public class SaradominBrew implements Effect { calc.setStat(stat); return calc.effect(client); - }) - ) - ).toArray(StatChange[]::new)); + })) + .reduce(Stream::concat) + .orElseGet(Stream::empty) + .toArray(StatChange[]::new)); changes.setPositivity(Stream.of(changes.getStatChanges()) - .map(sc -> sc.getPositivity()) + .map(StatChange::getPositivity) .max(Comparator.naturalOrder()).get()); return changes; } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyConfig.java index 4d484f4cdc..fd8553ad5a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyConfig.java @@ -56,17 +56,6 @@ public interface PartyConfig extends Config return true; } - @ConfigItem( - keyName = "messages", - name = "Join messages", - description = "Enables members join/leave game messages", - position = 3 - ) - default boolean messages() - { - return true; - } - @ConfigItem( keyName = "recolorNames", name = "Recolor names", @@ -78,28 +67,6 @@ public interface PartyConfig extends Config return true; } - @ConfigItem( - keyName = "autoOverlay", - name = "Auto overlay", - description = "Automatically add an overlay with player data when a member joins", - position = 5 - ) - default boolean autoOverlay() - { - return true; - } - - @ConfigItem( - keyName = "includeSelf", - name = "Include yourself", - description = "Shows yourself in the panel as part of the party", - position = 6 - ) - default boolean includeSelf() - { - return false; - } - @ConfigItem( keyName = "previousPartyId", name = "", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyMemberBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyMemberBox.java index 7b3839b406..832989f5a5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyMemberBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyMemberBox.java @@ -32,9 +32,7 @@ import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JLabel; -import javax.swing.JMenuItem; import javax.swing.JPanel; -import javax.swing.JPopupMenu; import javax.swing.SwingConstants; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; @@ -47,6 +45,7 @@ import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.MouseDragEventForwarder; import net.runelite.client.ui.components.ProgressBar; import net.runelite.client.util.ImageUtil; +import net.runelite.client.ws.PartyMember; class PartyMemberBox extends JPanel { @@ -61,9 +60,7 @@ class PartyMemberBox extends JPanel private final ProgressBar hpBar = new ProgressBar(); private final ProgressBar prayerBar = new ProgressBar(); - private final JLabel topName = new JLabel(); - private final JLabel bottomName = new JLabel(); - + private final JLabel name = new JLabel(); private final JLabel avatar = new JLabel(); private final PartyConfig config; @@ -84,15 +81,6 @@ class PartyMemberBox extends JPanel container.setBackground(ColorScheme.DARKER_GRAY_COLOR); container.setBorder(new EmptyBorder(5, 5, 5, 5)); - // Create Toggle overlay - final JMenuItem overlay = new JMenuItem("Toggle overlay"); - overlay.addActionListener(e -> memberPartyData.setShowOverlay(!memberPartyData.isShowOverlay())); - - // Create popup menu - final JPopupMenu popupMenu = new JPopupMenu(); - popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); - popupMenu.add(overlay); - // create a line border with the specified color and width Border border = BorderFactory.createLineBorder(Color.gray, 1); avatar.setBorder(border); @@ -113,14 +101,10 @@ class PartyMemberBox extends JPanel namesPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); namesPanel.setBorder(new EmptyBorder(2, 5, 2, 5)); - topName.setFont(FontManager.getRunescapeSmallFont()); - bottomName.setFont(FontManager.getRunescapeSmallFont()); + name.setFont(FontManager.getRunescapeSmallFont()); + name.putClientProperty("html.disable", Boolean.TRUE); - topName.putClientProperty("html.disable", Boolean.TRUE); - bottomName.putClientProperty("html.disable", Boolean.TRUE); - - namesPanel.add(topName); // top - namesPanel.add(bottomName); // bottom + namesPanel.add(name); headerPanel.add(avatar, BorderLayout.WEST); headerPanel.add(namesPanel, BorderLayout.CENTER); @@ -141,8 +125,6 @@ class PartyMemberBox extends JPanel container.add(headerPanel, BorderLayout.NORTH); container.add(progressWrapper, BorderLayout.SOUTH); - container.setComponentPopupMenu(popupMenu); - // forward mouse drag events to parent panel for drag and drop reordering MouseDragEventForwarder mouseDragEventForwarder = new MouseDragEventForwarder(panel); container.addMouseListener(mouseDragEventForwarder); @@ -155,10 +137,12 @@ class PartyMemberBox extends JPanel void update() { + final PartyMember member = memberPartyData.getMember(); + // Avatar - if (!avatarSet && memberPartyData.getMember().getAvatar() != null) + if (!avatarSet && member.getAvatar() != null) { - ImageIcon icon = new ImageIcon(ImageUtil.resizeImage(memberPartyData.getMember().getAvatar(), 32, 32)); + ImageIcon icon = new ImageIcon(ImageUtil.resizeImage(member.getAvatar(), 32, 32)); icon.getImage().flush(); avatar.setIcon(icon); @@ -174,15 +158,12 @@ class PartyMemberBox extends JPanel prayerBar.setMaximumValue(memberPartyData.getMaxPrayer()); prayerBar.setCenterLabel(progressBarLabel(memberPartyData.getPrayer(), memberPartyData.getMaxPrayer())); - // Update name labels + // Update name label Color playerColor = config.recolorNames() ? memberPartyData.getColor() : Color.WHITE; - boolean isLoggedIn = !memberPartyData.getCharacterName().isEmpty(); + boolean isLoggedIn = member.isLoggedIn(); - topName.setForeground(playerColor); - topName.setText(memberPartyData.getMember().getName()); - - bottomName.setForeground(isLoggedIn ? playerColor : Color.GRAY); - bottomName.setText(isLoggedIn ? memberPartyData.getCharacterName() : "Logged out"); + name.setForeground(isLoggedIn ? playerColor : Color.GRAY); + name.setText(member.getDisplayName()); } private static String progressBarLabel(int current, int max) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPanel.java index 52d4799e95..2affaf1214 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPanel.java @@ -41,6 +41,7 @@ import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; +import net.runelite.client.callback.ClientThread; import net.runelite.client.plugins.party.data.PartyData; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; @@ -51,13 +52,12 @@ import net.runelite.client.ws.PartyService; class PartyPanel extends PluginPanel { private static final String BTN_CREATE_TEXT = "Create party"; - private static final String BTN_LEAVE_TEXT = "Leave party"; + private static final String BTN_LEAVE_TEXT = "Leave"; private final PartyPlugin plugin; private final PartyService party; private final PartyConfig config; - private final Map requestBoxes = new HashMap<>(); private final Map memberBoxes = new HashMap<>(); private final JButton startButton = new JButton(); @@ -68,10 +68,9 @@ class PartyPanel extends PluginPanel private final PluginErrorPanel noPartyPanel = new PluginErrorPanel(); private final PluginErrorPanel partyEmptyPanel = new PluginErrorPanel(); private final JComponent memberBoxPanel = new DragAndDropReorderPane(); - private final JComponent requestBoxPanel = new DragAndDropReorderPane(); @Inject - PartyPanel(final PartyPlugin plugin, final PartyConfig config, final PartyService party) + PartyPanel(final ClientThread clientThread, final PartyPlugin plugin, final PartyConfig config, final PartyService party) { this.plugin = plugin; this.party = party; @@ -113,7 +112,6 @@ class PartyPanel extends PluginPanel topPanel.add(rejoinPartyButton, c); layoutPanel.add(topPanel); - layoutPanel.add(requestBoxPanel); layoutPanel.add(memberBoxPanel); startButton.setText(party.isInParty() ? BTN_LEAVE_TEXT : BTN_CREATE_TEXT); @@ -125,7 +123,7 @@ class PartyPanel extends PluginPanel rejoinPartyButton.setText("Join previous party"); rejoinPartyButton.setFocusable(false); - copyPartyIdButton.setText("Copy party id"); + copyPartyIdButton.setText("Copy passphrase"); copyPartyIdButton.setFocusable(false); startButton.addActionListener(e -> @@ -146,7 +144,7 @@ class PartyPanel extends PluginPanel else { // Create party - party.changeParty(party.getLocalPartyId()); + clientThread.invokeLater(() -> party.changeParty(party.generatePasspharse())); } }); @@ -156,8 +154,8 @@ class PartyPanel extends PluginPanel { String s = (String) JOptionPane.showInputDialog( joinPartyButton, - "Please enter the party id:", - "Party Id", + "Please enter the party passphrase:", + "Party Passphrase", JOptionPane.PLAIN_MESSAGE, null, null, @@ -168,15 +166,7 @@ class PartyPanel extends PluginPanel return; } - try - { - party.changeParty(UUID.fromString(s)); - } - catch (IllegalArgumentException ex) - { - JOptionPane.showMessageDialog(joinPartyButton, "You have entered an invalid party id.", "Invalid Party Id", - JOptionPane.ERROR_MESSAGE); - } + party.changeParty(s); } }); @@ -184,17 +174,7 @@ class PartyPanel extends PluginPanel { if (!party.isInParty()) { - try - { - party.changeParty(UUID.fromString(config.previousPartyId())); - } - catch (IllegalArgumentException ex) - { - JOptionPane.showMessageDialog(rejoinPartyButton, - "Failed to join your previous party, create a new party or join a new one.", - "Failed to Join Party", - JOptionPane.ERROR_MESSAGE); - } + party.changeParty(config.previousPartyId()); } }); @@ -203,12 +183,11 @@ class PartyPanel extends PluginPanel if (party.isInParty()) { Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboard.setContents(new StringSelection(String.valueOf(party.getPartyId())), null); + clipboard.setContents(new StringSelection(party.getPartyPassphrase()), null); } }); noPartyPanel.setContent("Not in a party", "Create a party to begin."); - partyEmptyPanel.setContent("Party created", "You can now invite friends!"); updateParty(); } @@ -229,6 +208,8 @@ class PartyPanel extends PluginPanel } else if (plugin.getPartyDataMap().size() <= 1) { + partyEmptyPanel.setContent("Party created", "You can now invite friends!
" + + "Your party passphrase is: " + party.getPartyPassphrase() + "."); add(partyEmptyPanel); } } @@ -280,30 +261,4 @@ class PartyPanel extends PluginPanel { memberBoxes.forEach((key, value) -> value.update()); } - - void addRequest(String userId, String userName) - { - PartyRequestBox partyRequestBox = new PartyRequestBox(plugin, requestBoxPanel, userId, userName); - requestBoxes.put(userId, partyRequestBox); - requestBoxPanel.add(partyRequestBox); - requestBoxPanel.revalidate(); - } - - void removeAllRequests() - { - requestBoxes.forEach((key, value) -> requestBoxPanel.remove(value)); - requestBoxPanel.revalidate(); - requestBoxes.clear(); - } - - void removeRequest(String userId) - { - final PartyRequestBox requestBox = requestBoxes.remove(userId); - - if (requestBox != null) - { - requestBoxPanel.remove(requestBox); - requestBoxPanel.revalidate(); - } - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java index ae13a4a22f..927f575b9d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyPlugin.java @@ -58,13 +58,9 @@ import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; import net.runelite.api.events.MenuOptionClicked; import net.runelite.client.callback.ClientThread; -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.discord.DiscordService; -import net.runelite.client.discord.events.DiscordJoinRequest; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.OverlayMenuClicked; @@ -114,9 +110,6 @@ public class PartyPlugin extends Plugin @Inject private OverlayManager overlayManager; - @Inject - private PartyStatsOverlay partyStatsOverlay; - @Inject private PartyPingOverlay partyPingOverlay; @@ -138,9 +131,6 @@ public class PartyPlugin extends Plugin @Inject private ClientToolbar clientToolbar; - @Inject - private DiscordService discordService; - @Inject @Named("developerMode") boolean developerMode; @@ -157,7 +147,6 @@ public class PartyPlugin extends Plugin private int lastHp, lastPray; private String lastCharacterName = ""; private WorldPoint lastLocation; - private boolean sendAlert; @Override public void configure(Binder binder) @@ -181,7 +170,6 @@ public class PartyPlugin extends Plugin clientToolbar.addNavigation(navButton); - overlayManager.add(partyStatsOverlay); overlayManager.add(partyPingOverlay); wsClient.registerMessage(SkillUpdate.class); wsClient.registerMessage(TilePing.class); @@ -201,13 +189,11 @@ public class PartyPlugin extends Plugin partyDataMap.clear(); pendingTilePings.clear(); worldMapManager.removeIf(PartyWorldMapPoint.class::isInstance); - overlayManager.remove(partyStatsOverlay); overlayManager.remove(partyPingOverlay); wsClient.unregisterMessage(SkillUpdate.class); wsClient.unregisterMessage(TilePing.class); wsClient.unregisterMessage(LocationUpdate.class); wsClient.unregisterMessage(CharacterNameUpdate.class); - sendAlert = false; lastLocation = null; } @@ -231,21 +217,6 @@ public class PartyPlugin extends Plugin void leaveParty() { party.changeParty(null); - - if (!config.messages()) - { - return; - } - - final String leaveMessage = new ChatMessageBuilder() - .append(ChatColorType.HIGHLIGHT) - .append("You have left the party.") - .build(); - - chatMessageManager.queue(QueuedMessage.builder() - .type(ChatMessageType.FRIENDSCHATNOTIFICATION) - .runeLiteFormattedMessage(leaveMessage) - .build()); } @Subscribe @@ -253,22 +224,6 @@ public class PartyPlugin extends Plugin { if (event.getGroup().equals(PartyConfig.GROUP)) { - final PartyMember localMember = party.getLocalMember(); - - if (localMember != null) - { - if (config.includeSelf()) - { - final PartyData partyData = getPartyData(localMember.getMemberId()); - assert partyData != null; - SwingUtilities.invokeLater(() -> panel.addMember(partyData)); - } - else - { - SwingUtilities.invokeLater(() -> panel.removeMember(localMember.getMemberId())); - } - } - // rebuild the panel in the event the "Recolor names" option changes SwingUtilities.invokeLater(panel::updateAll); } @@ -314,35 +269,12 @@ public class PartyPlugin extends Plugin wsClient.send(tilePing); } - @Subscribe - public void onDiscordJoinRequest(DiscordJoinRequest request) - { - final String requestMessage = new ChatMessageBuilder() - .append(ChatColorType.HIGHLIGHT) - .append("New join request received. Check your Party panel.") - .build(); - - chatMessageManager.queue(QueuedMessage.builder() - .type(ChatMessageType.FRIENDSCHATNOTIFICATION) - .runeLiteFormattedMessage(requestMessage) - .build()); - - String userName = request.getUsername() + "#" + request.getDiscriminator(); - SwingUtilities.invokeLater(() -> panel.addRequest(request.getUserId(), userName)); - } - @Subscribe public void onGameStateChanged(GameStateChanged event) { checkStateChanged(false); } - public void replyToRequest(String userId, int reply) - { - discordService.respondToRequest(userId, reply); - panel.removeRequest(userId); - } - @Subscribe public void onTilePing(TilePing event) { @@ -400,12 +332,6 @@ public class PartyPlugin extends Plugin @Subscribe public void onGameTick(final GameTick event) { - if (sendAlert && client.getGameState() == GameState.LOGGED_IN) - { - sendAlert = false; - sendInstructionMessage(); - } - checkStateChanged(false); } @@ -430,11 +356,22 @@ public class PartyPlugin extends Plugin return; } - String name = event.getCharacterName(); - name = Text.removeTags(Text.toJagexName(name)); + final String name = Text.removeTags(Text.toJagexName(event.getCharacterName())); + final PartyMember member = partyData.getMember(); - partyData.setCharacterName(name); - SwingUtilities.invokeLater(() -> panel.updateMember(partyData.getMember().getMemberId())); + if (!name.isEmpty()) + { + member.setDisplayName(name); + member.setLoggedIn(true); + partyData.setColor(ColorUtil.fromObject(name)); + } + else + { + member.setLoggedIn(false); + partyData.setColor(Color.WHITE); + } + + SwingUtilities.invokeLater(() -> panel.updateMember(member.getMemberId())); } @Subscribe @@ -477,30 +414,8 @@ public class PartyPlugin extends Plugin @Subscribe public void onUserJoin(final UserJoin event) { - final PartyData partyData = getPartyData(event.getMemberId()); - - if (partyData == null || !config.messages()) - { - return; - } - - final String joinMessage = new ChatMessageBuilder() - .append(ChatColorType.HIGHLIGHT) - .append(partyData.getMember().getName()) - .append(" has joined the party!") - .build(); - - chatMessageManager.queue(QueuedMessage.builder() - .type(ChatMessageType.FRIENDSCHATNOTIFICATION) - .runeLiteFormattedMessage(joinMessage) - .build()); - - final PartyMember localMember = party.getLocalMember(); - - if (localMember != null && partyData.getMember().getMemberId().equals(localMember.getMemberId())) - { - sendAlert = true; - } + // this has a side effect of creating the party data + getPartyData(event.getMemberId()); } @Subscribe @@ -557,20 +472,6 @@ public class PartyPlugin extends Plugin if (removed != null) { - if (config.messages()) - { - final String joinMessage = new ChatMessageBuilder() - .append(ChatColorType.HIGHLIGHT) - .append(removed.getMember().getName()) - .append(" has left the party!") - .build(); - - chatMessageManager.queue(QueuedMessage.builder() - .type(ChatMessageType.FRIENDSCHATNOTIFICATION) - .runeLiteFormattedMessage(joinMessage) - .build()); - } - worldMapManager.remove(removed.getWorldMapPoint()); SwingUtilities.invokeLater(() -> panel.removeMember(event.getMemberId())); @@ -587,14 +488,10 @@ public class PartyPlugin extends Plugin if (event.getPartyId() != null) { - config.setPreviousPartyId(String.valueOf(event.getPartyId())); + config.setPreviousPartyId(event.getPassphrase()); } - SwingUtilities.invokeLater(() -> - { - panel.removeAllMembers(); - panel.removeAllRequests(); - }); + SwingUtilities.invokeLater(panel::removeAllMembers); } @Subscribe @@ -605,12 +502,11 @@ public class PartyPlugin extends Plugin return; } - chatMessageManager.queue(QueuedMessage.builder().type(ChatMessageType.GAMEMESSAGE).value("Party " + party.getPartyId()).build()); - chatMessageManager.queue(QueuedMessage.builder().type(ChatMessageType.GAMEMESSAGE).value("Local Party " + party.getLocalPartyId()).build()); + chatMessageManager.queue(QueuedMessage.builder().type(ChatMessageType.GAMEMESSAGE).value("Party " + party.getPartyPassphrase() + " ID " + party.getPartyId()).build()); chatMessageManager.queue(QueuedMessage.builder().type(ChatMessageType.GAMEMESSAGE).value("Local ID " + party.getLocalMember().getMemberId()).build()); for (PartyMember partyMember : party.getMembers()) { - chatMessageManager.queue(QueuedMessage.builder().type(ChatMessageType.GAMEMESSAGE).value(" " + partyMember.getName() + " " + partyMember.getMemberId()).build()); + chatMessageManager.queue(QueuedMessage.builder().type(ChatMessageType.GAMEMESSAGE).value("Member " + partyMember.getName() + " " + partyMember.getDisplayName() + " " + partyMember.getMemberId()).build()); } } @@ -636,7 +532,6 @@ public class PartyPlugin extends Plugin return partyDataMap.computeIfAbsent(uuid, (u) -> { final WorldMapPoint worldMapPoint = new PartyWorldMapPoint(new WorldPoint(0, 0, 0), memberById); - worldMapPoint.setTooltip(memberById.getName()); // When first joining a party, other members can join before getting a join for self PartyMember partyMember = party.getLocalMember(); @@ -648,32 +543,10 @@ public class PartyPlugin extends Plugin worldMapManager.add(worldMapPoint); } - PartyData partyData = new PartyData(memberById, worldMapPoint, ColorUtil.fromObject(memberById.getName())); - partyData.setShowOverlay(config.autoOverlay()); - - if (config.includeSelf() || !isSelf) - { - SwingUtilities.invokeLater(() -> panel.addMember(partyData)); - } - else - { - SwingUtilities.invokeLater(panel::updateParty); - } + PartyData partyData = new PartyData(memberById, worldMapPoint); + SwingUtilities.invokeLater(() -> panel.addMember(partyData)); return partyData; }); } - - private void sendInstructionMessage() - { - final String helpMessage = new ChatMessageBuilder() - .append(ChatColorType.HIGHLIGHT) - .append("To leave the party, click \"Leave party\" on the party panel.") - .build(); - - chatMessageManager.queue(QueuedMessage.builder() - .type(ChatMessageType.FRIENDSCHATNOTIFICATION) - .runeLiteFormattedMessage(helpMessage) - .build()); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyRequestBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyRequestBox.java deleted file mode 100644 index f36b262285..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyRequestBox.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2021, Jonathan Rousseau - * 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.party; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.GridLayout; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JPanel; -import javax.swing.border.EmptyBorder; -import net.runelite.client.ui.ColorScheme; -import net.runelite.client.ui.DynamicGridLayout; -import net.runelite.client.ui.FontManager; -import net.runelite.client.ui.components.MouseDragEventForwarder; -import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; -import net.runelite.client.util.ImageUtil; -import net.runelite.client.util.SwingUtil; -import net.runelite.discord.DiscordRPC; - -class PartyRequestBox extends JPanel -{ - private static final ImageIcon CONFIRM_ICON = new ImageIcon(ImageUtil.loadImageResource(PartyPlugin.class, "confirm_icon.png")); - private static final ImageIcon CONFIRM_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.bufferedImageFromImage(CONFIRM_ICON.getImage()), 0.54f)); - private static final ImageIcon CANCEL_ICON = new ImageIcon(ImageUtil.loadImageResource(PartyPlugin.class, "cancel_icon.png")); - private static final ImageIcon CANCEL_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.bufferedImageFromImage(CANCEL_ICON.getImage()), 0.6f)); - - PartyRequestBox(final PartyPlugin plugin, final JComponent panel, String userId, String userName) - { - setLayout(new BorderLayout()); - setBorder(new EmptyBorder(5, 0, 0, 0)); - - /* The box's wrapping container */ - final JPanel container = new JPanel(); - container.setLayout(new BorderLayout()); - container.setBackground(ColorScheme.DARKER_GRAY_COLOR); - container.setBorder(new EmptyBorder(5, 5, 5, 5)); - - JPanel namesPanel = new JPanel(); - namesPanel.setLayout(new DynamicGridLayout(2, 1)); - namesPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); - namesPanel.setBorder(new EmptyBorder(2, 5, 2, 5)); - - JShadowedLabel nameLabel = new JShadowedLabel(); - nameLabel.setFont(FontManager.getRunescapeSmallFont()); - nameLabel.setForeground(Color.WHITE); - nameLabel.setText(userName); - - JShadowedLabel messageLabel = new JShadowedLabel(); - messageLabel.setFont(FontManager.getRunescapeSmallFont()); - messageLabel.setForeground(Color.WHITE); - messageLabel.setText("Wants to join your party!"); - - namesPanel.add(nameLabel); - namesPanel.add(messageLabel); - - JPanel actionsContainer = new JPanel(new GridLayout(1, 2, 8, 0)); - actionsContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); - - JButton confirmButton = new JButton(CONFIRM_ICON); - SwingUtil.removeButtonDecorations(confirmButton); - confirmButton.setToolTipText("Invite"); - confirmButton.setRolloverIcon(CONFIRM_HOVER_ICON); - confirmButton.addActionListener(e -> plugin.replyToRequest(userId, DiscordRPC.DISCORD_REPLY_YES)); - confirmButton.setPreferredSize(new Dimension(18, 18)); - - JButton cancelButton = new JButton(CANCEL_ICON); - SwingUtil.removeButtonDecorations(cancelButton); - cancelButton.setToolTipText("Reject"); - cancelButton.setRolloverIcon(CANCEL_HOVER_ICON); - cancelButton.addActionListener(e -> plugin.replyToRequest(userId, DiscordRPC.DISCORD_REPLY_NO)); - cancelButton.setPreferredSize(new Dimension(18, 18)); - - actionsContainer.add(confirmButton); - actionsContainer.add(cancelButton); - - container.add(namesPanel, BorderLayout.WEST); - container.add(actionsContainer, BorderLayout.EAST); - - // forward mouse drag events to parent panel for drag and drop reordering - MouseDragEventForwarder mouseDragEventForwarder = new MouseDragEventForwarder(panel); - container.addMouseListener(mouseDragEventForwarder); - container.addMouseMotionListener(mouseDragEventForwarder); - - add(container, BorderLayout.NORTH); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyStatsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyStatsOverlay.java deleted file mode 100644 index e103649d35..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyStatsOverlay.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2019, Tomas Slusny - * Copyright (c) 2021, Jonathan Rousseau - * 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.party; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.util.Map; -import java.util.UUID; -import javax.inject.Inject; -import net.runelite.api.MenuAction; -import net.runelite.client.plugins.party.data.PartyData; -import net.runelite.client.ui.overlay.OverlayMenuEntry; -import net.runelite.client.ui.overlay.OverlayPanel; -import net.runelite.client.ui.overlay.components.ComponentConstants; -import net.runelite.client.ui.overlay.components.PanelComponent; -import net.runelite.client.ui.overlay.components.ProgressBarComponent; -import net.runelite.client.ui.overlay.components.TitleComponent; -import net.runelite.client.ws.PartyService; - -public class PartyStatsOverlay extends OverlayPanel -{ - private static final Color HP_FG = new Color(0, 146, 54, 230); - private static final Color HP_BG = new Color(102, 15, 16, 230); - private static final Color PRAY_FG = new Color(0, 149, 151); - private static final Color PRAY_BG = Color.black; - - private final PartyPlugin plugin; - private final PartyService party; - private final PartyConfig config; - - @Inject - private PartyStatsOverlay(final PartyPlugin plugin, final PartyService party, final PartyConfig config) - { - super(plugin); - this.plugin = plugin; - this.party = party; - this.config = config; - panelComponent.setBorder(new Rectangle()); - panelComponent.setGap(new Point(0, ComponentConstants.STANDARD_BORDER / 2)); - getMenuEntries().add(new OverlayMenuEntry(MenuAction.RUNELITE_OVERLAY, "Leave", "Party")); - } - - @Override - public Dimension render(Graphics2D graphics) - { - final Map partyDataMap = plugin.getPartyDataMap(); - if (partyDataMap.isEmpty()) - { - return null; - } - - panelComponent.setBackgroundColor(null); - - synchronized (plugin.getPartyDataMap()) - { - partyDataMap.forEach((k, v) -> - { - boolean isSelf = party.getLocalMember() != null && party.getLocalMember().getMemberId().equals(k); - - if (!v.isShowOverlay() || (!config.includeSelf() && isSelf)) - { - return; - } - - final PanelComponent panel = v.getPanel(); - panel.getChildren().clear(); - - final TitleComponent name = TitleComponent.builder() - .text(v.getCharacterName().isEmpty() ? v.getMember().getName() : v.getCharacterName()) - .color(config.recolorNames() ? v.getColor() : Color.WHITE) - .build(); - - panel.getChildren().add(name); - - if (v.getMaxHitpoints() > 0) - { - final ProgressBarComponent hpBar = new ProgressBarComponent(); - hpBar.setBackgroundColor(HP_BG); - hpBar.setForegroundColor(HP_FG); - hpBar.setMaximum(v.getMaxHitpoints()); - hpBar.setValue(v.getHitpoints()); - hpBar.setLabelDisplayMode(ProgressBarComponent.LabelDisplayMode.FULL); - panel.getChildren().add(hpBar); - } - - if (v.getMaxPrayer() > 0) - { - final ProgressBarComponent prayBar = new ProgressBarComponent(); - prayBar.setBackgroundColor(PRAY_BG); - prayBar.setForegroundColor(PRAY_FG); - prayBar.setMaximum(v.getMaxPrayer()); - prayBar.setValue(v.getPrayer()); - prayBar.setLabelDisplayMode(ProgressBarComponent.LabelDisplayMode.FULL); - panel.getChildren().add(prayBar); - } - - panelComponent.getChildren().add(panel); - }); - } - - return super.render(graphics); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyWorldMapPoint.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyWorldMapPoint.java index 1c2bfd5b86..e85e32430c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyWorldMapPoint.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/PartyWorldMapPoint.java @@ -45,12 +45,23 @@ class PartyWorldMapPoint extends WorldMapPoint this.member = member; this.setSnapToEdge(true); this.setJumpOnClick(true); - this.setName(member.getName()); this.setImagePoint(new Point( ARROW.getWidth() / 2, ARROW.getHeight())); } + @Override + public String getName() + { + return member.getDisplayName(); + } + + @Override + public String getTooltip() + { + return member.getDisplayName(); + } + @Override public BufferedImage getImage() { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/party/data/PartyData.java b/runelite-client/src/main/java/net/runelite/client/plugins/party/data/PartyData.java index f61a3b8866..b821420a68 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/party/data/PartyData.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/party/data/PartyData.java @@ -41,12 +41,10 @@ public class PartyData private final PartyMember member; private final WorldMapPoint worldMapPoint; private final PanelComponent panel = new PanelComponent(); - private final Color color; + private Color color = Color.WHITE; private int hitpoints; private int maxHitpoints; private int prayer; private int maxPrayer; - private String characterName = ""; - private boolean showOverlay; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/PlayerInfoDrop.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/PlayerInfoDrop.java new file mode 100644 index 0000000000..09b95d7111 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/PlayerInfoDrop.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022, Hexagon + * 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.specialcounter; + +import java.awt.Color; +import java.awt.Font; +import java.awt.image.BufferedImage; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.runelite.client.ui.FontManager; + +@Data +@AllArgsConstructor(access = AccessLevel.PRIVATE) +class PlayerInfoDrop +{ + private final int startCycle; + private final int endCycle; + private final int playerIdx; + private final String text; + private final int startHeightOffset; + private final int endHeightOffset; + private final Font font; + private final Color color; + private final BufferedImage image; + + public static Builder builder(int startCycle, int endCycle, int playerIdx, String text) + { + return new Builder(startCycle, endCycle, playerIdx, text); + } + + @RequiredArgsConstructor + @Accessors(fluent = true) + @Setter + static class Builder + { + private final int startCycle; + private final int endCycle; + private final int playerIdx; + private final String text; + private int startHeightOffset = 0; + private int endHeightOffset = 200; + private Font font = FontManager.getRunescapeBoldFont(); + private Color color = Color.WHITE; + private BufferedImage image; + + public PlayerInfoDrop build() + { + if (startCycle > endCycle) + { + throw new IllegalArgumentException("endCycle must be after startCycle"); + } + if (playerIdx < 0 || playerIdx > 2047) + { + throw new IllegalArgumentException("playerIdx must be between 0-2047"); + } + return new PlayerInfoDrop(startCycle, endCycle, playerIdx, text, startHeightOffset, endHeightOffset, font, color, image); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/PlayerInfoDropOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/PlayerInfoDropOverlay.java new file mode 100644 index 0000000000..497eb0d3d5 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/PlayerInfoDropOverlay.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2022, Hexagon + * 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.specialcounter; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.util.Iterator; +import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.api.Point; +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.util.ColorUtil; +import net.runelite.client.util.ImageUtil; + +@Singleton +class PlayerInfoDropOverlay extends Overlay +{ + private final SpecialCounterPlugin plugin; + private final SpecialCounterConfig config; + private final Client client; + + @Inject + private PlayerInfoDropOverlay(SpecialCounterPlugin plugin, SpecialCounterConfig config, Client client) + { + this.plugin = plugin; + this.config = config; + this.client = client; + setPosition(OverlayPosition.DYNAMIC); + setPriority(OverlayPriority.MED); + } + + @Override + public Dimension render(Graphics2D graphics) + { + final List infoDrops = plugin.getPlayerInfoDrops(); + if (infoDrops.isEmpty()) + { + return null; + } + + final int cycle = client.getGameCycle(); + for (Iterator iterator = infoDrops.iterator(); iterator.hasNext();) + { + PlayerInfoDrop infoDrop = iterator.next(); + + if (cycle < infoDrop.getStartCycle()) + { + continue; + } + + if (cycle > infoDrop.getEndCycle()) + { + iterator.remove(); + continue; + } + + if (!config.specDrops()) + { + continue; + } + + Player player = client.getCachedPlayers()[infoDrop.getPlayerIdx()]; + if (player == null) + { + continue; + } + + int elapsed = cycle - infoDrop.getStartCycle(); + int percent = elapsed * 100 / (infoDrop.getEndCycle() - infoDrop.getStartCycle()); + int currentHeight = infoDrop.getEndHeightOffset() * percent / 100; + String text = infoDrop.getText(); + + graphics.setFont(infoDrop.getFont()); + Point textLocation = player.getCanvasTextLocation(graphics, text, player.getLogicalHeight() + infoDrop.getStartHeightOffset() + currentHeight); + if (textLocation == null) + { + continue; + } + + int alpha = 255 - (255 * percent / 100); + BufferedImage image = infoDrop.getImage(); + if (image != null) + { + int textHeight = graphics.getFontMetrics().getHeight() - graphics.getFontMetrics().getMaxDescent(); + int textMargin = image.getWidth() / 2; + int x = textLocation.getX() - textMargin - 1; + int y = textLocation.getY() - textHeight / 2 - image.getHeight() / 2; + Point imageLocation = new Point(x, y); + + textLocation = new Point(textLocation.getX() + textMargin, textLocation.getY()); + + OverlayUtil.renderImageLocation(graphics, imageLocation, ImageUtil.alphaOffset(image, alpha - 255)); + } + + drawText(graphics, textLocation, text, infoDrop.getColor(), alpha); + } + return null; + } + + private static void drawText(Graphics2D g, Point point, String text, Color color, int colorAlpha) + { + g.setColor(ColorUtil.colorWithAlpha(Color.BLACK, colorAlpha)); + g.drawString(text, point.getX() + 1, point.getY() + 1); + g.setColor(ColorUtil.colorWithAlpha(color, colorAlpha)); + g.drawString(text, point.getX(), point.getY()); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java index 7eb596275a..4249fa3be8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java @@ -25,6 +25,7 @@ */ package net.runelite.client.plugins.specialcounter; +import java.awt.Color; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; @@ -45,6 +46,28 @@ public interface SpecialCounterConfig extends Config @ConfigItem( position = 1, + keyName = "specDrops", + name = "Spec Drops", + description = "Draws an overlay over the player when a special attack hits" + ) + default boolean specDrops() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "specDropColor", + name = "Spec Drop Color", + description = "Text color for spec drops" + ) + default Color specDropColor() + { + return Color.WHITE; + } + + @ConfigItem( + position = 10, keyName = "dragonWarhammerThreshold", name = "Dragon Warhammer", description = "Threshold for Dragon Warhammer (0 to disable)" @@ -55,7 +78,7 @@ public interface SpecialCounterConfig extends Config } @ConfigItem( - position = 2, + position = 20, keyName = "arclightThreshold", name = "Arclight", description = "Threshold for Arclight (0 to disable)" @@ -66,7 +89,7 @@ public interface SpecialCounterConfig extends Config } @ConfigItem( - position = 3, + position = 30, keyName = "darklightThreshold", name = "Darklight", description = "Threshold for Darklight (0 to disable)" @@ -77,7 +100,7 @@ public interface SpecialCounterConfig extends Config } @ConfigItem( - position = 4, + position = 40, keyName = "bandosGodswordThreshold", name = "Bandos Godsword", description = "Threshold for Bandos Godsword (0 to disable)" @@ -88,7 +111,7 @@ public interface SpecialCounterConfig extends Config } @ConfigItem( - position = 5, + position = 50, keyName = "bulwarkThreshold", name = "Dinh's Bulwark", description = "Threshold for Dinh's Bulwark (0 to disable)" diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java index c1f72e1b42..9391d82ee6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java @@ -26,11 +26,17 @@ package net.runelite.client.plugins.specialcounter; import com.google.common.collect.ImmutableSet; import com.google.inject.Provides; +import java.awt.image.BufferedImage; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Inject; +import javax.inject.Named; +import lombok.AccessLevel; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Actor; import net.runelite.api.Client; @@ -44,6 +50,7 @@ import net.runelite.api.NPC; import net.runelite.api.NpcID; import net.runelite.api.VarPlayer; import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.CommandExecuted; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; import net.runelite.api.events.HitsplatApplied; @@ -57,7 +64,9 @@ 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.ImageUtil; import net.runelite.client.ws.PartyService; import net.runelite.client.ws.WSClient; @@ -95,6 +104,9 @@ public class SpecialCounterPlugin extends Plugin private final Set interactedNpcIds = new HashSet<>(); private final SpecialCounter[] specialCounter = new SpecialCounter[SpecialWeapon.values().length]; + @Getter(AccessLevel.PACKAGE) + private final List playerInfoDrops = new ArrayList<>(); + @Inject private Client client; @@ -119,6 +131,16 @@ public class SpecialCounterPlugin extends Plugin @Inject private SpecialCounterConfig config; + @Inject + private OverlayManager overlayManager; + + @Inject + private PlayerInfoDropOverlay playerInfoDropOverlay; + + @Inject + @Named("developerMode") + boolean developerMode; + @Provides SpecialCounterConfig getConfig(ConfigManager configManager) { @@ -128,6 +150,7 @@ public class SpecialCounterPlugin extends Plugin @Override protected void startUp() { + overlayManager.add(playerInfoDropOverlay); wsClient.registerMessage(SpecialCounterUpdate.class); currentWorld = -1; specialPercentage = -1; @@ -140,6 +163,7 @@ public class SpecialCounterPlugin extends Plugin protected void shutDown() { removeCounters(); + overlayManager.remove(playerInfoDropOverlay); wsClient.unregisterMessage(SpecialCounterUpdate.class); } @@ -267,15 +291,18 @@ public class SpecialCounterPlugin extends Plugin if (wasSpec && specialWeapon != null && hitsplat.getAmount() > 0) { int hit = getHit(specialWeapon, hitsplat); + int localPlayerId = client.getLocalPlayer().getId(); updateCounter(specialWeapon, null, hit); if (!party.getMembers().isEmpty()) { - final SpecialCounterUpdate specialCounterUpdate = new SpecialCounterUpdate(interactingId, specialWeapon, hit); + final SpecialCounterUpdate specialCounterUpdate = new SpecialCounterUpdate(interactingId, specialWeapon, hit, client.getWorld(), localPlayerId); specialCounterUpdate.setMemberId(party.getLocalMember().getMemberId()); wsClient.send(specialCounterUpdate); } + + playerInfoDrops.add(createSpecInfoDrop(specialWeapon, hit, localPlayerId)); } } @@ -315,7 +342,7 @@ public class SpecialCounterPlugin extends Plugin return; } - String name = party.getMemberById(event.getMemberId()).getName(); + String name = party.getMemberById(event.getMemberId()).getDisplayName(); if (name == null) { return; @@ -334,9 +361,23 @@ public class SpecialCounterPlugin extends Plugin { updateCounter(event.getWeapon(), name, event.getHit()); } + + if (event.getWorld() == client.getWorld()) + { + playerInfoDrops.add(createSpecInfoDrop(event.getWeapon(), event.getHit(), event.getPlayerId())); + } }); } + @Subscribe + public void onCommandExecuted(CommandExecuted commandExecuted) + { + if (developerMode && commandExecuted.getCommand().equals("spec")) + { + playerInfoDrops.add(createSpecInfoDrop(SpecialWeapon.BANDOS_GODSWORD, 42, client.getLocalPlayer().getId())); + } + } + private SpecialWeapon usedSpecialWeapon() { ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); @@ -424,4 +465,17 @@ public class SpecialCounterPlugin extends Plugin { return specialWeapon.isDamage() ? hitsplat.getAmount() : 1; } + + private PlayerInfoDrop createSpecInfoDrop(SpecialWeapon weapon, int hit, int playerId) + { + int cycle = client.getGameCycle(); + BufferedImage image = ImageUtil.resizeImage(itemManager.getImage(weapon.getItemID()[0]), 24, 24); + + return PlayerInfoDrop.builder(cycle, cycle + 100, playerId, Integer.toString(hit)) + .color(config.specDropColor()) + .startHeightOffset(100) + .endHeightOffset(400) + .image(image) + .build(); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterUpdate.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterUpdate.java index 16eff1af25..2c7bcbe21b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterUpdate.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterUpdate.java @@ -35,4 +35,6 @@ public class SpecialCounterUpdate extends PartyMemberMessage private final int npcId; private final SpecialWeapon weapon; private final int hit; + private final int world; + private final int playerId; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/StatusBarsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/StatusBarsOverlay.java index e414b794d4..2b68c49eb5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/StatusBarsOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/statusbars/StatusBarsOverlay.java @@ -34,6 +34,7 @@ import java.util.EnumMap; import java.util.Map; import javax.inject.Inject; import net.runelite.api.Client; +import net.runelite.api.Experience; import net.runelite.api.MenuEntry; import net.runelite.api.Point; import net.runelite.api.Prayer; @@ -117,7 +118,7 @@ class StatusBarsOverlay extends Overlay { barRenderers.put(BarMode.DISABLED, null); barRenderers.put(BarMode.HITPOINTS, new BarRenderer( - () -> client.getRealSkillLevel(Skill.HITPOINTS), + () -> inLms() ? Experience.MAX_REAL_LEVEL : client.getRealSkillLevel(Skill.HITPOINTS), () -> client.getBoostedSkillLevel(Skill.HITPOINTS), () -> getRestoreValue(Skill.HITPOINTS.getName()), () -> @@ -170,7 +171,7 @@ class StatusBarsOverlay extends Overlay } )); barRenderers.put(BarMode.PRAYER, new BarRenderer( - () -> client.getRealSkillLevel(Skill.PRAYER), + () -> inLms() ? Experience.MAX_REAL_LEVEL : client.getRealSkillLevel(Skill.PRAYER), () -> client.getBoostedSkillLevel(Skill.PRAYER), () -> getRestoreValue(Skill.PRAYER.getName()), () -> @@ -349,4 +350,9 @@ class StatusBarsOverlay extends Overlay return ImageUtil.resizeCanvas(image, ICON_DIMENSIONS.width, ICON_DIMENSIONS.height); } + + private boolean inLms() + { + return client.getWidget(WidgetInfo.LMS_KDA) != null; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java index d3dbcad8af..57da5b9b95 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java @@ -29,6 +29,7 @@ package net.runelite.client.plugins.timers; import com.google.inject.Provides; import java.time.Duration; import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.inject.Inject; @@ -45,7 +46,6 @@ import net.runelite.api.ItemContainer; import net.runelite.api.ItemID; import static net.runelite.api.ItemID.FIRE_CAPE; import static net.runelite.api.ItemID.INFERNAL_CAPE; -import net.runelite.api.MenuAction; import net.runelite.api.NPC; import net.runelite.api.NpcID; import net.runelite.api.Player; @@ -152,11 +152,12 @@ public class TimersPlugin extends Plugin private int lastPoisonVarp; private int lastPvpVarb; private int lastCorruptionVarb; + private int lastHomeTeleport; + private int lastMinigameTeleport; private int lastImbuedHeartVarb; private boolean imbuedHeartTimerActive; private int nextPoisonTick; private WorldPoint lastPoint; - private TeleportWidget lastTeleportClicked; private int lastAnimation; private boolean widgetHiddenChangedOnPvpWorld; private ElapsedTimer tzhaarTimer; @@ -185,6 +186,11 @@ public class TimersPlugin extends Plugin @Override public void startUp() { + if (config.showHomeMinigameTeleports()) + { + checkTeleport(VarPlayer.LAST_HOME_TELEPORT); + checkTeleport(VarPlayer.LAST_MINIGAME_TELEPORT); + } } @Override @@ -193,7 +199,6 @@ public class TimersPlugin extends Plugin infoBoxManager.removeIf(t -> t instanceof TimerTimer); lastRaidVarb = -1; lastPoint = null; - lastTeleportClicked = null; lastAnimation = -1; widgetHiddenChangedOnPvpWorld = false; lastPoisonVarp = 0; @@ -202,6 +207,8 @@ public class TimersPlugin extends Plugin staminaTimer = null; imbuedHeartTimerActive = false; lastImbuedHeartVarb = 0; + lastHomeTeleport = 0; + lastMinigameTeleport = 0; } @Subscribe @@ -214,6 +221,8 @@ public class TimersPlugin extends Plugin int pvpVarb = client.getVarbitValue(Varbits.PVP_SPEC_ORB); int corruptionCooldownVarb = client.getVarbitValue(Varbits.CORRUPTION_COOLDOWN); int imbuedHeartCooldownVarb = client.getVarbitValue(Varbits.IMBUED_HEART_COOLDOWN); + int homeTeleportVarp = client.getVar(VarPlayer.LAST_HOME_TELEPORT); + int minigameTeleportVarp = client.getVar(VarPlayer.LAST_MINIGAME_TELEPORT); if (lastRaidVarb != raidVarb) { @@ -320,6 +329,18 @@ public class TimersPlugin extends Plugin lastImbuedHeartVarb = imbuedHeartCooldownVarb; } + + if (lastHomeTeleport != homeTeleportVarp) + { + checkTeleport(VarPlayer.LAST_HOME_TELEPORT); + lastHomeTeleport = homeTeleportVarp; + } + + if (lastMinigameTeleport != minigameTeleportVarp) + { + checkTeleport(VarPlayer.LAST_MINIGAME_TELEPORT); + lastMinigameTeleport = minigameTeleportVarp; + } } @Subscribe @@ -335,6 +356,11 @@ public class TimersPlugin extends Plugin removeGameTimer(HOME_TELEPORT); removeGameTimer(MINIGAME_TELEPORT); } + else + { + checkTeleport(VarPlayer.LAST_HOME_TELEPORT); + checkTeleport(VarPlayer.LAST_MINIGAME_TELEPORT); + } if (!config.showAntiFire()) { @@ -491,15 +517,6 @@ public class TimersPlugin extends Plugin return; } } - - if (event.getMenuAction() == MenuAction.CC_OP) - { - TeleportWidget teleportWidget = TeleportWidget.of(event.getParam1()); - if (teleportWidget != null) - { - lastTeleportClicked = teleportWidget; - } - } } @Subscribe @@ -877,6 +894,37 @@ public class TimersPlugin extends Plugin } } + private void checkTeleport(VarPlayer varPlayer) + { + final GameTimer teleport; + switch (varPlayer) + { + case LAST_HOME_TELEPORT: + teleport = HOME_TELEPORT; + break; + case LAST_MINIGAME_TELEPORT: + teleport = MINIGAME_TELEPORT; + break; + default: + // Other var changes are not handled as teleports + return; + } + + int lastTeleport = client.getVar(varPlayer); + long lastTeleportSeconds = (long) lastTeleport * 60; + Instant teleportExpireInstant = Instant.ofEpochSecond(lastTeleportSeconds).plus(teleport.getDuration().getSeconds(), ChronoUnit.SECONDS); + Duration remainingTime = Duration.between(Instant.now(), teleportExpireInstant); + + if (remainingTime.getSeconds() > 0) + { + createGameTimer(teleport, remainingTime); + } + else + { + removeGameTimer(teleport); + } + } + @Subscribe public void onGameTick(GameTick event) { @@ -956,23 +1004,6 @@ public class TimersPlugin extends Plugin return; } - if (config.showHomeMinigameTeleports() - && client.getLocalPlayer().getAnimation() == AnimationID.IDLE - && (lastAnimation == AnimationID.BOOK_HOME_TELEPORT_5 - || lastAnimation == AnimationID.COW_HOME_TELEPORT_6 - || lastAnimation == AnimationID.LEAGUE_HOME_TELEPORT_6 - || lastAnimation == AnimationID.SHATTERED_LEAGUE_HOME_TELEPORT_6)) - { - if (lastTeleportClicked == TeleportWidget.HOME_TELEPORT) - { - createGameTimer(HOME_TELEPORT); - } - else if (lastTeleportClicked == TeleportWidget.MINIGAME_TELEPORT) - { - createGameTimer(MINIGAME_TELEPORT); - } - } - if (config.showDFSSpecial() && lastAnimation == AnimationID.DRAGONFIRE_SHIELD_SPECIAL) { createGameTimer(DRAGON_FIRE_SHIELD); diff --git a/runelite-client/src/main/java/net/runelite/client/ws/PartyMember.java b/runelite-client/src/main/java/net/runelite/client/ws/PartyMember.java index a5e6cc3275..cdc6ac1033 100644 --- a/runelite-client/src/main/java/net/runelite/client/ws/PartyMember.java +++ b/runelite-client/src/main/java/net/runelite/client/ws/PartyMember.java @@ -33,5 +33,7 @@ public class PartyMember { private final UUID memberId; private final String name; + private String displayName = ""; + private boolean loggedIn; private BufferedImage avatar; } diff --git a/runelite-client/src/main/java/net/runelite/client/ws/PartyService.java b/runelite-client/src/main/java/net/runelite/client/ws/PartyService.java index dc00df2e4d..123a172a0b 100644 --- a/runelite-client/src/main/java/net/runelite/client/ws/PartyService.java +++ b/runelite-client/src/main/java/net/runelite/client/ws/PartyService.java @@ -25,20 +25,25 @@ */ package net.runelite.client.ws; -import com.google.common.base.Charsets; +import com.google.common.base.CharMatcher; import com.google.common.hash.Hashing; import java.awt.image.BufferedImage; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; +import java.util.Random; import java.util.UUID; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.ItemComposition; import net.runelite.client.account.AccountSession; import net.runelite.client.account.SessionManager; import net.runelite.client.chat.ChatMessageManager; @@ -46,8 +51,8 @@ import net.runelite.client.chat.QueuedMessage; import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.PartyChanged; -import net.runelite.client.util.Text; import net.runelite.client.events.PartyMemberAvatar; +import net.runelite.client.util.Text; import static net.runelite.client.util.Text.JAGEX_PRINTABLE_CHAR_MATCHER; import net.runelite.http.api.ws.messages.party.Join; import net.runelite.http.api.ws.messages.party.Part; @@ -60,31 +65,27 @@ import net.runelite.http.api.ws.messages.party.UserSync; @Singleton public class PartyService { - public static final int PARTY_MAX = 15; private static final int MAX_MESSAGE_LEN = 150; private static final int MAX_USERNAME_LEN = 32; // same as Discord + private static final String USERNAME = "rluser-" + new Random().nextInt(Integer.MAX_VALUE); + private static final String ALPHABET = "bcdfghjklmnpqrstvwxyz"; + private final Client client; private final WSClient wsClient; private final SessionManager sessionManager; private final EventBus eventBus; private final ChatMessageManager chat; private final List members = new ArrayList<>(); - @Getter - private UUID localPartyId = UUID.randomUUID(); - - @Getter - private UUID publicPartyId; // public party id, for advertising on discord, derived from the secret - @Getter private UUID partyId; // secret party id - - @Setter - private String username; + @Getter + private String partyPassphrase; @Inject - private PartyService(final WSClient wsClient, final SessionManager sessionManager, final EventBus eventBus, final ChatMessageManager chat) + private PartyService(final Client client, final WSClient wsClient, final SessionManager sessionManager, final EventBus eventBus, final ChatMessageManager chat) { + this.client = client; this.wsClient = wsClient; this.sessionManager = sessionManager; this.eventBus = eventBus; @@ -92,36 +93,89 @@ public class PartyService eventBus.register(this); } - public void changeParty(@Nullable UUID newParty) + public String generatePasspharse() { - if (username == null) + assert client.isClientThread(); + + Random r = new Random(); + StringBuilder sb = new StringBuilder(); + + if (client.getGameState().getState() >= GameState.LOGIN_SCREEN.getState()) { - log.warn("Tried to join a party with no username"); - return; + int len = 0; + final CharMatcher matcher = CharMatcher.javaLetter(); + do + { + final int itemId = r.nextInt(client.getItemCount()); + final ItemComposition def = client.getItemDefinition(itemId); + final String name = def.getName(); + if (name == null || name.isEmpty() || name.equals("null")) + { + continue; + } + + final String[] split = name.split(" "); + final String token = split[r.nextInt(split.length)]; + if (!matcher.matchesAllOf(token) || token.length() <= 2) + { + continue; + } + + if (sb.length() > 0) + { + sb.append('-'); + } + sb.append(token.toLowerCase(Locale.US)); + ++len; + } + while (len < 4); + } + else + { + int len = 0; + do + { + if (sb.length() > 0) + { + sb.append('-'); + } + for (int i = 0; i < 5; ++i) + { + sb.append(ALPHABET.charAt(r.nextInt(ALPHABET.length()))); + } + ++len; + } + while (len < 4); } + String partyPassphrase = sb.toString(); + log.debug("Generated party passpharse {}", partyPassphrase); + return partyPassphrase; + } + + public void changeParty(@Nullable String passphrase) + { if (wsClient.sessionExists()) { wsClient.send(new Part()); } - log.debug("Party change to {}", newParty); + UUID id = passphrase != null ? passphraseToId(passphrase) : null; + + log.debug("Party change to {} (id {})", passphrase, id); members.clear(); - partyId = newParty; - // The public party ID needs to be consistent across party members, but not a secret - publicPartyId = newParty != null ? UUID.nameUUIDFromBytes(Hashing.sha256().hashString(newParty.toString(), Charsets.UTF_8).asBytes()) : null; + partyId = id; + partyPassphrase = passphrase; if (partyId == null) { - localPartyId = UUID.randomUUID(); // cycle local party id so that a new party is created now - // close the websocket if the session id isn't for an account if (sessionManager.getAccountSession() == null) { wsClient.changeSession(null); } - eventBus.post(new PartyChanged(partyId)); + eventBus.post(new PartyChanged(partyPassphrase, partyId)); return; } @@ -134,8 +188,8 @@ public class PartyService wsClient.changeSession(uuid); } - eventBus.post(new PartyChanged(partyId)); - wsClient.send(new Join(partyId, username)); + eventBus.post(new PartyChanged(partyPassphrase, partyId)); + wsClient.send(new Join(partyId, USERNAME)); } @Subscribe(priority = 1) // run prior to plugins so that the member is joined by the time the plugins see it. @@ -171,11 +225,18 @@ public class PartyService @Subscribe public void onPartyChatMessage(final PartyChatMessage message) { + final PartyMember member = getMemberById(message.getMemberId()); + if (member == null || !member.isLoggedIn()) + { + log.debug("Dropping party chat from non logged-in member"); + return; + } + // Remove non-printable characters, and tags from message String sentMesage = JAGEX_PRINTABLE_CHAR_MATCHER.retainFrom(message.getValue()) .replaceAll("", ""); - // Cap the mesage length + // Cap the message length if (sentMesage.length() > MAX_MESSAGE_LEN) { sentMesage = sentMesage.substring(0, MAX_MESSAGE_LEN); @@ -184,14 +245,14 @@ public class PartyService chat.queue(QueuedMessage.builder() .type(ChatMessageType.FRIENDSCHAT) .sender("Party") - .name(getMemberById(message.getMemberId()).getName()) + .name(member.getDisplayName()) .runeLiteFormattedMessage(sentMesage) .build()); } public PartyMember getLocalMember() { - return getMemberByName(username); + return getMemberByName(USERNAME); } public PartyMember getMemberById(final UUID id) @@ -230,11 +291,6 @@ public class PartyService return partyId != null; } - public boolean isPartyOwner() - { - return localPartyId.equals(partyId); - } - public void setPartyMemberAvatar(UUID memberID, BufferedImage image) { final PartyMember memberById = getMemberById(memberID); @@ -255,4 +311,13 @@ public class PartyService } return s; } + + private static UUID passphraseToId(String passphrase) + { + return UUID.nameUUIDFromBytes( + Hashing.sha256().hashBytes( + passphrase.getBytes(StandardCharsets.UTF_8) + ).asBytes() + ); + } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java index 977e379a9a..4846efdd9a 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java @@ -26,8 +26,10 @@ package net.runelite.client.plugins.specialcounter; import com.google.inject.Guice; import com.google.inject.Inject; +import com.google.inject.name.Named; import com.google.inject.testing.fieldbinder.Bind; import com.google.inject.testing.fieldbinder.BoundFieldModule; +import java.awt.image.BufferedImage; import net.runelite.api.Actor; import net.runelite.api.Client; import net.runelite.api.EquipmentInventorySlot; @@ -44,13 +46,16 @@ import net.runelite.api.events.InteractingChanged; import net.runelite.api.events.VarbitChanged; import net.runelite.client.Notifier; import net.runelite.client.game.ItemManager; +import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import net.runelite.client.util.AsyncBufferedImage; import net.runelite.client.ws.PartyService; import net.runelite.client.ws.WSClient; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import org.mockito.Mock; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; @@ -66,6 +71,10 @@ public class SpecialCounterPluginTest @Bind private Client client; + @Bind + @Named("developerMode") + boolean developerMode; + @Mock @Bind private InfoBoxManager infoBoxManager; @@ -90,6 +99,14 @@ public class SpecialCounterPluginTest @Bind private SpecialCounterConfig specialCounterConfig; + @Mock + @Bind + private OverlayManager overlayManager; + + @Mock + @Bind + private PlayerInfoDropOverlay playerInfoDropOverlay; + @Inject private SpecialCounterPlugin specialCounterPlugin; @@ -107,6 +124,8 @@ public class SpecialCounterPluginTest when(client.getVar(VarPlayer.SPECIAL_ATTACK_PERCENT)).thenReturn(100); specialCounterPlugin.onVarbitChanged(new VarbitChanged()); + // Set up item image for spec info drop + when(itemManager.getImage(anyInt())).thenReturn(new AsyncBufferedImage(24, 24, BufferedImage.TYPE_INT_ARGB)); } private static HitsplatApplied hitsplat(Actor target, Hitsplat.HitsplatType type)