diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index d61416bb0c..5efe2e542e 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -265,6 +265,14 @@ public enum Varbits PYRAMID_PLUNDER_TIMER(2375), PYRAMID_PLUNDER_ROOM(2377), + /** + * Spicy stew ingredients + */ + SPICY_STEW_RED_SPICES(1879), + SPICY_STEW_YELLOW_SPICES(1880), + SPICY_STEW_BROWN_SPICES(1881), + SPICY_STEW_ORANGE_SPICES(1882), + /** * Multicombat area */ diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java index 6510fbb7fc..070b6ebd43 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/ItemStatChanges.java @@ -30,6 +30,7 @@ import java.util.HashSet; import java.util.Map; import lombok.extern.slf4j.Slf4j; import static net.runelite.api.ItemID.*; +import net.runelite.client.plugins.itemstats.special.SpicyStew; import net.runelite.client.plugins.itemstats.potions.SuperRestore; import static net.runelite.client.plugins.itemstats.Builders.*; import static net.runelite.client.plugins.itemstats.stats.Stats.*; @@ -41,12 +42,12 @@ import net.runelite.client.plugins.itemstats.potions.PrayerPotion; public class ItemStatChanges { - public ItemStatChanges() + ItemStatChanges() { init(); } - final void init() + private void init() { add(food(-5), POISON_KARAMBWAN); add(food(1), POTATO, ONION, CABBAGE, POT_OF_CREAM, CHOPPED_ONION); @@ -147,10 +148,11 @@ public class ItemStatChanges add(combo(2, heal(HITPOINTS, 11), boost(AGILITY, 5), heal(RUN_ENERGY, 10)), SUMMER_PIE, HALF_A_SUMMER_PIE); // Other + add(new SpicyStew(), SPICY_STEW); add(boost(MAGIC, perc(.10, 1)), IMBUED_HEART); add(combo(boost(ATTACK, 2), boost(STRENGTH, 1), heal(DEFENCE, -1)), JANGERBERRIES); - log.debug("{} items; {} behaviours loaded", effects.size(), new HashSet(effects.values()).size()); + log.debug("{} items; {} behaviours loaded", effects.size(), new HashSet<>(effects.values()).size()); } private final Map effects = new HashMap<>(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatChange.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatChange.java index 23540ef78d..3157748083 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatChange.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/StatChange.java @@ -33,9 +33,30 @@ import net.runelite.client.plugins.itemstats.stats.Stat; @Data public class StatChange { + /** + * The stat which will be boosted (or damaged). + */ private Stat stat; + + /** + * Relative change that will occur if the stat boost is applied now. + * Should be a number prefixed by "+" or "-". + */ private String relative; - private String absolute; + + /** + * Theoretical change that can occur before boost cap is enforced. + * Should be a number prefixed by "+" or "-". + */ private String theoretical; + + /** + * Absolute total of the stat after applying the boost. + */ + private String absolute; + + /** + * How beneficial this stat boost will be to the player. + */ private Positivity positivity; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/SpicyStew.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/SpicyStew.java new file mode 100644 index 0000000000..03ed2fce34 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/special/SpicyStew.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018, Adam + * 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.special; + +import java.util.ArrayList; +import java.util.List; +import net.runelite.api.Client; +import net.runelite.api.Varbits; +import net.runelite.client.plugins.itemstats.*; +import net.runelite.client.plugins.itemstats.stats.Stat; +import net.runelite.client.plugins.itemstats.stats.Stats; + +public class SpicyStew implements Effect +{ + + @Override + public StatsChanges calculate(Client client) + { + /* + * Spice boosts listed in the colour order of [Spicy stew -> Smell] + */ + int redBoost = spiceBoostOf(client.getSetting(Varbits.SPICY_STEW_RED_SPICES)); + int yellowBoost = spiceBoostOf(client.getSetting(Varbits.SPICY_STEW_YELLOW_SPICES)); + int orangeBoost = spiceBoostOf(client.getSetting(Varbits.SPICY_STEW_ORANGE_SPICES)); + int brownBoost = spiceBoostOf(client.getSetting(Varbits.SPICY_STEW_BROWN_SPICES)); + + List changes = new ArrayList<>(); + + /* + * Red spices: Attack, Strength, Defence, Ranged, Magic + */ + if (redBoost > 0) + { + changes.add(statChangeOf(Stats.ATTACK, redBoost, client)); + changes.add(statChangeOf(Stats.STRENGTH, redBoost, client)); + changes.add(statChangeOf(Stats.DEFENCE, redBoost, client)); + changes.add(statChangeOf(Stats.RANGED, redBoost, client)); + changes.add(statChangeOf(Stats.MAGIC, redBoost, client)); + } + + /* + * Yellow spices: Prayer, Agility, Thieving, Slayer, Hunter + */ + if (yellowBoost > 0) + { + changes.add(statChangeOf(Stats.PRAYER, yellowBoost, client)); + changes.add(statChangeOf(Stats.AGILITY, yellowBoost, client)); + changes.add(statChangeOf(Stats.THIEVING, yellowBoost, client)); + changes.add(statChangeOf(Stats.SLAYER, yellowBoost, client)); + changes.add(statChangeOf(Stats.HUNTER, yellowBoost, client)); + } + + /* + * Orange spices: Smithing, Cooking, Crafting, Firemaking, Fletching, Runecraft, Construction + */ + if (orangeBoost > 0) + { + changes.add(statChangeOf(Stats.SMITHING, orangeBoost, client)); + changes.add(statChangeOf(Stats.COOKING, orangeBoost, client)); + changes.add(statChangeOf(Stats.CRAFTING, orangeBoost, client)); + changes.add(statChangeOf(Stats.FIREMAKING, orangeBoost, client)); + changes.add(statChangeOf(Stats.FLETCHING, orangeBoost, client)); + changes.add(statChangeOf(Stats.RUNECRAFT, orangeBoost, client)); + changes.add(statChangeOf(Stats.CONSTRUCTION, orangeBoost, client)); + } + + /* + * Brown spices: Mining, Herblore, Fishing, Woodcutting, Farming + */ + if (brownBoost > 0) + { + changes.add(statChangeOf(Stats.MINING, brownBoost, client)); + changes.add(statChangeOf(Stats.HERBLORE, brownBoost, client)); + changes.add(statChangeOf(Stats.FISHING, brownBoost, client)); + changes.add(statChangeOf(Stats.WOODCUTTING, brownBoost, client)); + changes.add(statChangeOf(Stats.FARMING, brownBoost, client)); + } + + StatsChanges changesReturn = new StatsChanges(4); + changesReturn.setStatChanges(changes.toArray(new StatChange[changes.size()])); + + return changesReturn; + } + + /** + * Calculate the potential boost that a spice currently offers, + * based on its number of doses in the stew. + * + * @param spiceDoses Number of doses between 0 and 3. + * @return Either 0, +1, +3, or +5. + */ + private static int spiceBoostOf(int spiceDoses) + { + return Math.max(0, (spiceDoses * 2) - 1); + } + + /** + * Calculate the fields of a stat change tooltip row. + * + * @param stat Stat that the spice boost affects. + * @param spiceBoost Potential spice boost before capping. + * @param client Client API, needed to check current stat values. + * @return StatChange object with all required values. + */ + private static StatChange statChangeOf(Stat stat, int spiceBoost, Client client) + { + int currentValue = stat.getValue(client); + int currentBase = stat.getMaximum(client); + + int currentBoost = currentValue - currentBase; // Can be negative + int spiceBoostCapped = (currentBoost <= 0) ? spiceBoost : Math.max(0, spiceBoost - currentBoost); + + StatChange change = new StatChange(); + change.setStat(stat); + change.setRelative("±" + spiceBoostCapped); + change.setTheoretical("±" + spiceBoost); + change.setAbsolute(String.valueOf(stat.getValue(client) + spiceBoostCapped)); + + Positivity positivity; + if (spiceBoostCapped == 0) + { + positivity = Positivity.NO_CHANGE; + } + else if (spiceBoost > spiceBoostCapped) + { + positivity = Positivity.BETTER_CAPPED; + } + else + { + positivity = Positivity.BETTER_UNCAPPED; + } + change.setPositivity(positivity); + + return change; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/stats/Stat.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/stats/Stat.java index 7fb3547d5e..be7b2ff4d8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/stats/Stat.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemstats/stats/Stat.java @@ -46,7 +46,13 @@ public abstract class Stat return name; } + /** + * Get the current stat value including any boosts or damage. + */ public abstract int getValue(Client client); + /** + * Get the base stat maximum, ie. the bottom half of the stat fraction. + */ public abstract int getMaximum(Client client); }