From b2f48815bfe028fd86161b44ab3aab11d16dd4a2 Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 27 Mar 2018 20:06:35 +0100 Subject: [PATCH 1/6] Fix nitpicks in Fishing plugin --- game/plugin/skills/fishing/build.gradle | 4 +- game/plugin/skills/fishing/src/fishing.kt | 112 ++++++++---------- .../skills/fishing/src/fishing.plugin.kts | 101 ++++++++-------- game/plugin/skills/fishing/src/spots.kts | 1 - 4 files changed, 102 insertions(+), 116 deletions(-) diff --git a/game/plugin/skills/fishing/build.gradle b/game/plugin/skills/fishing/build.gradle index 6f870b9d..a7c3c5df 100644 --- a/game/plugin/skills/fishing/build.gradle +++ b/game/plugin/skills/fishing/build.gradle @@ -6,6 +6,8 @@ plugin { "tlf30" ] dependencies = [ - "util:lookup", "entity:spawn", "api" + "api", + "entity:spawn", + "util:lookup" ] } diff --git a/game/plugin/skills/fishing/src/fishing.kt b/game/plugin/skills/fishing/src/fishing.kt index e045a445..b5994935 100644 --- a/game/plugin/skills/fishing/src/fishing.kt +++ b/game/plugin/skills/fishing/src/fishing.kt @@ -1,50 +1,54 @@ package org.apollo.game.plugin.skills.fishing -import org.apollo.cache.def.ItemDefinition import org.apollo.game.model.Animation +import org.apollo.game.plugin.api.Definitions +import org.apollo.game.plugin.api.rand import org.apollo.game.plugin.skills.fishing.Fish.* import org.apollo.game.plugin.skills.fishing.FishingTool.* -import java.util.Random /** * A fish that can be gathered using the fishing skill. */ enum class Fish(val id: Int, val level: Int, val experience: Double) { - SHRIMP(317, 1, 10.0), - SARDINE(327, 5, 20.0), - MACKEREL(353, 16, 20.0), - HERRING(345, 10, 30.0), - ANCHOVY(321, 15, 40.0), - TROUT(335, 20, 50.0), - COD(341, 23, 45.0), - PIKE(349, 25, 60.0), - SALMON(331, 30, 70.0), - TUNA(359, 35, 80.0), - LOBSTER(377, 40, 90.0), - BASS(363, 46, 100.0), - SWORDFISH(371, 50, 100.0), - SHARK(383, 76, 110.0); + SHRIMP(id = 317, level = 1, experience = 10.0), + SARDINE(id = 327, level = 5, experience = 20.0), + MACKEREL(id = 353, level = 16, experience = 20.0), + HERRING(id = 345, level = 10, experience = 30.0), + ANCHOVY(id = 321, level = 15, experience = 40.0), + TROUT(id = 335, level = 20, experience = 50.0), + COD(id = 341, level = 23, experience = 45.0), + PIKE(id = 349, level = 25, experience = 60.0), + SALMON(id = 331, level = 30, experience = 70.0), + TUNA(id = 359, level = 35, experience = 80.0), + LOBSTER(id = 377, level = 40, experience = 90.0), + BASS(id = 363, level = 46, experience = 100.0), + SWORDFISH(id = 371, level = 50, experience = 100.0), + SHARK(id = 383, level = 76, experience = 110.0); /** * The name of this fish, formatted so it can be inserted into a message. */ - val formattedName = ItemDefinition.lookup(id).name.toLowerCase() + val formattedName = Definitions.item(id)!!.name.toLowerCase() + // TODO this leads to incorrect messages, e.g. 'You catch a raw shrimps'. } /** * A tool used to gather [Fish] from a [FishingSpot]. */ -enum class FishingTool(val id: Int, animation: Int, val message: String, val bait: Int, val baitName: String?) { - LOBSTER_CAGE(301, 619, "You attempt to catch a lobster..."), - SMALL_NET(303, 620, "You cast out your net..."), - BIG_NET(305, 620, "You cast out your net..."), - HARPOON(311, 618, "You start harpooning fish..."), - FISHING_ROD(307, 622, "You attempt to catch a fish...", 313, "feathers"), - FLY_FISHING_ROD(309, 622, "You attempt to catch a fish...", 314, "fishing bait"); - - @Suppress("unused") // IntelliJ bug, doesn't detect that this constructor is used - constructor(id: Int, animation: Int, message: String) : this(id, animation, message, -1, null) +enum class FishingTool( + val message: String, + val id: Int, + animation: Int, + val bait: Int = -1, + val baitName: String? = null +) { + LOBSTER_CAGE("You attempt to catch a lobster...", id = 301, animation = 619), + SMALL_NET("You cast out your net...", id = 303, animation = 620), + BIG_NET("You cast out your net...", id = 305, animation = 620), + HARPOON("You start harpooning fish...", id = 311, animation = 618), + FISHING_ROD("You attempt to catch a fish...", id = 307, animation = 622, bait = 313, baitName = "feathers"), + FLY_FISHING_ROD("You attempt to catch a fish...", id = 309, animation = 622, bait = 314, baitName = "fishing bait"); /** * The [Animation] played when fishing with this tool. @@ -54,7 +58,7 @@ enum class FishingTool(val id: Int, animation: Int, val message: String, val bai /** * The name of this tool, formatted so it can be inserted into a message. */ - val formattedName = ItemDefinition.lookup(id).name.toLowerCase() + val formattedName = Definitions.item(id)!!.name.toLowerCase() } @@ -68,14 +72,12 @@ enum class FishingSpot(val npc: Int, private val first: Option, private val seco NET_ROD(316, Option.of(SMALL_NET, SHRIMP, ANCHOVY), Option.of(FISHING_ROD, SARDINE, HERRING)); companion object { - private val FISHING_SPOTS = FishingSpot.values().associateBy({ it.npc }, { it }) /** * Returns the [FishingSpot] with the specified [id], or `null` if the spot does not exist. */ fun lookup(id: Int): FishingSpot? = FISHING_SPOTS[id] - } /** @@ -94,19 +96,6 @@ enum class FishingSpot(val npc: Int, private val first: Option, private val seco */ sealed class Option { - companion object { - - fun of(tool: FishingTool, primary: Fish): Option = Single(tool, primary) - - fun of(tool: FishingTool, primary: Fish, secondary: Fish): Option { - return when { - primary.level < secondary.level -> Pair(tool, primary, secondary) - else -> Pair(tool, secondary, primary) - } - } - - } - /** * The tool used to obtain fish */ @@ -131,35 +120,38 @@ enum class FishingSpot(val npc: Int, private val first: Option, private val seco override val level = primary.level override fun sample(level: Int): Fish = primary - } /** * A [FishingSpot] [Option] that can provide a two different types of fish. */ private data class Pair(override val tool: FishingTool, val primary: Fish, val secondary: Fish) : Option() { - - companion object { - - val random = Random() - - /** - * The weighting factor that causes the lower-level fish to be returned more frequently. - */ - const val WEIGHTING = 70 - - } - override val level = Math.min(primary.level, secondary.level) override fun sample(level: Int): Fish { - if (secondary.level > level) { - return primary + return if (level < secondary.level || rand(100) < WEIGHTING) { + primary + } else { + secondary } + } + private companion object { + /** + * The weighting factor that causes the lower-level fish to be returned more frequently. + */ + private const val WEIGHTING = 70 + } + } + + companion object { + + fun of(tool: FishingTool, primary: Fish): Option = Single(tool, primary) + + fun of(tool: FishingTool, primary: Fish, secondary: Fish): Option { return when { - random.nextInt(100) < WEIGHTING -> primary - else -> secondary + primary.level < secondary.level -> Pair(tool, primary, secondary) + else -> Pair(tool, secondary, primary) } } diff --git a/game/plugin/skills/fishing/src/fishing.plugin.kts b/game/plugin/skills/fishing/src/fishing.plugin.kts index 56755edb..a2138bd9 100644 --- a/game/plugin/skills/fishing/src/fishing.plugin.kts +++ b/game/plugin/skills/fishing/src/fishing.plugin.kts @@ -6,44 +6,31 @@ import org.apollo.game.message.impl.NpcActionMessage import org.apollo.game.model.Position import org.apollo.game.model.entity.Player import org.apollo.game.plugin.api.fishing +import org.apollo.game.plugin.api.rand import org.apollo.game.plugin.skills.fishing.FishingSpot import org.apollo.game.plugin.skills.fishing.FishingTool import java.util.Objects -import java.util.Random // TODO: moving fishing spots, seaweed and caskets, evil bob +/** + * Intercepts the [NpcActionMessage] and starts a [FishingAction] if the npc + */ +on { NpcActionMessage::class } + .where { option == 1 || option == 3 } + .then { player -> + val entity = player.world.npcRepository[index] + val spot = FishingSpot.lookup(entity.id) ?: return@then + + val option = spot.option(option) + player.startAction(FishingAction(player, entity.position, option)) + + terminate() + } + class FishingAction(player: Player, position: Position, val option: FishingSpot.Option) : AsyncDistancedAction(0, true, player, position, SPOT_DISTANCE) { - companion object { - private const val SPOT_DISTANCE = 1 - private const val FISHING_DELAY = 4 - - /** - * The random number generator used by the fishing plugin. - */ - private val random = Random() - - /** - * Returns whether or not the catch was successful. - * TODO: We need to identify the correct algorithm for this - */ - private fun successfulCatch(level: Int, req: Int): Boolean = minOf(level - req + 5, 40) > random.nextInt(100) - - /** - * Returns whether or not the [Player] has (or does not need) bait. - */ - private fun hasBait(player: Player, bait: Int): Boolean = bait == -1 || player.inventory.contains(bait) - - /** - * @return if the player has the needed tool to fish at the spot. - */ - private fun hasTool(player: Player, tool: FishingTool): Boolean = player.equipment.contains(tool.id) || - player.inventory.contains(tool.id) - - } - /** * The [FishingTool] used for the fishing spot. */ @@ -75,10 +62,12 @@ class FishingAction(player: Player, position: Position, val option: FishingSpot. if (mob.inventory.freeSlots() == 0) { mob.inventory.forceCapacityExceeded() + mob.stopAnimation() stop() } else if (!hasBait(mob, tool.bait)) { mob.sendMessage("You need more ${tool.baitName} to fish at this spot.") + mob.stopAnimation() stop() } @@ -90,21 +79,17 @@ class FishingAction(player: Player, position: Position, val option: FishingSpot. * Verifies that the player can gather fish from the [FishingSpot] they clicked. */ private fun verify(): Boolean { - if (mob.fishing.current < option.level) { - mob.sendMessage("You need a fishing level of ${option.level} to fish at this spot.") - return false - } else if (!hasTool(mob, tool)) { - mob.sendMessage("You need a ${tool.formattedName} to fish at this spot.") - return false - } else if (!hasBait(mob, tool.bait)) { - mob.sendMessage("You need some ${tool.baitName} to fish at this spot.") - return false - } else if (mob.inventory.freeSlots() == 0) { - mob.inventory.forceCapacityExceeded() - return false + val current = mob.fishing.current + + when { + current < option.level -> mob.sendMessage("You need a fishing level of ${option.level} to fish at this spot.") + !hasTool(mob, tool) -> mob.sendMessage("You need a ${tool.formattedName} to fish at this spot.") + !hasBait(mob, tool.bait) -> mob.sendMessage("You need some ${tool.baitName} to fish at this spot.") + mob.inventory.freeSlots() == 0 -> mob.inventory.forceCapacityExceeded() + else -> return true } - return true + return false } override fun equals(other: Any?): Boolean { @@ -117,19 +102,27 @@ class FishingAction(player: Player, position: Position, val option: FishingSpot. override fun hashCode(): Int = Objects.hash(option, position, mob) -} + private companion object { + private const val SPOT_DISTANCE = 1 + private const val FISHING_DELAY = 4 -/** - * Intercepts the [NpcActionMessage] and starts a [FishingAction] if the npc - */ -on { NpcActionMessage::class } - .where { option == 1 || option == 3 } - .then { - val entity = it.world.npcRepository[index] - val spot = FishingSpot.lookup(entity.id) ?: return@then + /** + * Returns whether or not the catch was successful. + * TODO: We need to identify the correct algorithm for this + */ + private fun successfulCatch(level: Int, req: Int): Boolean = minOf(level - req + 5, 40) > rand(100) - val option = spot.option(option) - it.startAction(FishingAction(it, entity.position, option)) + /** + * Returns whether or not the [Player] has (or does not need) bait. + */ + private fun hasBait(player: Player, bait: Int): Boolean = bait == -1 || player.inventory.contains(bait) + + /** + * Returns whether or not the player has the required tool to fish at the spot. + */ + private fun hasTool(player: Player, tool: FishingTool): Boolean = player.equipment.contains(tool.id) || + player.inventory.contains(tool.id) - terminate() } + +} \ No newline at end of file diff --git a/game/plugin/skills/fishing/src/spots.kts b/game/plugin/skills/fishing/src/spots.kts index 46001275..014c5d13 100644 --- a/game/plugin/skills/fishing/src/spots.kts +++ b/game/plugin/skills/fishing/src/spots.kts @@ -163,7 +163,6 @@ register(NET_ROD, x = 2990, y = 3169) register(NET_ROD, x = 2986, y = 3176) // Shilo Village - register(ROD, x = 2855, y = 2974) register(ROD, x = 2865, y = 2972) register(ROD, x = 2860, y = 2972) From 45a0b43eee995d6050271f857e3a9524db468cef Mon Sep 17 00:00:00 2001 From: Major Date: Tue, 27 Mar 2018 20:07:31 +0100 Subject: [PATCH 2/6] Remove incorrect Gunnarsgrunn fishing spots The spots are actually on Tutorial Island and result in two fishing spots stacked on top of each other. --- game/plugin/skills/fishing/src/spots.kts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/game/plugin/skills/fishing/src/spots.kts b/game/plugin/skills/fishing/src/spots.kts index 014c5d13..b86b6e8f 100644 --- a/game/plugin/skills/fishing/src/spots.kts +++ b/game/plugin/skills/fishing/src/spots.kts @@ -125,10 +125,6 @@ register(ROD, x = 2385, y = 3422) register(ROD, x = 2384, y = 3419) register(ROD, x = 2383, y = 3417) -// Gunnarsgrunn -register(ROD, x = 3101, y = 3092) -register(ROD, x = 3103, y = 3092) - // Karamja register(NET_ROD, x = 2921, y = 3178) register(CAGE_HARPOON, x = 2923, y = 3179) From 173623b76b15f6e3f5b24a71bb3ef7fd14f3f90b Mon Sep 17 00:00:00 2001 From: Major Date: Wed, 28 Mar 2018 01:21:12 +0100 Subject: [PATCH 3/6] Fix nitpicks in Woodcutting plugin --- game/plugin/skills/woodcutting/src/axe.kt | 35 ++++--- game/plugin/skills/woodcutting/src/wood.kt | 95 +++++++------------ .../woodcutting/src/woodcutting.plugin.kts | 48 ++++------ 3 files changed, 73 insertions(+), 105 deletions(-) diff --git a/game/plugin/skills/woodcutting/src/axe.kt b/game/plugin/skills/woodcutting/src/axe.kt index d7d23e81..ab51ec0d 100644 --- a/game/plugin/skills/woodcutting/src/axe.kt +++ b/game/plugin/skills/woodcutting/src/axe.kt @@ -1,23 +1,28 @@ package org.apollo.game.plugin.skills.woodcutting -import org.apollo.game.model.Animation; +import org.apollo.game.model.Animation +import org.apollo.game.model.entity.Player +import org.apollo.game.plugin.api.woodcutting -//Animation IDs thanks to Deadly A G S at rune-server.ee -enum class Axe(val id: Int, val level: Int, val animation: Animation, val pulses: Int) { - RUNE(1359, 41, Animation(867), 3), - ADAMANT(1357, 31, Animation(869), 4), - MITHRIL(1355, 21, Animation(871), 5), - BLACK(1361, 11, Animation(873), 6), - STEEL(1353, 6, Animation(875), 6), - IRON(1349, 1, Animation(877), 7), - BRONZE(1351, 1, Animation(879), 8); +enum class Axe(val id: Int, val level: Int, animation: Int, val pulses: Int) { + BRONZE(id = 1351, level = 1, animation = 879, pulses = 8), + IRON(id = 1349, level = 1, animation = 877, pulses = 7), + STEEL(id = 1353, level = 6, animation = 875, pulses = 6), + BLACK(id = 1361, level = 11, animation = 873, pulses = 6), + MITHRIL(id = 1355, level = 21, animation = 871, pulses = 5), + ADAMANT(id = 1357, level = 31, animation = 869, pulses = 4), + RUNE(id = 1359, level = 41, animation = 867, pulses = 3); + + val animation = Animation(animation) companion object { - private val AXES = Axe.values() - fun getAxes(): Array { - return AXES + private val AXES = Axe.values().sortedByDescending { it.level } + + fun bestFor(player: Player): Axe? { + return AXES.asSequence() + .filter { it.level <= player.woodcutting.current } + .filter { player.equipment.contains(it.id) || player.inventory.contains(it.id) } + .firstOrNull() } } } - - diff --git a/game/plugin/skills/woodcutting/src/wood.kt b/game/plugin/skills/woodcutting/src/wood.kt index 71c910ad..51a274a0 100644 --- a/game/plugin/skills/woodcutting/src/wood.kt +++ b/game/plugin/skills/woodcutting/src/wood.kt @@ -1,67 +1,27 @@ package org.apollo.game.plugin.skills.woodcutting -private val NORMAL_STUMP = 1342 -private val ACHEY_STUMP = 3371 -private val OAK_STUMP = 1342 -private val WILLOW_STUMP = 1342 -private val TEAK_STUMP = 1342 -private val MAPLE_STUMP = 1342 -private val MAHOGANY_STUMP = 1342 -private val YEW_STUMP = 1342 -private val MAGIC_STUMP = 1324 - -private val NORMAL_OBJECTS = hashSetOf( - 1276, 1277, 1278, 1279, 1280, 1282, 1283, 1284, 1285, 1285, 1286, 1289, 1290, 1291, 1315, - 1316, 1318, 1330, 1331, 1332, 1365, 1383, 1384, 2409, 3033, 3034, 3035, 3036, 3881, 3882, - 3883, 5902, 5903, 5904, 10041 -) - -private val ACHEY_OBJECTS = hashSetOf( - 2023 -) - -private val OAK_OBJECTS = hashSetOf( - 1281, 3037 -) - -private val WILLOW_OBJECTS = hashSetOf( - 5551, 5552, 5553 -) - -private val TEAK_OBJECTS = hashSetOf( - 9036 -) - - -private val MAPLE_OBJECTS = hashSetOf( - 1307, 4674 -) - -private val MAHOGANY_OBJECTS = hashSetOf( - 9034 -) - -private val YEW_OBJECTS = hashSetOf( - 1309 -) - -private val MAGIC_OBJECTS = hashSetOf( - 1292, 1306 -) - -/** - * values thanks to: http://oldschoolrunescape.wikia.com/wiki/Woodcutting +/* + * Values thanks to: http://oldschoolrunescape.wikia.com/wiki/Woodcutting + * https://twitter.com/JagexKieren/status/713409506273787904 */ -enum class Tree(val id: Int, val objects: HashSet, val stump: Int, val level: Int, val exp: Double, val chance: Double) { - NORMAL(1511, NORMAL_OBJECTS, NORMAL_STUMP, 1, 25.0, 100.0), - ACHEY(2862, ACHEY_OBJECTS, ACHEY_STUMP, 1, 25.0, 100.0), - OAK(1521, OAK_OBJECTS, OAK_STUMP, 15, 37.5, 0.125), - WILLOW(1519, WILLOW_OBJECTS, WILLOW_STUMP, 30, 67.5, 0.125), - TEAK(6333, TEAK_OBJECTS, TEAK_STUMP, 35, 85.0, 0.125), - MAPLE(1517, MAPLE_OBJECTS, MAPLE_STUMP, 45, 100.0, 0.125), - MAHOGANY(6332, MAHOGANY_OBJECTS, MAHOGANY_STUMP, 50, 125.0, 0.125), - YEW(1515, YEW_OBJECTS, YEW_STUMP, 60, 175.0, 0.125), - MAGIC(1513, MAGIC_OBJECTS, MAGIC_STUMP, 75, 250.0, 0.125); + +enum class Tree( + val objects: Set, + val id: Int, + val stump: Int, + val level: Int, + val exp: Double, + val chance: Double +) { + NORMAL(NORMAL_OBJECTS, id = 1511, stump = 1342, level = 1, exp = 25.0, chance = 100.0), + ACHEY(ACHEY_OBJECTS, id = 2862, stump = 3371, level = 1, exp = 25.0, chance = 100.0), + OAK(OAK_OBJECTS, id = 1521, stump = 1342, level = 15, exp = 37.5, chance = 12.5), + WILLOW(WILLOW_OBJECTS, id = 1519, stump = 1342, level = 30, exp = 67.5, chance = 12.5), + TEAK(TEAK_OBJECTS, id = 6333, stump = 1342, level = 35, exp = 85.0, chance = 12.5), + MAPLE(MAPLE_OBJECTS, id = 1517, stump = 1342, level = 45, exp = 100.0, chance = 12.5), + MAHOGANY(MAHOGANY_OBJECTS, id = 6332, stump = 1342, level = 50, exp = 125.0, chance = 12.5), + YEW(YEW_OBJECTS, id = 1515, stump = 1342, level = 60, exp = 175.0, chance = 12.5), + MAGIC(MAGIC_OBJECTS, id = 1513, stump = 1324, level = 75, exp = 250.0, chance = 12.5); companion object { private val TREES = Tree.values().flatMap { tree -> tree.objects.map { Pair(it, tree) } }.toMap() @@ -69,4 +29,17 @@ enum class Tree(val id: Int, val objects: HashSet, val stump: Int, val leve } } +private val NORMAL_OBJECTS = hashSetOf( + 1276, 1277, 1278, 1279, 1280, 1282, 1283, 1284, 1285, 1285, 1286, 1289, 1290, 1291, 1315, + 1316, 1318, 1330, 1331, 1332, 1365, 1383, 1384, 2409, 3033, 3034, 3035, 3036, 3881, 3882, + 3883, 5902, 5903, 5904, 10041 +) +private val ACHEY_OBJECTS = hashSetOf(2023) +private val OAK_OBJECTS = hashSetOf(1281, 3037) +private val WILLOW_OBJECTS = hashSetOf(5551, 5552, 5553) +private val TEAK_OBJECTS = hashSetOf(9036) +private val MAPLE_OBJECTS = hashSetOf(1307, 4674) +private val MAHOGANY_OBJECTS = hashSetOf(9034) +private val YEW_OBJECTS = hashSetOf(1309) +private val MAGIC_OBJECTS = hashSetOf(1292, 1306) \ No newline at end of file diff --git a/game/plugin/skills/woodcutting/src/woodcutting.plugin.kts b/game/plugin/skills/woodcutting/src/woodcutting.plugin.kts index ca20a248..9d3de2ae 100644 --- a/game/plugin/skills/woodcutting/src/woodcutting.plugin.kts +++ b/game/plugin/skills/woodcutting/src/woodcutting.plugin.kts @@ -1,3 +1,4 @@ + import org.apollo.game.GameConstants import org.apollo.game.action.ActionBlock import org.apollo.game.action.AsyncDistancedAction @@ -13,17 +14,24 @@ import org.apollo.game.plugin.api.rand import org.apollo.game.plugin.api.woodcutting import org.apollo.game.plugin.skills.woodcutting.Axe import org.apollo.game.plugin.skills.woodcutting.Tree -import java.util.Optional import java.util.concurrent.TimeUnit +// TODO Accurate chopping rates, e.g. https://twitter.com/JagexKieren/status/713403124464107520 + +on { ObjectActionMessage::class } + .where { option == 1 } + .then { player -> + Tree.lookup(id)?.let { WoodcuttingAction.start(this, player, it) } + } + class WoodcuttingTarget(private val objectId: Int, val position: Position, val tree: Tree) { /** * Get the tree object in the world */ - fun getObject(world: World): Optional { + fun getObject(world: World): GameObject? { val region = world.regionRepository.fromPosition(position) - return region.findObject(position, objectId) + return region.findObject(position, objectId).orElse(null) } /** @@ -44,23 +52,12 @@ class WoodcuttingAction( private const val TREE_SIZE = 2 private const val MINIMUM_RESPAWN_TIME = 30L // In seconds - /** - * Find the highest level axe the player has - */ - private fun axeFor(player: Player): Axe? { - return Axe.getAxes() - .filter { it.level <= player.woodcutting.current } - .filter { player.equipment.contains(it.id) || player.inventory.contains(it.id) } - .sortedByDescending { it.level } - .firstOrNull() - } - /** * Starts a [WoodcuttingAction] for the specified [Player], terminating the [ObjectActionMessage] that triggered * it. */ fun start(message: ObjectActionMessage, player: Player, wood: Tree) { - val axe = axeFor(player) + val axe = Axe.bestFor(player) if (axe != null) { if (player.inventory.freeSlots() == 0) { player.inventory.forceCapacityExceeded() @@ -92,33 +89,26 @@ class WoodcuttingAction( wait(tool.pulses) - //Check that the object exists in the world + // Check that the object exists in the world val obj = target.getObject(mob.world) - if (!obj.isPresent) { + if (obj == null) { stop() } if (mob.inventory.add(target.tree.id)) { - val logName = Definitions.item(target.tree.id)?.name?.toLowerCase() + val logName = Definitions.item(target.tree.id)!!.name.toLowerCase() mob.sendMessage("You managed to cut some $logName.") mob.woodcutting.experience += target.tree.exp } if (target.isCutDown()) { - //respawn time: http://runescape.wikia.com/wiki/Trees + // respawn time: http://runescape.wikia.com/wiki/Trees val respawn = TimeUnit.SECONDS.toMillis(MINIMUM_RESPAWN_TIME + rand(150)) / GameConstants.PULSE_DELAY - mob.world.expireObject(obj.get(), target.tree.stump, respawn.toInt()) + + mob.world.expireObject(obj!!, target.tree.stump, respawn.toInt()) stop() } } } -} -on { ObjectActionMessage::class } - .where { option == 1 } - .then { - val tree = Tree.lookup(id) - if (tree != null) { - WoodcuttingAction.start(this, it, tree) - } - } +} \ No newline at end of file From 6941fc9de0056ebde22f765f6bc3613d2fade09d Mon Sep 17 00:00:00 2001 From: Major Date: Wed, 28 Mar 2018 16:50:43 +0100 Subject: [PATCH 4/6] Fix nitpicks in Mining plugin Also adds equals() and hashcode() implementations to the various Mining actions. --- game/plugin/skills/mining/build.gradle | 1 + game/plugin/skills/mining/src/gem.kt | 19 +- .../skills/mining/src/mining.plugin.kts | 136 +++++---- game/plugin/skills/mining/src/ore.kt | 273 +++++++++--------- game/plugin/skills/mining/src/pickaxe.kt | 42 +-- 5 files changed, 257 insertions(+), 214 deletions(-) diff --git a/game/plugin/skills/mining/build.gradle b/game/plugin/skills/mining/build.gradle index c301b216..71bc9020 100644 --- a/game/plugin/skills/mining/build.gradle +++ b/game/plugin/skills/mining/build.gradle @@ -3,6 +3,7 @@ plugin { authors = [ "Graham", "Mikey`", + "Major", "WH:II:DOW", "Requa", "Clifton", diff --git a/game/plugin/skills/mining/src/gem.kt b/game/plugin/skills/mining/src/gem.kt index 6414bfe6..6f8fa2e7 100644 --- a/game/plugin/skills/mining/src/gem.kt +++ b/game/plugin/skills/mining/src/gem.kt @@ -1,12 +1,13 @@ package org.apollo.game.plugin.skills.mining -enum class Gem(val id: Int, val chance: Int) { - UNCUT_SAPPHIRE(1623, 0), - UNCUT_EMERALD(1605, 0), - UNCUT_RUBY(1619, 0), - UNCUT_DIAMOND(1617, 0) -} +enum class Gem(val id: Int) { // TODO add gem drop chances + UNCUT_SAPPHIRE(1623), + UNCUT_EMERALD(1605), + UNCUT_RUBY(1619), + UNCUT_DIAMOND(1617); -val GEMS = Gem.values() - -fun lookupGem(id: Int): Gem? = GEMS.find { it.id == id } + companion object { + private val GEMS = Gem.values().associateBy({ it.id }, { it }) + fun lookup(id: Int): Gem? = GEMS[id] + } +} \ No newline at end of file diff --git a/game/plugin/skills/mining/src/mining.plugin.kts b/game/plugin/skills/mining/src/mining.plugin.kts index c7d34db2..2c380f1c 100644 --- a/game/plugin/skills/mining/src/mining.plugin.kts +++ b/game/plugin/skills/mining/src/mining.plugin.kts @@ -12,17 +12,33 @@ import org.apollo.game.plugin.api.findObject import org.apollo.game.plugin.api.mining import org.apollo.game.plugin.api.rand import org.apollo.game.plugin.skills.mining.Ore -import org.apollo.game.plugin.skills.mining.PICKAXES import org.apollo.game.plugin.skills.mining.Pickaxe -import org.apollo.game.plugin.skills.mining.lookupOreRock import org.apollo.net.message.Message -import java.util.Optional +import java.util.Objects -class MiningTarget(val objectId: Int, val position: Position, val ore: Ore) { +on { ObjectActionMessage::class } + .where { option == Actions.MINING } + .then { player -> + Ore.fromRock(id)?.let { ore -> + MiningAction.start(this, player, ore) + } + } - fun getObject(world: World): Optional { +on { ObjectActionMessage::class } + .where { option == Actions.PROSPECTING } + .then { player -> + val ore = Ore.fromRock(id) + + if (ore != null) { + ProspectingAction.start(this, player, ore) + } + } + +data class MiningTarget(val objectId: Int, val position: Position, val ore: Ore) { + + fun getObject(world: World): GameObject? { val region = world.regionRepository.fromPosition(position) - return region.findObject(position, objectId) + return region.findObject(position, objectId).orElse(null) } fun isSuccessful(mob: Player): Boolean { @@ -41,27 +57,22 @@ class MiningAction( ) : AsyncDistancedAction(PULSES, true, player, target.position, ORE_SIZE) { companion object { - private val PULSES = 0 - private val ORE_SIZE = 1 - - fun pickaxeFor(player: Player): Pickaxe? { - return PICKAXES - .filter { it.level <= player.mining.current } - .filter { player.equipment.contains(it.id) || player.inventory.contains(it.id) } - .sortedByDescending { it.level } - .firstOrNull() - } + private const val PULSES = 0 + private const val ORE_SIZE = 1 /** * Starts a [MiningAction] for the specified [Player], terminating the [Message] that triggered it. */ fun start(message: ObjectActionMessage, player: Player, ore: Ore) { - val pickaxe = pickaxeFor(player) - if (pickaxe != null) { - val action = MiningAction(player, pickaxe, MiningTarget(message.id, message.position, ore)) - player.startAction(action) - } else { + val pickaxe = Pickaxe.bestFor(player) + + if (pickaxe == null) { player.sendMessage("You do not have a pickaxe for which you have the level to use.") + } else { + val target = MiningTarget(message.id, message.position, ore) + val action = MiningAction(player, pickaxe, target) + + player.startAction(action) } message.terminate() @@ -83,7 +94,7 @@ class MiningAction( wait(tool.pulses) val obj = target.getObject(mob.world) - if (!obj.isPresent) { + if (obj == null) { stop() } @@ -94,25 +105,46 @@ class MiningAction( } if (mob.inventory.add(target.ore.id)) { - val oreName = Definitions.item(target.ore.id)?.name?.toLowerCase() + val oreName = Definitions.item(target.ore.id)!!.name.toLowerCase() mob.sendMessage("You manage to mine some $oreName") mob.mining.experience += target.ore.exp - mob.world.expireObject(obj.get(), target.ore.objects[target.objectId]!!, target.ore.respawn) + mob.world.expireObject(obj!!, target.ore.objects[target.objectId]!!, target.ore.respawn) stop() } } } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as MiningAction + return mob == other.mob && target == other.target + } + + override fun hashCode(): Int = Objects.hash(mob, target) + } class ExpiredProspectingAction( mob: Player, position: Position -) : DistancedAction(PROSPECT_PULSES, true, mob, position, ORE_SIZE) { +) : DistancedAction(DELAY, true, mob, position, ORE_SIZE) { companion object { - private val PROSPECT_PULSES = 0 - private val ORE_SIZE = 1 + private const val DELAY = 0 + private const val ORE_SIZE = 1 + + /** + * Starts a [ExpiredProspectingAction] for the specified [Player], terminating the [Message] that triggered it. + */ + fun start(message: ObjectActionMessage, player: Player) { + val action = ExpiredProspectingAction(player, message.position) + player.startAction(action) + + message.terminate() + } } override fun executeAction() { @@ -120,17 +152,27 @@ class ExpiredProspectingAction( stop() } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ExpiredProspectingAction + return mob == other.mob && position == other.position + } + + override fun hashCode(): Int = Objects.hash(mob, position) + } class ProspectingAction( player: Player, position: Position, private val ore: Ore -) : AsyncDistancedAction(PROSPECT_PULSES, true, player, position, ORE_SIZE) { +) : AsyncDistancedAction(DELAY, true, player, position, ORE_SIZE) { companion object { - private val PROSPECT_PULSES = 3 - private val ORE_SIZE = 1 + private const val DELAY = 3 + private const val ORE_SIZE = 1 /** * Starts a [MiningAction] for the specified [Player], terminating the [Message] that triggered it. @@ -141,7 +183,6 @@ class ProspectingAction( message.terminate() } - } override fun action(): ActionBlock = { @@ -151,27 +192,24 @@ class ProspectingAction( wait() val oreName = Definitions.item(ore.id)?.name?.toLowerCase() - mob.sendMessage("This rock contains $oreName") + mob.sendMessage("This rock contains $oreName.") stop() } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ProspectingAction + return mob == other.mob && position == other.position && ore == other.ore + } + + override fun hashCode(): Int = Objects.hash(mob, position, ore) + } -on { ObjectActionMessage::class } - .where { option == 1 } - .then { - val ore = lookupOreRock(id) - if (ore != null) { - MiningAction.start(this, it, ore) - } - } - -on { ObjectActionMessage::class } - .where { option == 2 } - .then { - val ore = lookupOreRock(id) - if (ore != null) { // TODO send expired action if rock is expired - ProspectingAction.start(this, it, ore) - } - } +private object Actions { + const val MINING = 1 + const val PROSPECTING = 2 +} \ No newline at end of file diff --git a/game/plugin/skills/mining/src/ore.kt b/game/plugin/skills/mining/src/ore.kt index 90001fdc..e272b182 100644 --- a/game/plugin/skills/mining/src/ore.kt +++ b/game/plugin/skills/mining/src/ore.kt @@ -1,155 +1,154 @@ package org.apollo.game.plugin.skills.mining -import com.google.common.collect.Maps.asMap - /* Thanks to Mikey` for helping to find some of the item/object IDs, minimum levels and experiences. Thanks to Clifton for helping to find some of the expired object IDs. */ - -val CLAY_OBJECTS = mapOf( - 2108 to 450, - 2109 to 451, - 14904 to 14896, - 14905 to 14897 -) - -val COPPER_OBJECTS = mapOf( - 11960 to 11555, - 11961 to 11556, - 11962 to 11557, - 11936 to 11552, - 11937 to 11553, - 11938 to 11554, - 2090 to 450, - 2091 to 451, - 14906 to 14898, - 14907 to 14899, - 14856 to 14832, - 14857 to 14833, - 14858 to 14834 -) - -val TIN_OBJECTS = mapOf( - 11597 to 11555, - 11958 to 11556, - 11959 to 11557, - 11933 to 11552, - 11934 to 11553, - 11935 to 11554, - 2094 to 450, - 2095 to 451, - 14092 to 14894, - 14903 to 14895 -) - -val IRON_OBJECTS = mapOf( - 11954 to 11555, - 11955 to 11556, - 11956 to 11557, - 2092 to 450, - 2093 to 451, - 14900 to 14892, - 14901 to 14893, - 14913 to 14915, - 14914 to 14916 -) - -val COAL_OBJECTS = mapOf( - 11963 to 11555, - 11964 to 11556, - 11965 to 11557, - 11930 to 11552, - 11931 to 11553, - 11932 to 11554, - 2096 to 450, - 2097 to 451, - 14850 to 14832, - 14851 to 14833, - 14852 to 14834 -) - -val SILVER_OBJECTS = mapOf ( - 11948 to 11555, - 11949 to 11556, - 11950 to 11557, - 2100 to 450, - 2101 to 451 -) - -val GOLD_OBJECTS = mapOf( - 11951 to 11555, - 11952 to 11556, - 11953 to 11557, - 2098 to 450, - 2099 to 451 -) - -val MITHRIL_OBJECTS = mapOf( - 11945 to 11555, - 11946 to 11556, - 11947 to 11557, - 11942 to 11552, - 11943 to 11553, - 11944 to 11554, - 2102 to 450, - 2103 to 451, - 14853 to 14832, - 14854 to 14833, - 14855 to 14834 -) - -val ADAMANT_OBJECTS = mapOf( - 11939 to 11552, - 11940 to 11553, - 11941 to 11554, - 2104 to 450, - 2105 to 451, - 14862 to 14832, - 14863 to 14833, - 14864 to 14834 -) - -val RUNITE_OBJECTS = mapOf( - 2106 to 450, - 2107 to 451, - 14859 to 14832, - 14860 to 14833, - 14861 to 14834 -) - - /** * Chance values thanks to: http://runescape.wikia.com/wiki/Talk:Mining#Mining_success_rate_formula * Respawn times and xp thanks to: http://oldschoolrunescape.wikia.com/wiki/ */ -enum class Ore(val id: Int, val objects: Map, val level: Int, val exp: Double, val respawn: Int, val chance: Double, val chanceOffset: Boolean) { - CLAY(434, CLAY_OBJECTS, 1, 5.0, 1, 0.0085, true), - COPPER(436, COPPER_OBJECTS, 1, 17.5, 4, 0.0085, true), - TIN(438, TIN_OBJECTS, 1, 17.5, 4, 0.0085, true), - IRON(440, IRON_OBJECTS, 15, 35.0, 9, 0.0085, true), - COAL(453, COAL_OBJECTS, 30, 50.0, 50, 0.004, false), - GOLD(444, GOLD_OBJECTS, 40, 65.0, 100, 0.003, false), - SILVER(442, SILVER_OBJECTS, 20, 40.0, 100, 0.0085, false), - MITHRIL(447, MITHRIL_OBJECTS, 55, 80.0, 200, 0.002, false), - ADAMANT(449, ADAMANT_OBJECTS, 70, 95.0, 800, 0.001, false), - RUNITE(451, RUNITE_OBJECTS, 85, 125.0, 1200, 0.0008, false) -} +enum class Ore( + val objects: Map, + val id: Int, + val level: Int, + val exp: Double, + val respawn: Int, + val chance: Double, + val chanceOffset: Boolean = false +) { + CLAY(CLAY_OBJECTS, id = 434, level = 1, exp = 5.0, respawn = 1, chance = 0.0085, chanceOffset = true), + COPPER(COPPER_OBJECTS, id = 436, level = 1, exp = 17.5, respawn = 4, chance = 0.0085, chanceOffset = true), + TIN(TIN_OBJECTS, id = 438, level = 1, exp = 17.5, respawn = 4, chance = 0.0085, chanceOffset = true), + IRON(IRON_OBJECTS, id = 440, level = 15, exp = 35.0, respawn = 9, chance = 0.0085, chanceOffset = true), + COAL(COAL_OBJECTS, id = 453, level = 30, exp = 50.0, respawn = 50, chance = 0.004), + GOLD(GOLD_OBJECTS, id = 444, level = 40, exp = 65.0, respawn = 100, chance = 0.003), + SILVER(SILVER_OBJECTS, id = 442, level = 20, exp = 40.0, respawn = 100, chance = 0.0085), + MITHRIL(MITHRIL_OBJECTS, id = 447, level = 55, exp = 80.0, respawn = 200, chance = 0.002), + ADAMANT(ADAMANT_OBJECTS, id = 449, level = 70, exp = 95.0, respawn = 800, chance = 0.001), + RUNITE(RUNITE_OBJECTS, id = 451, level = 85, exp = 125.0, respawn = 1_200, chance = 0.0008); -val ORES = enumValues() + companion object { + private val ORE_ROCKS = Ore.values().flatMap { ore -> ore.objects.map { Pair(it.key, ore) } }.toMap() + private val EXPIRED_ORE = Ore.values().flatMap { ore -> ore.objects.map { Pair(it.value, ore) } }.toMap() -fun lookupOre(id: Int): Ore? = ORES.find { it.id == id } + fun fromRock(id: Int): Ore? = ORE_ROCKS[id] + fun fromExpiredRock(id: Int): Ore? = EXPIRED_ORE[id] -fun lookupOreRock(id: Int): Ore? { - for (ore in enumValues()) { - for (rock in ore.objects) { - if (rock.key == id) { - return ore - } - } } - return null } +// Maps from regular rock id to expired rock id. + +val CLAY_OBJECTS = mapOf( + 2108 to 450, + 2109 to 451, + 14904 to 14896, + 14905 to 14897 +) + +val COPPER_OBJECTS = mapOf( + 11960 to 11555, + 11961 to 11556, + 11962 to 11557, + 11936 to 11552, + 11937 to 11553, + 11938 to 11554, + 2090 to 450, + 2091 to 451, + 14906 to 14898, + 14907 to 14899, + 14856 to 14832, + 14857 to 14833, + 14858 to 14834 +) + +val TIN_OBJECTS = mapOf( + 11597 to 11555, + 11958 to 11556, + 11959 to 11557, + 11933 to 11552, + 11934 to 11553, + 11935 to 11554, + 2094 to 450, + 2095 to 451, + 14092 to 14894, + 14903 to 14895 +) + +val IRON_OBJECTS = mapOf( + 11954 to 11555, + 11955 to 11556, + 11956 to 11557, + 2092 to 450, + 2093 to 451, + 14900 to 14892, + 14901 to 14893, + 14913 to 14915, + 14914 to 14916 +) + +val COAL_OBJECTS = mapOf( + 11963 to 11555, + 11964 to 11556, + 11965 to 11557, + 11930 to 11552, + 11931 to 11553, + 11932 to 11554, + 2096 to 450, + 2097 to 451, + 14850 to 14832, + 14851 to 14833, + 14852 to 14834 +) + +val SILVER_OBJECTS = mapOf( + 11948 to 11555, + 11949 to 11556, + 11950 to 11557, + 2100 to 450, + 2101 to 451 +) + +val GOLD_OBJECTS = mapOf( + 11951 to 11555, + 11952 to 11556, + 11953 to 11557, + 2098 to 450, + 2099 to 451 +) + +val MITHRIL_OBJECTS = mapOf( + 11945 to 11555, + 11946 to 11556, + 11947 to 11557, + 11942 to 11552, + 11943 to 11553, + 11944 to 11554, + 2102 to 450, + 2103 to 451, + 14853 to 14832, + 14854 to 14833, + 14855 to 14834 +) + +val ADAMANT_OBJECTS = mapOf( + 11939 to 11552, + 11940 to 11553, + 11941 to 11554, + 2104 to 450, + 2105 to 451, + 14862 to 14832, + 14863 to 14833, + 14864 to 14834 +) + +val RUNITE_OBJECTS = mapOf( + 2106 to 450, + 2107 to 451, + 14859 to 14832, + 14860 to 14833, + 14861 to 14834 +) \ No newline at end of file diff --git a/game/plugin/skills/mining/src/pickaxe.kt b/game/plugin/skills/mining/src/pickaxe.kt index 4b5f0faa..a669f492 100644 --- a/game/plugin/skills/mining/src/pickaxe.kt +++ b/game/plugin/skills/mining/src/pickaxe.kt @@ -1,23 +1,27 @@ package org.apollo.game.plugin.skills.mining -import org.apollo.game.model.Animation; +import org.apollo.game.model.Animation +import org.apollo.game.model.entity.Player +import org.apollo.game.plugin.api.mining -enum class Pickaxe(val id: Int, val level: Int, val animation: Animation, val pulses: Int) { - RUNE(1275, 41, Animation(624), 3), - ADAMANT(1271, 31, Animation(628), 4), - MITHRIL(1273, 21, Animation(629), 5), - STEEL(1269, 1, Animation(627), 6), - ITRON(1267, 1, Animation(626), 7), - BRONZE(1265, 1, Animation(625), 8) +enum class Pickaxe(val id: Int, val level: Int, animation: Int, val pulses: Int) { + BRONZE(id = 1265, level = 1, animation = 625, pulses = 8), + ITRON(id = 1267, level = 1, animation = 626, pulses = 7), + STEEL(id = 1269, level = 1, animation = 627, pulses = 6), + MITHRIL(id = 1273, level = 21, animation = 629, pulses = 5), + ADAMANT(id = 1271, level = 31, animation = 628, pulses = 4), + RUNE(id = 1275, level = 41, animation = 624, pulses = 3); + + val animation = Animation(animation) + + companion object { + private val PICKAXES = Pickaxe.values().sortedByDescending { it.level } + + fun bestFor(player: Player): Pickaxe? { + return PICKAXES.asSequence() + .filter { it.level <= player.mining.current } + .filter { player.equipment.contains(it.id) || player.inventory.contains(it.id) } + .firstOrNull() + } + } } - -val PICKAXES = Pickaxe.values() - - - -fun getPickaxes(): Array { - return PICKAXES -} - -fun lookupPickaxe(id: Int): Pickaxe? = PICKAXES.find { it.id == id } - From 0d7b2ea6e890454c897813f842117096af536cd2 Mon Sep 17 00:00:00 2001 From: Major Date: Wed, 28 Mar 2018 16:52:23 +0100 Subject: [PATCH 5/6] Add prospecting on expired rocks --- game/plugin/skills/mining/src/mining.plugin.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/game/plugin/skills/mining/src/mining.plugin.kts b/game/plugin/skills/mining/src/mining.plugin.kts index 2c380f1c..09ecc88c 100644 --- a/game/plugin/skills/mining/src/mining.plugin.kts +++ b/game/plugin/skills/mining/src/mining.plugin.kts @@ -31,6 +31,8 @@ on { ObjectActionMessage::class } if (ore != null) { ProspectingAction.start(this, player, ore) + } else if (Ore.fromExpiredRock(id) != null) { + ExpiredProspectingAction.start(this, player) } } From 4b68b8e47e3303a2af88bc384ac745e179f7204c Mon Sep 17 00:00:00 2001 From: Major Date: Wed, 28 Mar 2018 17:34:38 +0100 Subject: [PATCH 6/6] Separate prospecting actions --- .../skills/mining/src/mining.plugin.kts | 101 ++---------------- game/plugin/skills/mining/src/prospecting.kt | 92 ++++++++++++++++ 2 files changed, 101 insertions(+), 92 deletions(-) create mode 100644 game/plugin/skills/mining/src/prospecting.kt diff --git a/game/plugin/skills/mining/src/mining.plugin.kts b/game/plugin/skills/mining/src/mining.plugin.kts index 09ecc88c..83ad0621 100644 --- a/game/plugin/skills/mining/src/mining.plugin.kts +++ b/game/plugin/skills/mining/src/mining.plugin.kts @@ -1,6 +1,5 @@ import org.apollo.game.action.ActionBlock import org.apollo.game.action.AsyncDistancedAction -import org.apollo.game.action.DistancedAction import org.apollo.game.message.impl.ObjectActionMessage import org.apollo.game.model.Position import org.apollo.game.model.World @@ -36,22 +35,6 @@ on { ObjectActionMessage::class } } } -data class MiningTarget(val objectId: Int, val position: Position, val ore: Ore) { - - fun getObject(world: World): GameObject? { - val region = world.regionRepository.fromPosition(position) - return region.findObject(position, objectId).orElse(null) - } - - fun isSuccessful(mob: Player): Boolean { - val offset = if (ore.chanceOffset) 1 else 0 - val percent = (ore.chance * mob.mining.current + offset) * 100 - - return rand(100) < percent - } - -} - class MiningAction( player: Player, private val tool: Pickaxe, @@ -129,86 +112,20 @@ class MiningAction( } -class ExpiredProspectingAction( - mob: Player, - position: Position -) : DistancedAction(DELAY, true, mob, position, ORE_SIZE) { +data class MiningTarget(val objectId: Int, val position: Position, val ore: Ore) { - companion object { - private const val DELAY = 0 - private const val ORE_SIZE = 1 - - /** - * Starts a [ExpiredProspectingAction] for the specified [Player], terminating the [Message] that triggered it. - */ - fun start(message: ObjectActionMessage, player: Player) { - val action = ExpiredProspectingAction(player, message.position) - player.startAction(action) - - message.terminate() - } + fun getObject(world: World): GameObject? { + val region = world.regionRepository.fromPosition(position) + return region.findObject(position, objectId).orElse(null) } - override fun executeAction() { - mob.sendMessage("There is currently no ore available in this rock.") - stop() + fun isSuccessful(mob: Player): Boolean { + val offset = if (ore.chanceOffset) 1 else 0 + val percent = (ore.chance * mob.mining.current + offset) * 100 + + return rand(100) < percent } - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ExpiredProspectingAction - return mob == other.mob && position == other.position - } - - override fun hashCode(): Int = Objects.hash(mob, position) - -} - -class ProspectingAction( - player: Player, - position: Position, - private val ore: Ore -) : AsyncDistancedAction(DELAY, true, player, position, ORE_SIZE) { - - companion object { - private const val DELAY = 3 - private const val ORE_SIZE = 1 - - /** - * Starts a [MiningAction] for the specified [Player], terminating the [Message] that triggered it. - */ - fun start(message: ObjectActionMessage, player: Player, ore: Ore) { - val action = ProspectingAction(player, message.position, ore) - player.startAction(action) - - message.terminate() - } - } - - override fun action(): ActionBlock = { - mob.sendMessage("You examine the rock for ores...") - mob.turnTo(position) - - wait() - - val oreName = Definitions.item(ore.id)?.name?.toLowerCase() - mob.sendMessage("This rock contains $oreName.") - - stop() - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as ProspectingAction - return mob == other.mob && position == other.position && ore == other.ore - } - - override fun hashCode(): Int = Objects.hash(mob, position, ore) - } private object Actions { diff --git a/game/plugin/skills/mining/src/prospecting.kt b/game/plugin/skills/mining/src/prospecting.kt new file mode 100644 index 00000000..9b0f3d1c --- /dev/null +++ b/game/plugin/skills/mining/src/prospecting.kt @@ -0,0 +1,92 @@ +import org.apollo.game.action.ActionBlock +import org.apollo.game.action.AsyncDistancedAction +import org.apollo.game.action.DistancedAction +import org.apollo.game.message.impl.ObjectActionMessage +import org.apollo.game.model.Position +import org.apollo.game.model.entity.Player +import org.apollo.game.plugin.api.Definitions +import org.apollo.game.plugin.skills.mining.Ore +import org.apollo.net.message.Message +import java.util.Objects + +class ProspectingAction( + player: Player, + position: Position, + private val ore: Ore +) : AsyncDistancedAction(DELAY, true, player, position, ORE_SIZE) { + + companion object { + private const val DELAY = 3 + private const val ORE_SIZE = 1 + + /** + * Starts a [MiningAction] for the specified [Player], terminating the [Message] that triggered it. + */ + fun start(message: ObjectActionMessage, player: Player, ore: Ore) { + val action = ProspectingAction(player, message.position, ore) + player.startAction(action) + + message.terminate() + } + } + + override fun action(): ActionBlock = { + mob.sendMessage("You examine the rock for ores...") + mob.turnTo(position) + + wait() + + val oreName = Definitions.item(ore.id)?.name?.toLowerCase() + mob.sendMessage("This rock contains $oreName.") + + stop() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ProspectingAction + return mob == other.mob && position == other.position && ore == other.ore + } + + override fun hashCode(): Int = Objects.hash(mob, position, ore) + +} + +class ExpiredProspectingAction( + mob: Player, + position: Position +) : DistancedAction(DELAY, true, mob, position, ORE_SIZE) { + + companion object { + private const val DELAY = 0 + private const val ORE_SIZE = 1 + + /** + * Starts a [ExpiredProspectingAction] for the specified [Player], terminating the [Message] that triggered it. + */ + fun start(message: ObjectActionMessage, player: Player) { + val action = ExpiredProspectingAction(player, message.position) + player.startAction(action) + + message.terminate() + } + } + + override fun executeAction() { + mob.sendMessage("There is currently no ore available in this rock.") + stop() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ExpiredProspectingAction + return mob == other.mob && position == other.position + } + + override fun hashCode(): Int = Objects.hash(mob, position) + +} \ No newline at end of file