diff --git a/Server/src/main/content/global/handlers/npc/DecantListener.kt b/Server/src/main/content/global/handlers/npc/DecantListener.kt index 57bdc858a..e8d78a71f 100644 --- a/Server/src/main/content/global/handlers/npc/DecantListener.kt +++ b/Server/src/main/content/global/handlers/npc/DecantListener.kt @@ -1,5 +1,6 @@ package content.global.handlers.npc +import core.api.* import content.data.consumables.Consumables import core.game.consumable.Potion import core.game.node.entity.player.Player @@ -8,42 +9,22 @@ import core.game.dialogue.DialogueFile import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs class DecantListener : InteractionListener { override fun defineListeners() { on(IntType.NPC,"decant"){ player, node -> - decant(player) + val (toRemove, toAdd) = decantContainer(player.inventory) + for (item in toRemove) + removeItem(player, item) + for (item in toAdd) + addItem(player, item.id, item.amount) player.dialogueInterpreter.open(DecantingDialogue(),node.asNpc()) return@on true } } - - fun decant(p: Player) { - val potcounts = HashMap() - val results: List - for (i in 0..27) { - val pot = (Consumables.getConsumableById(p.inventory.getId(i)) ?: continue) as? Potion ?: continue - if (pot != null) { - val dosage = p.inventory[i].name.replace("[^\\d.]".toRegex(), "").toInt() - if (potcounts[pot] != null) { - potcounts.replace(pot, potcounts[pot]!! + dosage) - } else { - potcounts.putIfAbsent(pot, dosage) - } - p.inventory.remove(Item(p.inventory.getId(i))) - } - } - potcounts.keys.forEach{ pot: Potion -> - val full_count = potcounts[pot]!! / pot.ids.size - val partial_dose_amt = potcounts[pot]!! % pot.ids.size - p.inventory.add(Item(pot.ids[0], full_count)) - if (partial_dose_amt > 0) p.inventory - .add(Item(pot.ids[pot.ids.size - partial_dose_amt])) - } - } - internal class DecantingDialogue : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { when(stage++){ @@ -53,4 +34,4 @@ class DecantListener : InteractionListener { } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/herblore/PotionDecantingPlugin.java b/Server/src/main/content/global/skill/herblore/PotionDecantingPlugin.java deleted file mode 100644 index 884d4917b..000000000 --- a/Server/src/main/content/global/skill/herblore/PotionDecantingPlugin.java +++ /dev/null @@ -1,332 +0,0 @@ -package content.global.skill.herblore; - -import core.cache.def.impl.ItemDefinition; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents the plugin used to handle the decanting of potions. - * @author 'Vexia - */ -@Initializable -public final class PotionDecantingPlugin extends UseWithHandler { - - /** - * Represents the empty item vial. - */ - private static final Item EMPTY_VIAL = new Item(229, 1); - - /** - * Represents the array of potions to utilitize. - */ - public static final int[] POTIONS = new int[] { - /* empty vial */229, - /* Strength potion(4) */113, - /* Strength potion(3) */115, - /* Strength potion(2) */117, - /* Strength potion(1) */119, - /* Attack potion(3) */121, - /* Attack potion(2) */123, - /* Attack potion(1) */125, - /* Restore potion(3) */127, - /* Restore potion(2) */129, - /* Restore potion(1) */131, - /* Defence potion(3) */133, - /* Defence potion(2) */135, - /* Defence potion(1) */137, - /* Prayer potion(3) */139, - /* Prayer potion(2) */141, - /* Prayer potion(1) */143, - /* Super attack(3) */145, - /* Super attack(2) */147, - /* Super attack(1) */149, - /* Fishing potion(3) */151, - /* Fishing potion(2) */153, - /* Fishing potion(1) */155, - /* Super strength(3) */157, - /* Super strength(2) */159, - /* Super strength(1) */161, - /* Super defence(3) */163, - /* Super defence(2) */165, - /* Super defence(1) */167, - /* Ranging potion(3) */169, - /* Ranging potion(2) */171, - /* Ranging potion(1) */173, - /* Antipoison(3) */175, - /* Antipoison(2) */177, - /* Antipoison(1) */179, - /* Super antipoison(3) */181, - /* Super antipoison(2) */183, - /* Super antipoison(1) */185, - /* Bravery potion */739, - /* Cadava potion */756, - /* Magic ogre potion */2395, - /* Attack potion(4) */2428, - /* Restore potion(4) */2430, - /* Defence potion(4) */2432, - /* Prayer potion(4) */2434, - /* Super attack(4) */2436, - /* Fishing potion(4) */2438, - /* Super strength(4) */2440, - /* Super defence(4) */2442, - /* Ranging potion(4) */2444, - /* Antipoison(4) */2446, - /* Super antipoison(4) */2448, - /* Antifire potion(4) */2452, - /* Antifire potion(3) */2454, - /* Antifire potion(2) */2456, - /* Antifire potion(1) */2458, - /* Energy potion(4) */3008, - /* Energy potion(3) */3010, - /* Energy potion(2) */3012, - /* Energy potion(1) */3014, - /* Super energy(4) */3016, - /* Super energy(3) */3018, - /* Super energy(2) */3020, - /* Super energy(1) */3022, - /* Super restore(4) */3024, - /* Super restore(3) */3026, - /* Super restore(2) */3028, - /* Super restore(1) */3030, - /* Agility potion(4) */3032, - /* Agility potion(3) */3034, - /* Agility potion(2) */3036, - /* Agility potion(1) */3038, - /* Magic potion(4) */3040, - /* Magic potion(3) */3042, - /* Magic potion(2) */3044, - /* Magic potion(1) */3046, - /* Troll potion */3265, - /* Explosive potion */4045, - /* Super kebab */4608, - /* Strange potion */4836, - /* Antipoison+(4) */5943, - /* Antipoison+(3) */5945, - /* Antipoison+(2) */5947, - /* Antipoison+(1) */5949, - /* Antipoison++(4) */5952, - /* Antipoison++(3) */5954, - /* Antipoison++(2) */5956, - /* Antipoison++(1) */5958, - /* Supercompost */6034, - /* Compost potion(4) */6470, - /* Compost potion(3) */6472, - /* Compost potion(2) */6474, - /* Compost potion(1) */6476, - /* Combat potion(4) */9739, - /* Combat potion(3) */9741, - /* Combat potion(2) */9743, - /* Combat potion(1) */9745, - /* Hunter potion(4) */9998, - /* Hunter potion(3) */10000, - /* Hunter potion(2) */10002, - /* Hunter potion(1) */10004, - /* Antipoison mix(2) */11433, - /* Antipoison mix(1) */11435, - /* Superattack mix(2) */11469, - /* Superattack mix(1) */11471, - /* Antifire mix(2) */11505, - /* Antifire mix(1) */11507, - /* Goblin potion (4) */11809, - /* Goblin potion (3) */11810, - /* Goblin potion (2) */11811, - /* Goblin potion (1) */11812, - /* Summoning potion(4) */12140, - /* Summoning potion(3) */12142, - /* Summoning potion(2) */12144, - /* Summoning potion(1) */12146, - /* Saradomin brews */ 6685, 6687, 6689, 6691, - /* Zamorak brews*/ 2450, 189, 191, 193, - /* Extended antifire potions*/ 14753, 14755, 14757, 14759, - /* Super ranging */ 14776, 14777, 14778, 14779, - /* Super magic */ 14780, 14781, 14782, 14783, - /* Overloads */ 14784, 14785, 14786, 14787, - /*Super Combat Pots */ 14871, 14869, 14867, 14865}; - - /** - * Constructs a new {@code PotionDecantingPlugin} {@code Object}. - */ - public PotionDecantingPlugin() { - super(POTIONS); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int i : POTIONS) { - addHandler(i, ITEM_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final Item item = event.getUsedItem(); - final Item other = event.getBaseItem(); - final String itemName = formatName(item); - final String otherName = formatName(other); - if (!itemName.equals(otherName) && (item.getId() != 229 && other.getId() != 229)) { - return false; - } - final int itemDose = getPotionDose(item); - final int otherDose = getPotionDose(other); - if (flagged(itemDose, otherDose)) { - return false; - } - - if (item.getId() == item.getNoteChange() || other.getId() == item.getNoteChange()) { - return false; - } - - final int[] newDoses = getDoses(itemDose, otherDose); - if (itemDose == 4 && otherDose == 0 || otherDose == 4 && itemDose == 0) { - player.getInventory().replace(getItem(item.getName().contains("Vial") ? otherName : itemName, newDoses[1]), item.getSlot()); - player.getInventory().replace(getItem(item.getName().contains("Vial") ? otherName : itemName, newDoses[1]), other.getSlot()); - player.getPacketDispatch().sendMessage("You decant the potion into two equal parts."); - } else { - player.getInventory().replace(getItem(item.getName().contains("Vial") ? otherName : itemName, newDoses[0]), other.getSlot()); - player.getInventory().replace(getItem(item.getName().contains("Vial") ? otherName : itemName, newDoses[1]), item.getSlot()); - player.getPacketDispatch().sendMessage("You have combined the liquid into " + (newDoses[0] == 0 ? newDoses[1] : newDoses[0]) + " doses."); - } - return true; - } - - /** - * Decants all the potions in a players inventory. - * @param player The player - */ - public static void decant(Player player) { - Map potions = new HashMap<>(); - - int totalVials = 0; - int newVials = 0; - int vialsToGive; - - for (Item item : player.getInventory().toArray()) { - if (item != null) { - if (getPotionDose(item) > 0 && isPot(item.getId())) { - String itemName = formatName(item); - item.setAmount(item.getAmount()); - totalVials += item.getAmount(); - if (player.getInventory().remove(item)) { - if (potions.get(itemName) != null) { - potions.put(itemName, potions.get(itemName) + (item.getAmount() * getPotionDose(item))); - } else { - potions.put(itemName, item.getAmount() * getPotionDose(item)); - } - } - } - } - } - - for (Map.Entry entry : potions.entrySet()) { - int amount = entry.getValue() / 4; - int excess = entry.getValue() % 4; - newVials += amount + (excess > 0 ? 1 : 0); - Item fourDose = getItem(entry.getKey(), 4); - player.getInventory().add(new Item(fourDose.getId() + 1, amount), player); - if (excess > 0) { - player.getInventory().add(new Item(getItem(entry.getKey(), excess).getId() + 1), player); - } - } - - vialsToGive = totalVials - newVials; - - if (vialsToGive > 0) { - player.getInventory().add(new Item(EMPTY_VIAL.getId() + 1, vialsToGive), player); - } - } - - /** - * Method used to format a name of a potion. - * @param item the item. - * @return the name. - */ - public static String formatName(final Item item) { - return item.getName().replace("(1)", "").replace("(2)", "").replace("(3)", "").replace("(4)", "").trim(); - } - - /** - * Method used to get the potion does between two potions. - * @param item the item. - * @return the doses. - */ - public static int getPotionDose(final Item item) { - return item.getName().contains("(1)") ? 1 : item.getName().contains("(2)") ? 2 : item.getName().contains("(3)") ? 3 : item.getName().contains("(4)") ? 4 : 0; - } - - /** - * Method used to check if a new potion ca be created based on the doses. - * @param itemDose the item dose. - * @param otherDose the other dose. - * @return True if flagged. - */ - public boolean flagged(final int itemDose, final int otherDose) { - return (itemDose == 4 && otherDose != 0 || itemDose != 4 && otherDose == 0 || itemDose == 4 && otherDose == 3 || itemDose == 0 && otherDose != 4) || (otherDose == 4 && itemDose != 0 || otherDose != 4 && itemDose == 0 || otherDose == 4 && itemDose == 3 || otherDose == 0 && itemDose != 4); - } - - /** - * Method used to return the doses of the new items. - * @param itemDose the item dose. - * @param otherDose the other dose. - * @return the new doses. - */ - public int[] getDoses(final int itemDose, final int otherDose) { - return itemDose == 1 && otherDose == 1 ? new int[] { 2, 0 } : itemDose == 3 && otherDose == 3 ? new int[] { 4, 2 } : itemDose == 2 && otherDose == 2 ? new int[] { 4, 0 } : itemDose == 2 && otherDose == 3 ? new int[] { 4, 1 } : itemDose == 1 && otherDose == 2 ? new int[] { 0, 3 } : itemDose == 4 && otherDose == 0 ? new int[] { 2, 2 } : itemDose == 1 && otherDose == 3 ? new int[] { 0, 4 } : otherDose == 2 && itemDose == 3 ? new int[] { 1, 4 } : otherDose == 1 && itemDose == 2 ? new int[] { 0, 3 } : otherDose == 4 && itemDose == 0 ? new int[] { 2, 2 } : otherDose == 1 && itemDose == 3 ? new int[] { 0, 4 } : itemDose == 0 && otherDose == 4 ? new int[] { 2, 2 } : otherDose == 4 && itemDose == 0 ? new int[] { 2, 2 } : new int[] { 0, 0 }; - } - - /** - * Method used to get the new item. - * @param name the name. - * @param dose the dose of the new item. - * @return the new item. - */ - public static Item getItem(String name, int dose) { - ItemDefinition def = null; - name += (oldSchool(name) ? " " : "")+"(" + dose + ")"; - if (dose == 0) { - return EMPTY_VIAL; - } - for (int id : POTIONS) { - def = ItemDefinition.forId(id); - if (def.getName().equals(name)) { - return new Item(id); - } - } - return null; - } - - /** - * Checks if an item is a pot. - * @param itemId The item id. - * @return true if so. - */ - public static boolean isPot(int itemId) { - for (int i : POTIONS) { - if (itemId == i || itemId == (i + 1)) { - return true; - } - } - return false; - } - - /** - * If an item is oldschool... add an extra space. - * @param name - * @return - * @author Jagex - */ - private static boolean oldSchool(String name){ - if(name.contains("xtended") || name.contains("uper rangin") || name.contains("uper magic") || name.contains("verload")){ - return true; - } - return false; - } -} diff --git a/Server/src/main/content/region/desert/nardah/dialogue/ZahurDialoguePlugin.java b/Server/src/main/content/region/desert/nardah/dialogue/ZahurDialoguePlugin.java new file mode 100644 index 000000000..2907db505 --- /dev/null +++ b/Server/src/main/content/region/desert/nardah/dialogue/ZahurDialoguePlugin.java @@ -0,0 +1,90 @@ +package content.region.desert.nardah.handlers; + +import core.game.node.item.Item; +import core.cache.def.impl.NPCDefinition; +import core.plugin.Initializable; +import core.game.dialogue.DialoguePlugin; +import core.game.dialogue.FacialExpression; +import core.game.interaction.OptionHandler; +import core.game.node.Node; +import core.game.node.entity.player.Player; +import core.plugin.Plugin; +import core.plugin.ClassScanner; +import java.util.List; +import kotlin.Pair; + +import static core.api.ContentAPIKt.decantContainer; + +/** + * Handles the zahur dialogue. + * @author Empathy + */ +@Initializable +public class ZahurDialoguePlugin extends DialoguePlugin { + /** + * Constructs a new {@code ZahurDialoguePlugin} {@code Object}. + */ + public ZahurDialoguePlugin() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code ZahurDialoguePlugin} {@code Object}. + * @param player the player. + */ + public ZahurDialoguePlugin(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new ZahurDialoguePlugin(player); + } + + @Override + public boolean open(Object... args) { + npc("I can combine your potion vials to try and make", "the potions fit into fewer vials. This service is free.", "Would you like to do this?"); + stage = 1; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 1: + options("Yes", "No"); + stage = 2; + break; + case 2: + if (buttonId == 1) { + Pair,List> decantResult = decantContainer(player.getInventory()); + List toRemove = decantResult.getFirst(); + List toAdd = decantResult.getSecond(); + + for (Item item : toRemove) { + player.getInventory().remove(item); + } + + for (Item item : toAdd) { + player.getInventory().add(item); + } + + npc("There, all done."); + } + stage = 3; + break; + case 3: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 3037 }; + } + +} diff --git a/Server/src/main/content/region/desert/nardah/handlers/ZahurOptionPlugin.java b/Server/src/main/content/region/desert/nardah/handlers/ZahurOptionPlugin.java deleted file mode 100644 index 11318c265..000000000 --- a/Server/src/main/content/region/desert/nardah/handlers/ZahurOptionPlugin.java +++ /dev/null @@ -1,101 +0,0 @@ -package content.region.desert.nardah.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.plugin.Initializable; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -import content.global.skill.herblore.PotionDecantingPlugin; - -/** - * Handles the zahur options. - * @author Empathy - * - */ -@Initializable -public class ZahurOptionPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(3037).getHandlers().put("option:combine", this); - ClassScanner.definePlugin(new ZahurDialoguePlugin()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - PotionDecantingPlugin.decant(player); - player.getDialogueInterpreter().sendDialogues(node.asNpc(), FacialExpression.HALF_GUILTY, "There, all done."); - return true; - } - - - /** - * Handles the Zahur Dialogue. - * @author Empathy - * - */ - public class ZahurDialoguePlugin extends DialoguePlugin { - - /** - * Constructs a new {@code ZahurDialoguePlugin} {@code Object}. - */ - public ZahurDialoguePlugin() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ZahurDialoguePlugin} {@code Object}. - * @param player the player. - */ - public ZahurDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ZahurDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - npc("I can combine your potion vials to try and make", "the potions fit into fewer vials. This service is free.", "Would you like to do this?"); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - options("Yes", "No"); - stage = 2; - break; - case 2: - if (buttonId == 1) { - PotionDecantingPlugin.decant(player); - npc("There, all done."); - } - stage = 3; - break; - case 3: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 3037 }; - } - - } -} diff --git a/Server/src/main/core/api/ContentAPI.kt b/Server/src/main/core/api/ContentAPI.kt index 7b40daee6..16b22952f 100644 --- a/Server/src/main/core/api/ContentAPI.kt +++ b/Server/src/main/core/api/ContentAPI.kt @@ -52,6 +52,7 @@ import core.game.ge.GrandExchangeRecords import core.game.interaction.InteractionListeners import content.global.handlers.iface.ge.StockMarket import content.global.skill.slayer.SlayerManager +import content.data.consumables.* import core.game.activity.Cutscene import core.game.interaction.Clocks import core.game.interaction.QueueStrength @@ -65,8 +66,10 @@ import core.game.world.GameWorld import core.game.world.GameWorld.Pulser import core.game.world.map.path.ProjectilePathfinder import core.game.world.repository.Repository +import core.game.consumable.* import core.tools.Log import core.tools.tick +import java.util.regex.* import kotlin.math.absoluteValue /** @@ -2411,4 +2414,41 @@ fun modPrayerPoints(player: Player, amount: Double) { else return } +/** + * Iterates a container and "decants" every potion type in the container. (Decanting is the process of condensing multiple variable-dose potions into the minimum number of max-dose potions) + * @param container the container to iterate + * @return a pair where the first element is the list of items to be removed, and the second element is the list of items to add. +*/ +fun decantContainer (container: core.game.container.Container) : Pair, List> { + val doseCount = HashMap() + val toRemove = ArrayList() + val toAdd = ArrayList() + val doseRegex = Pattern.compile("(\\([0-9]\\))") //Matches "(2)" in "Defense potion (2)" + + for (item in container.toArray()) { + if (item == null) continue + val potionType = Consumables.getConsumableById(item.id)?.consumable as? Potion ?: continue + val matcher = doseRegex.matcher(item.name) + if (!matcher.find()) continue + val dosage = matcher.group(1).replace("(","").replace(")","").toIntOrNull() ?: continue + doseCount[potionType] = (doseCount[potionType] ?: 0) + dosage + toRemove.add(item) + } + + for ((consumable, count) in doseCount) { + val maxDose = consumable.ids.size + val totalMaxDosePotions = count / maxDose + val remainderDose = count % maxDose + if (totalMaxDosePotions > 0) + toAdd.add(Item(consumable.ids[0], totalMaxDosePotions)) + if (remainderDose > 0) + toAdd.add(Item(consumable.ids[consumable.ids.size - remainderDose])) + } + + val emptyVials = toRemove.size - toAdd.sumBy { it.amount } + if (emptyVials > 0) + toAdd.add(Item(229, emptyVials)) + return Pair,List>(toRemove, toAdd) +} + private class ContentAPI