From a52837975e95ba70e1b8fd8aeef05894a725eb71 Mon Sep 17 00:00:00 2001 From: Chivvon Date: Fri, 6 Apr 2018 16:50:27 +0200 Subject: [PATCH] Port Herblore plugin to Kotlin --- game/plugin/skills/herblore/build.gradle | 10 +++ .../herblore/src/CrushIngredientAction.kt | 39 ++++++++++ game/plugin/skills/herblore/src/Herb.kt | 34 +++++++++ .../skills/herblore/src/IdentifyHerbAction.kt | 49 +++++++++++++ game/plugin/skills/herblore/src/Ingredient.kt | 60 +++++++++++++++ .../herblore/src/MakeFinishedPotionAction.kt | 50 +++++++++++++ .../src/MakeUnfinishedPotionAction.kt | 47 ++++++++++++ game/plugin/skills/herblore/src/Potion.kt | 73 +++++++++++++++++++ .../skills/herblore/src/herblore.plugin.kts | 48 ++++++++++++ 9 files changed, 410 insertions(+) create mode 100644 game/plugin/skills/herblore/build.gradle create mode 100644 game/plugin/skills/herblore/src/CrushIngredientAction.kt create mode 100644 game/plugin/skills/herblore/src/Herb.kt create mode 100644 game/plugin/skills/herblore/src/IdentifyHerbAction.kt create mode 100644 game/plugin/skills/herblore/src/Ingredient.kt create mode 100644 game/plugin/skills/herblore/src/MakeFinishedPotionAction.kt create mode 100644 game/plugin/skills/herblore/src/MakeUnfinishedPotionAction.kt create mode 100644 game/plugin/skills/herblore/src/Potion.kt create mode 100644 game/plugin/skills/herblore/src/herblore.plugin.kts diff --git a/game/plugin/skills/herblore/build.gradle b/game/plugin/skills/herblore/build.gradle new file mode 100644 index 000000000..6dc08aac4 --- /dev/null +++ b/game/plugin/skills/herblore/build.gradle @@ -0,0 +1,10 @@ +plugin { + name = "herblore_skill" + authors = [ + "Chivvon", + "Chris Fletcher", + "Major" + ] + + dependencies = ["api"] +} diff --git a/game/plugin/skills/herblore/src/CrushIngredientAction.kt b/game/plugin/skills/herblore/src/CrushIngredientAction.kt new file mode 100644 index 000000000..bf9cb2834 --- /dev/null +++ b/game/plugin/skills/herblore/src/CrushIngredientAction.kt @@ -0,0 +1,39 @@ +import org.apollo.game.action.ActionBlock +import org.apollo.game.action.AsyncAction +import org.apollo.game.model.Animation +import org.apollo.game.model.entity.Player +import java.util.Objects + +class CrushIngredientAction( + player: Player, + private val ingredient: CrushableIngredient +) : AsyncAction(0, true, player) { + + override fun action(): ActionBlock = { + mob.playAnimation(GRINDING_ANIM) + wait(pulses = 1) + + val inventory = mob.inventory + if (inventory.remove(ingredient.uncrushed)) { + inventory.add(ingredient.id) + + mob.sendMessage("You carefully grind the ${ingredient.uncrushedName} to dust.") + } + + stop() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as CrushIngredientAction + return mob == other.mob && ingredient == other.ingredient + } + + override fun hashCode(): Int = Objects.hash(mob, ingredient) + + private companion object { + private val GRINDING_ANIM = Animation(364) + } +} \ No newline at end of file diff --git a/game/plugin/skills/herblore/src/Herb.kt b/game/plugin/skills/herblore/src/Herb.kt new file mode 100644 index 000000000..c80e1edff --- /dev/null +++ b/game/plugin/skills/herblore/src/Herb.kt @@ -0,0 +1,34 @@ +import org.apollo.game.plugin.api.Definitions + +enum class Herb( + val identified: Int, + val unidentified: Int, + val level: Int, + val experience: Double +) { + GUAM_LEAF(identified = 249, unidentified = 199, level = 1, experience = 2.5), + MARRENTILL(identified = 251, unidentified = 201, level = 5, experience = 3.8), + TARROMIN(identified = 253, unidentified = 203, level = 11, experience = 5.0), + HARRALANDER(identified = 255, unidentified = 205, level = 20, experience = 6.3), + RANARR(identified = 257, unidentified = 207, level = 25, experience = 7.5), + TOADFLAX(identified = 2998, unidentified = 2998, level = 30, experience = 8.0), + IRIT_LEAF(identified = 259, unidentified = 209, level = 40, experience = 8.8), + AVANTOE(identified = 261, unidentified = 211, level = 48, experience = 10.0), + KWUARM(identified = 263, unidentified = 213, level = 54, experience = 11.3), + SNAPDRAGON(identified = 3000, unidentified = 3051, level = 59, experience = 11.8), + CADANTINE(identified = 265, unidentified = 215, level = 65, experience = 12.5), + LANTADYME(identified = 2481, unidentified = 2485, level = 67, experience = 13.1), + DWARF_WEED(identified = 267, unidentified = 217, level = 70, experience = 13.8), + TORSTOL(identified = 269, unidentified = 219, level = 75, experience = 15.0); + + val identifiedName: String = Definitions.item(identified)!!.name + + companion object { + private val identified = Herb.values().map(Herb::identified).toHashSet() + private val herbs = Herb.values().associateBy(Herb::unidentified) + + operator fun get(id: Int): Herb? = herbs[id] + internal fun Int.isUnidentified(): Boolean = this in herbs + internal fun Int.isIdentified(): Boolean = this in identified + } +} \ No newline at end of file diff --git a/game/plugin/skills/herblore/src/IdentifyHerbAction.kt b/game/plugin/skills/herblore/src/IdentifyHerbAction.kt new file mode 100644 index 000000000..a3b11ac90 --- /dev/null +++ b/game/plugin/skills/herblore/src/IdentifyHerbAction.kt @@ -0,0 +1,49 @@ + +import org.apollo.game.action.ActionBlock +import org.apollo.game.action.AsyncAction +import org.apollo.game.model.entity.Player +import org.apollo.game.plugin.api.herblore +import org.apollo.util.LanguageUtil +import java.util.Objects + +class IdentifyHerbAction( + player: Player, + private val slot: Int, + private val herb: Herb +) : AsyncAction(0, true, player) { + + override fun action(): ActionBlock = { + if (mob.herblore.current < herb.level) { + mob.sendMessage("You need a Herblore level of ${herb.level} to clean this herb.") + stop() + } + + val inventory = mob.inventory + + if (inventory.removeSlot(slot, 1) > 0) { + inventory.add(herb.identified) + mob.herblore.experience += herb.experience + + val name = herb.identifiedName + val article = LanguageUtil.getIndefiniteArticle(name) + + mob.sendMessage("You identify the herb as $article $name.") + } + + stop() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as IdentifyHerbAction + return mob == other.mob && herb == other.herb && slot == other.slot + } + + override fun hashCode(): Int = Objects.hash(mob, herb, slot) + + companion object { + const val IDENTIFY_OPTION = 1 + } +} \ No newline at end of file diff --git a/game/plugin/skills/herblore/src/Ingredient.kt b/game/plugin/skills/herblore/src/Ingredient.kt new file mode 100644 index 000000000..3784f0905 --- /dev/null +++ b/game/plugin/skills/herblore/src/Ingredient.kt @@ -0,0 +1,60 @@ +import CrushableIngredient.Companion.isCrushable +import NormalIngredient.Companion.isNormalIngredient +import org.apollo.game.plugin.api.Definitions + +/** + * A secondary ingredient in a potion. + */ +interface Ingredient { + val id: Int + + companion object { + internal fun Int.isIngredient(): Boolean = isNormalIngredient() || isCrushable() + } +} + +enum class CrushableIngredient(val uncrushed: Int, override val id: Int) : Ingredient { + UNICORN_HORN(uncrushed = 237, id = 235), + DRAGON_SCALE(uncrushed = 243, id = 241), + CHOCOLATE_BAR(uncrushed = 1973, id = 1975), + BIRDS_NEST(uncrushed = 5075, id = 6693); + + val uncrushedName: String = Definitions.item(uncrushed)!!.name + + companion object { + private const val PESTLE_AND_MORTAR = 233 + private const val KNIFE = 5605 + + private val ingredients = CrushableIngredient.values().associateBy(CrushableIngredient::uncrushed) + operator fun get(id: Int): CrushableIngredient? = ingredients[id] + + internal fun Int.isCrushable(): Boolean = this in ingredients + internal fun Int.isGrindingTool(): Boolean = this == KNIFE || this == PESTLE_AND_MORTAR + } +} + +enum class NormalIngredient(override val id: Int) : Ingredient { + EYE_NEWT(id = 221), + RED_SPIDERS_EGGS(id = 223), + LIMPWURT_ROOT(id = 225), + SNAPE_GRASS(id = 231), + WHITE_BERRIES(id = 239), + WINE_ZAMORAK(id = 245), + JANGERBERRIES(id = 247), + TOADS_LEGS(id = 2152), + MORT_MYRE_FUNGI(id = 2970), + POTATO_CACTUS(id = 3138), + PHOENIX_FEATHER(id = 4621), + FROG_SPAWN(id = 5004), + PAPAYA_FRUIT(id = 5972), + POISON_IVY_BERRIES(id = 6018), + YEW_ROOTS(id = 6049), + MAGIC_ROOTS(id = 6051); + + companion object { + private val ingredients = NormalIngredient.values().associateBy(NormalIngredient::id) + operator fun get(id: Int): NormalIngredient? = ingredients[id] + + internal fun Int.isNormalIngredient(): Boolean = this in ingredients + } +} \ No newline at end of file diff --git a/game/plugin/skills/herblore/src/MakeFinishedPotionAction.kt b/game/plugin/skills/herblore/src/MakeFinishedPotionAction.kt new file mode 100644 index 000000000..37cc56d5f --- /dev/null +++ b/game/plugin/skills/herblore/src/MakeFinishedPotionAction.kt @@ -0,0 +1,50 @@ +import org.apollo.game.action.ActionBlock +import org.apollo.game.action.AsyncAction +import org.apollo.game.model.Animation +import org.apollo.game.model.entity.Player +import org.apollo.game.plugin.api.herblore +import java.util.Objects + +class MakeFinishedPotionAction( + player: Player, + private val potion: FinishedPotion +) : AsyncAction(1, true, player) { + + override fun action(): ActionBlock = { + val level = mob.herblore.current + + if (level < potion.level) { + mob.sendMessage("You need a Herblore level of ${potion.level} to make this.") + stop() + } + + val unfinished = potion.unfinished.id + val inventory = mob.inventory + + if (inventory.contains(unfinished) && inventory.contains(potion.ingredient)) { + inventory.remove(unfinished) + inventory.remove(potion.ingredient) + + mob.playAnimation(MIXING_ANIMATION) + + inventory.add(potion.id) + mob.herblore.experience += potion.experience + + mob.sendMessage("You mix the ${potion.ingredientName} into your potion.") + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as MakeFinishedPotionAction + return mob == other.mob && potion == other.potion + } + + override fun hashCode(): Int = Objects.hash(mob, potion) + + private companion object { + private val MIXING_ANIMATION = Animation(363) + } +} \ No newline at end of file diff --git a/game/plugin/skills/herblore/src/MakeUnfinishedPotionAction.kt b/game/plugin/skills/herblore/src/MakeUnfinishedPotionAction.kt new file mode 100644 index 000000000..03bbd5a7d --- /dev/null +++ b/game/plugin/skills/herblore/src/MakeUnfinishedPotionAction.kt @@ -0,0 +1,47 @@ +import org.apollo.game.action.ActionBlock +import org.apollo.game.action.AsyncAction +import org.apollo.game.model.Animation +import org.apollo.game.model.entity.Player +import org.apollo.game.plugin.api.herblore +import java.util.Objects + +class MakeUnfinishedPotionAction( + player: Player, + private val potion: UnfinishedPotion +) : AsyncAction(1, true, player) { + + override fun action(): ActionBlock = { + val level = mob.herblore.current + + if (level < potion.level) { + mob.sendMessage("You need a Herblore level of ${potion.level} to make this.") + stop() + } + + val inventory = mob.inventory + + if (inventory.contains(VIAL_OF_WATER) && inventory.contains(potion.herb)) { + inventory.remove(VIAL_OF_WATER) + inventory.remove(potion.herb) + + mob.playAnimation(MIXING_ANIMATION) + + inventory.add(potion.id) + mob.sendMessage("You put the ${potion.herbName} into the vial of water.") + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as MakeUnfinishedPotionAction + return mob == other.mob && potion == other.potion + } + + override fun hashCode(): Int = Objects.hash(mob, potion) + + private companion object { + private val MIXING_ANIMATION = Animation(363) + } +} \ No newline at end of file diff --git a/game/plugin/skills/herblore/src/Potion.kt b/game/plugin/skills/herblore/src/Potion.kt new file mode 100644 index 000000000..299a82987 --- /dev/null +++ b/game/plugin/skills/herblore/src/Potion.kt @@ -0,0 +1,73 @@ +import CrushableIngredient.CHOCOLATE_BAR +import CrushableIngredient.DRAGON_SCALE +import CrushableIngredient.UNICORN_HORN +import NormalIngredient.* +import UnfinishedPotion.* +import org.apollo.game.plugin.api.Definitions + +const val VIAL_OF_WATER = 227 + +enum class UnfinishedPotion(val id: Int, herb: Herb, val level: Int) { + GUAM(id = 91, herb = Herb.GUAM_LEAF, level = 1), + MARRENTILL(id = 93, herb = Herb.MARRENTILL, level = 5), + TARROMIN(id = 95, herb = Herb.TARROMIN, level = 12), + HARRALANDER(id = 97, herb = Herb.HARRALANDER, level = 22), + RANARR(id = 99, herb = Herb.RANARR, level = 30), + TOADFLAX(id = 3002, herb = Herb.TOADFLAX, level = 34), + IRIT(id = 101, herb = Herb.IRIT_LEAF, level = 45), + AVANTOE(id = 103, herb = Herb.AVANTOE, level = 50), + KWUARM(id = 105, herb = Herb.KWUARM, level = 55), + SNAPDRAGON(id = 3004, herb = Herb.SNAPDRAGON, level = 63), + CADANTINE(id = 107, herb = Herb.CADANTINE, level = 66), + LANTADYME(id = 2483, herb = Herb.LANTADYME, level = 69), + DWARF_WEED(id = 109, herb = Herb.DWARF_WEED, level = 72), + TORSTOL(id = 111, herb = Herb.TORSTOL, level = 78); + + val herb = herb.identified + val herbName: String = Definitions.item(herb.identified)!!.name + + companion object { + private val ids = values().map(UnfinishedPotion::id).toHashSet() + private val potions = values().associateBy(UnfinishedPotion::herb) + + operator fun get(id: Int) = potions[id] + internal fun Int.isUnfinished(): Boolean = this in ids + } +} + +enum class FinishedPotion( + val id: Int, + val unfinished: UnfinishedPotion, + ingredient: Ingredient, + val level: Int, + val experience: Double +) { + ATTACK(id = 121, unfinished = GUAM, ingredient = EYE_NEWT, level = 1, experience = 25.0), + ANTIPOISON(id = 175, unfinished = MARRENTILL, ingredient = UNICORN_HORN, level = 5, experience = 37.5), + STRENGTH(id = 115, unfinished = TARROMIN, ingredient = LIMPWURT_ROOT, level = 12, experience = 50.0), + RESTORE(id = 127, unfinished = HARRALANDER, ingredient = RED_SPIDERS_EGGS, level = 18, experience = 62.5), + ENERGY(id = 3010, unfinished = HARRALANDER, ingredient = CHOCOLATE_BAR, level = 26, experience = 67.5), + DEFENCE(id = 133, unfinished = RANARR, ingredient = WHITE_BERRIES, level = 30, experience = 75.0), + AGILITY(id = 3034, unfinished = TOADFLAX, ingredient = TOADS_LEGS, level = 34, experience = 80.0), + PRAYER(id = 139, unfinished = RANARR, ingredient = SNAPE_GRASS, level = 38, experience = 87.5), + SUPER_ATTACK(id = 145, unfinished = IRIT, ingredient = EYE_NEWT, level = 45, experience = 100.0), + SUPER_ANTIPOISON(id = 181, unfinished = IRIT, ingredient = UNICORN_HORN, level = 48, experience = 106.3), + FISHING(id = 151, unfinished = AVANTOE, ingredient = SNAPE_GRASS, level = 50, experience = 112.5), + SUPER_ENERGY(id = 3018, unfinished = AVANTOE, ingredient = MORT_MYRE_FUNGI, level = 52, experience = 117.5), + SUPER_STRENGTH(id = 157, unfinished = KWUARM, ingredient = LIMPWURT_ROOT, level = 55, experience = 125.0), + WEAPON_POISON(id = 187, unfinished = KWUARM, ingredient = DRAGON_SCALE, level = 60, experience = 137.5), + SUPER_RESTORE(id = 3026, unfinished = SNAPDRAGON, ingredient = RED_SPIDERS_EGGS, level = 63, experience = 142.5), + SUPER_DEFENCE(id = 163, unfinished = CADANTINE, ingredient = WHITE_BERRIES, level = 66, experience = 150.0), + ANTIFIRE(id = 2428, unfinished = LANTADYME, ingredient = DRAGON_SCALE, level = 69, experience = 157.5), + RANGING(id = 169, unfinished = DWARF_WEED, ingredient = WINE_ZAMORAK, level = 72, experience = 162.5), + MAGIC(id = 3042, unfinished = LANTADYME, ingredient = POTATO_CACTUS, level = 76, experience = 172.5), + ZAMORAK_BREW(id = 189, unfinished = TORSTOL, ingredient = JANGERBERRIES, level = 78, experience = 175.0); + + val ingredientName = Definitions.item(ingredient.id)!!.name.toLowerCase() + val ingredient = ingredient.id + + companion object { + private val potions = FinishedPotion.values().associateBy { Pair(it.unfinished.id, it.ingredient) } + operator fun get(key: Pair) = potions[key] + } +} diff --git a/game/plugin/skills/herblore/src/herblore.plugin.kts b/game/plugin/skills/herblore/src/herblore.plugin.kts new file mode 100644 index 000000000..f575a6532 --- /dev/null +++ b/game/plugin/skills/herblore/src/herblore.plugin.kts @@ -0,0 +1,48 @@ + +import CrushableIngredient.Companion.isCrushable +import CrushableIngredient.Companion.isGrindingTool +import Herb.Companion.isIdentified +import Herb.Companion.isUnidentified +import Ingredient.Companion.isIngredient +import UnfinishedPotion.Companion.isUnfinished +import org.apollo.game.message.impl.ItemOnItemMessage +import org.apollo.game.message.impl.ItemOptionMessage + +on { ItemOptionMessage::class } + .where { option == IdentifyHerbAction.IDENTIFY_OPTION && id.isUnidentified() } + .then { player -> + val unidentified = Herb[id]!! + + player.startAction(IdentifyHerbAction(player, slot, unidentified)) + terminate() + } + +on { ItemOnItemMessage::class } + .where { (id.isGrindingTool() && targetId.isCrushable() || id.isCrushable() && targetId.isGrindingTool()) } + .then { player -> + val crushableId = if (id.isCrushable()) id else targetId + val raw = CrushableIngredient[crushableId]!! + + player.startAction(CrushIngredientAction(player, raw)) + terminate() + } + +on { ItemOnItemMessage::class } + .where { id == VIAL_OF_WATER && targetId.isIdentified() || id.isIdentified() && targetId == VIAL_OF_WATER } + .then { player -> + val herbId = if (id.isIdentified()) id else targetId + val unfinished = UnfinishedPotion[herbId]!! + + player.startAction(MakeUnfinishedPotionAction(player, unfinished)) + terminate() + } + +on { ItemOnItemMessage::class } + .where { id.isUnfinished() && targetId.isIngredient() || id.isIngredient() && targetId.isUnfinished() } + .then { player -> + val key = if (id.isUnfinished()) Pair(id, targetId) else Pair(targetId, id) + val finished = FinishedPotion[key]!! + + player.startAction(MakeFinishedPotionAction(player, finished)) + terminate() + } \ No newline at end of file