Merge pull request #3207 from SRLJustin/upstream-2905

upstream: merge
This commit is contained in:
Justin
2022-05-29 19:26:54 +10:00
committed by GitHub
33 changed files with 987 additions and 880 deletions

View File

@@ -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;

View File

@@ -30,5 +30,6 @@ import lombok.Value;
@Value
public class PartyChanged
{
private final String passphrase;
private final UUID partyId;
}

View File

@@ -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

View File

@@ -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();

View File

@@ -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<EventWithTime> 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());
}
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -76,9 +76,6 @@ public class HunterPlugin extends Plugin
@Getter
private final Map<WorldPoint, HunterTrap> 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;

View File

@@ -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);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Jordan Atwood <jordan.atwood423@gmail.com>
* Copyright (c) 2022 Hydrox6 <ikada@protonmail.ch>
* 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;
}
}

View File

@@ -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();

View File

@@ -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());
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2022 Hydrox6 <ikada@protonmail.ch>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.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;
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 2022, Jordan Atwood <nightfirecat@nightfirec.at>
* 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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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 = "",

View File

@@ -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)

View File

@@ -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<String, PartyRequestBox> requestBoxes = new HashMap<>();
private final Map<UUID, PartyMemberBox> 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!<br/>" +
"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();
}
}
}

View File

@@ -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());
}
}

View File

@@ -1,111 +0,0 @@
/*
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* 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);
}
}

View File

@@ -1,129 +0,0 @@
/*
* Copyright (c) 2019, Tomas Slusny <slusnucky@gmail.com>
* Copyright (c) 2021, Jonathan Rousseau <https://github.com/JoRouss>
* 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<UUID, PartyData> 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);
}
}

View File

@@ -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()
{

View File

@@ -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;
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) 2022, Hexagon <hexagon@fking.work>
* 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);
}
}
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2022, Hexagon <hexagon@fking.work>
* 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<PlayerInfoDrop> infoDrops = plugin.getPlayerInfoDrops();
if (infoDrops.isEmpty())
{
return null;
}
final int cycle = client.getGameCycle();
for (Iterator<PlayerInfoDrop> 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());
}
}

View File

@@ -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)"

View File

@@ -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<Integer> interactedNpcIds = new HashSet<>();
private final SpecialCounter[] specialCounter = new SpecialCounter[SpecialWeapon.values().length];
@Getter(AccessLevel.PACKAGE)
private final List<PlayerInfoDrop> 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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -33,5 +33,7 @@ public class PartyMember
{
private final UUID memberId;
private final String name;
private String displayName = "<unknown>";
private boolean loggedIn;
private BufferedImage avatar;
}

View File

@@ -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<PartyMember> 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 <img> tags from message
String sentMesage = JAGEX_PRINTABLE_CHAR_MATCHER.retainFrom(message.getValue())
.replaceAll("<img=.+>", "");
// 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()
);
}
}

View File

@@ -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)