mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-07 00:38:46 +00:00
Merge pull request #386 from Major-/kotlin-experiments
Improve woodcutting, fishing, mining plugin code
This commit is contained in:
@@ -6,6 +6,8 @@ plugin {
|
|||||||
"tlf30"
|
"tlf30"
|
||||||
]
|
]
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"util:lookup", "entity:spawn", "api"
|
"api",
|
||||||
|
"entity:spawn",
|
||||||
|
"util:lookup"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,54 @@
|
|||||||
package org.apollo.game.plugin.skills.fishing
|
package org.apollo.game.plugin.skills.fishing
|
||||||
|
|
||||||
import org.apollo.cache.def.ItemDefinition
|
|
||||||
import org.apollo.game.model.Animation
|
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.Fish.*
|
||||||
import org.apollo.game.plugin.skills.fishing.FishingTool.*
|
import org.apollo.game.plugin.skills.fishing.FishingTool.*
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fish that can be gathered using the fishing skill.
|
* A fish that can be gathered using the fishing skill.
|
||||||
*/
|
*/
|
||||||
enum class Fish(val id: Int, val level: Int, val experience: Double) {
|
enum class Fish(val id: Int, val level: Int, val experience: Double) {
|
||||||
SHRIMP(317, 1, 10.0),
|
SHRIMP(id = 317, level = 1, experience = 10.0),
|
||||||
SARDINE(327, 5, 20.0),
|
SARDINE(id = 327, level = 5, experience = 20.0),
|
||||||
MACKEREL(353, 16, 20.0),
|
MACKEREL(id = 353, level = 16, experience = 20.0),
|
||||||
HERRING(345, 10, 30.0),
|
HERRING(id = 345, level = 10, experience = 30.0),
|
||||||
ANCHOVY(321, 15, 40.0),
|
ANCHOVY(id = 321, level = 15, experience = 40.0),
|
||||||
TROUT(335, 20, 50.0),
|
TROUT(id = 335, level = 20, experience = 50.0),
|
||||||
COD(341, 23, 45.0),
|
COD(id = 341, level = 23, experience = 45.0),
|
||||||
PIKE(349, 25, 60.0),
|
PIKE(id = 349, level = 25, experience = 60.0),
|
||||||
SALMON(331, 30, 70.0),
|
SALMON(id = 331, level = 30, experience = 70.0),
|
||||||
TUNA(359, 35, 80.0),
|
TUNA(id = 359, level = 35, experience = 80.0),
|
||||||
LOBSTER(377, 40, 90.0),
|
LOBSTER(id = 377, level = 40, experience = 90.0),
|
||||||
BASS(363, 46, 100.0),
|
BASS(id = 363, level = 46, experience = 100.0),
|
||||||
SWORDFISH(371, 50, 100.0),
|
SWORDFISH(id = 371, level = 50, experience = 100.0),
|
||||||
SHARK(383, 76, 110.0);
|
SHARK(id = 383, level = 76, experience = 110.0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of this fish, formatted so it can be inserted into a message.
|
* 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].
|
* 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?) {
|
enum class FishingTool(
|
||||||
LOBSTER_CAGE(301, 619, "You attempt to catch a lobster..."),
|
val message: String,
|
||||||
SMALL_NET(303, 620, "You cast out your net..."),
|
val id: Int,
|
||||||
BIG_NET(305, 620, "You cast out your net..."),
|
animation: Int,
|
||||||
HARPOON(311, 618, "You start harpooning fish..."),
|
val bait: Int = -1,
|
||||||
FISHING_ROD(307, 622, "You attempt to catch a fish...", 313, "feathers"),
|
val baitName: String? = null
|
||||||
FLY_FISHING_ROD(309, 622, "You attempt to catch a fish...", 314, "fishing bait");
|
) {
|
||||||
|
LOBSTER_CAGE("You attempt to catch a lobster...", id = 301, animation = 619),
|
||||||
@Suppress("unused") // IntelliJ bug, doesn't detect that this constructor is used
|
SMALL_NET("You cast out your net...", id = 303, animation = 620),
|
||||||
constructor(id: Int, animation: Int, message: String) : this(id, animation, message, -1, null)
|
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.
|
* 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.
|
* 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));
|
NET_ROD(316, Option.of(SMALL_NET, SHRIMP, ANCHOVY), Option.of(FISHING_ROD, SARDINE, HERRING));
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val FISHING_SPOTS = FishingSpot.values().associateBy({ it.npc }, { it })
|
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.
|
* Returns the [FishingSpot] with the specified [id], or `null` if the spot does not exist.
|
||||||
*/
|
*/
|
||||||
fun lookup(id: Int): FishingSpot? = FISHING_SPOTS[id]
|
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 {
|
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
|
* 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 val level = primary.level
|
||||||
|
|
||||||
override fun sample(level: Int): Fish = primary
|
override fun sample(level: Int): Fish = primary
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [FishingSpot] [Option] that can provide a two different types of fish.
|
* 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() {
|
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 val level = Math.min(primary.level, secondary.level)
|
||||||
|
|
||||||
override fun sample(level: Int): Fish {
|
override fun sample(level: Int): Fish {
|
||||||
if (secondary.level > level) {
|
return if (level < secondary.level || rand(100) < WEIGHTING) {
|
||||||
return primary
|
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 {
|
return when {
|
||||||
random.nextInt(100) < WEIGHTING -> primary
|
primary.level < secondary.level -> Pair(tool, primary, secondary)
|
||||||
else -> secondary
|
else -> Pair(tool, secondary, primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,44 +6,31 @@ import org.apollo.game.message.impl.NpcActionMessage
|
|||||||
import org.apollo.game.model.Position
|
import org.apollo.game.model.Position
|
||||||
import org.apollo.game.model.entity.Player
|
import org.apollo.game.model.entity.Player
|
||||||
import org.apollo.game.plugin.api.fishing
|
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.FishingSpot
|
||||||
import org.apollo.game.plugin.skills.fishing.FishingTool
|
import org.apollo.game.plugin.skills.fishing.FishingTool
|
||||||
import java.util.Objects
|
import java.util.Objects
|
||||||
import java.util.Random
|
|
||||||
|
|
||||||
// TODO: moving fishing spots, seaweed and caskets, evil bob
|
// 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) :
|
class FishingAction(player: Player, position: Position, val option: FishingSpot.Option) :
|
||||||
AsyncDistancedAction<Player>(0, true, player, position, SPOT_DISTANCE) {
|
AsyncDistancedAction<Player>(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.
|
* 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) {
|
if (mob.inventory.freeSlots() == 0) {
|
||||||
mob.inventory.forceCapacityExceeded()
|
mob.inventory.forceCapacityExceeded()
|
||||||
|
|
||||||
mob.stopAnimation()
|
mob.stopAnimation()
|
||||||
stop()
|
stop()
|
||||||
} else if (!hasBait(mob, tool.bait)) {
|
} else if (!hasBait(mob, tool.bait)) {
|
||||||
mob.sendMessage("You need more ${tool.baitName} to fish at this spot.")
|
mob.sendMessage("You need more ${tool.baitName} to fish at this spot.")
|
||||||
|
|
||||||
mob.stopAnimation()
|
mob.stopAnimation()
|
||||||
stop()
|
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.
|
* Verifies that the player can gather fish from the [FishingSpot] they clicked.
|
||||||
*/
|
*/
|
||||||
private fun verify(): Boolean {
|
private fun verify(): Boolean {
|
||||||
if (mob.fishing.current < option.level) {
|
val current = mob.fishing.current
|
||||||
mob.sendMessage("You need a fishing level of ${option.level} to fish at this spot.")
|
|
||||||
return false
|
when {
|
||||||
} else if (!hasTool(mob, tool)) {
|
current < option.level -> mob.sendMessage("You need a fishing level of ${option.level} to fish at this spot.")
|
||||||
mob.sendMessage("You need a ${tool.formattedName} to fish at this spot.")
|
!hasTool(mob, tool) -> mob.sendMessage("You need a ${tool.formattedName} to fish at this spot.")
|
||||||
return false
|
!hasBait(mob, tool.bait) -> mob.sendMessage("You need some ${tool.baitName} to fish at this spot.")
|
||||||
} else if (!hasBait(mob, tool.bait)) {
|
mob.inventory.freeSlots() == 0 -> mob.inventory.forceCapacityExceeded()
|
||||||
mob.sendMessage("You need some ${tool.baitName} to fish at this spot.")
|
else -> return true
|
||||||
return false
|
|
||||||
} else if (mob.inventory.freeSlots() == 0) {
|
|
||||||
mob.inventory.forceCapacityExceeded()
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
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)
|
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
|
* Returns whether or not the catch was successful.
|
||||||
*/
|
* TODO: We need to identify the correct algorithm for this
|
||||||
on { NpcActionMessage::class }
|
*/
|
||||||
.where { option == 1 || option == 3 }
|
private fun successfulCatch(level: Int, req: Int): Boolean = minOf(level - req + 5, 40) > rand(100)
|
||||||
.then {
|
|
||||||
val entity = it.world.npcRepository[index]
|
|
||||||
val spot = FishingSpot.lookup(entity.id) ?: return@then
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -125,10 +125,6 @@ register(ROD, x = 2385, y = 3422)
|
|||||||
register(ROD, x = 2384, y = 3419)
|
register(ROD, x = 2384, y = 3419)
|
||||||
register(ROD, x = 2383, y = 3417)
|
register(ROD, x = 2383, y = 3417)
|
||||||
|
|
||||||
// Gunnarsgrunn
|
|
||||||
register(ROD, x = 3101, y = 3092)
|
|
||||||
register(ROD, x = 3103, y = 3092)
|
|
||||||
|
|
||||||
// Karamja
|
// Karamja
|
||||||
register(NET_ROD, x = 2921, y = 3178)
|
register(NET_ROD, x = 2921, y = 3178)
|
||||||
register(CAGE_HARPOON, x = 2923, y = 3179)
|
register(CAGE_HARPOON, x = 2923, y = 3179)
|
||||||
@@ -163,7 +159,6 @@ register(NET_ROD, x = 2990, y = 3169)
|
|||||||
register(NET_ROD, x = 2986, y = 3176)
|
register(NET_ROD, x = 2986, y = 3176)
|
||||||
|
|
||||||
// Shilo Village
|
// Shilo Village
|
||||||
|
|
||||||
register(ROD, x = 2855, y = 2974)
|
register(ROD, x = 2855, y = 2974)
|
||||||
register(ROD, x = 2865, y = 2972)
|
register(ROD, x = 2865, y = 2972)
|
||||||
register(ROD, x = 2860, y = 2972)
|
register(ROD, x = 2860, y = 2972)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ plugin {
|
|||||||
authors = [
|
authors = [
|
||||||
"Graham",
|
"Graham",
|
||||||
"Mikey`",
|
"Mikey`",
|
||||||
|
"Major",
|
||||||
"WH:II:DOW",
|
"WH:II:DOW",
|
||||||
"Requa",
|
"Requa",
|
||||||
"Clifton",
|
"Clifton",
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package org.apollo.game.plugin.skills.mining
|
package org.apollo.game.plugin.skills.mining
|
||||||
|
|
||||||
enum class Gem(val id: Int, val chance: Int) {
|
enum class Gem(val id: Int) { // TODO add gem drop chances
|
||||||
UNCUT_SAPPHIRE(1623, 0),
|
UNCUT_SAPPHIRE(1623),
|
||||||
UNCUT_EMERALD(1605, 0),
|
UNCUT_EMERALD(1605),
|
||||||
UNCUT_RUBY(1619, 0),
|
UNCUT_RUBY(1619),
|
||||||
UNCUT_DIAMOND(1617, 0)
|
UNCUT_DIAMOND(1617);
|
||||||
}
|
|
||||||
|
|
||||||
val GEMS = Gem.values()
|
companion object {
|
||||||
|
private val GEMS = Gem.values().associateBy({ it.id }, { it })
|
||||||
fun lookupGem(id: Int): Gem? = GEMS.find { it.id == id }
|
fun lookup(id: Int): Gem? = GEMS[id]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import org.apollo.game.action.ActionBlock
|
import org.apollo.game.action.ActionBlock
|
||||||
import org.apollo.game.action.AsyncDistancedAction
|
import org.apollo.game.action.AsyncDistancedAction
|
||||||
import org.apollo.game.action.DistancedAction
|
|
||||||
import org.apollo.game.message.impl.ObjectActionMessage
|
import org.apollo.game.message.impl.ObjectActionMessage
|
||||||
import org.apollo.game.model.Position
|
import org.apollo.game.model.Position
|
||||||
import org.apollo.game.model.World
|
import org.apollo.game.model.World
|
||||||
@@ -12,28 +11,30 @@ import org.apollo.game.plugin.api.findObject
|
|||||||
import org.apollo.game.plugin.api.mining
|
import org.apollo.game.plugin.api.mining
|
||||||
import org.apollo.game.plugin.api.rand
|
import org.apollo.game.plugin.api.rand
|
||||||
import org.apollo.game.plugin.skills.mining.Ore
|
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.Pickaxe
|
||||||
import org.apollo.game.plugin.skills.mining.lookupOreRock
|
|
||||||
import org.apollo.net.message.Message
|
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 }
|
||||||
fun getObject(world: World): Optional<GameObject> {
|
.then { player ->
|
||||||
val region = world.regionRepository.fromPosition(position)
|
Ore.fromRock(id)?.let { ore ->
|
||||||
return region.findObject(position, objectId)
|
MiningAction.start(this, player, ore)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isSuccessful(mob: Player): Boolean {
|
on { ObjectActionMessage::class }
|
||||||
val offset = if (ore.chanceOffset) 1 else 0
|
.where { option == Actions.PROSPECTING }
|
||||||
val percent = (ore.chance * mob.mining.current + offset) * 100
|
.then { player ->
|
||||||
|
val ore = Ore.fromRock(id)
|
||||||
|
|
||||||
return rand(100) < percent
|
if (ore != null) {
|
||||||
|
ProspectingAction.start(this, player, ore)
|
||||||
|
} else if (Ore.fromExpiredRock(id) != null) {
|
||||||
|
ExpiredProspectingAction.start(this, player)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class MiningAction(
|
class MiningAction(
|
||||||
player: Player,
|
player: Player,
|
||||||
private val tool: Pickaxe,
|
private val tool: Pickaxe,
|
||||||
@@ -41,27 +42,22 @@ class MiningAction(
|
|||||||
) : AsyncDistancedAction<Player>(PULSES, true, player, target.position, ORE_SIZE) {
|
) : AsyncDistancedAction<Player>(PULSES, true, player, target.position, ORE_SIZE) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val PULSES = 0
|
private const val PULSES = 0
|
||||||
private val ORE_SIZE = 1
|
private const 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()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a [MiningAction] for the specified [Player], terminating the [Message] that triggered it.
|
* Starts a [MiningAction] for the specified [Player], terminating the [Message] that triggered it.
|
||||||
*/
|
*/
|
||||||
fun start(message: ObjectActionMessage, player: Player, ore: Ore) {
|
fun start(message: ObjectActionMessage, player: Player, ore: Ore) {
|
||||||
val pickaxe = pickaxeFor(player)
|
val pickaxe = Pickaxe.bestFor(player)
|
||||||
if (pickaxe != null) {
|
|
||||||
val action = MiningAction(player, pickaxe, MiningTarget(message.id, message.position, ore))
|
if (pickaxe == null) {
|
||||||
player.startAction(action)
|
|
||||||
} else {
|
|
||||||
player.sendMessage("You do not have a pickaxe for which you have the level to use.")
|
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()
|
message.terminate()
|
||||||
@@ -83,7 +79,7 @@ class MiningAction(
|
|||||||
wait(tool.pulses)
|
wait(tool.pulses)
|
||||||
|
|
||||||
val obj = target.getObject(mob.world)
|
val obj = target.getObject(mob.world)
|
||||||
if (!obj.isPresent) {
|
if (obj == null) {
|
||||||
stop()
|
stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,84 +90,45 @@ class MiningAction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mob.inventory.add(target.ore.id)) {
|
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.sendMessage("You manage to mine some $oreName")
|
||||||
|
|
||||||
mob.mining.experience += target.ore.exp
|
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()
|
stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
class ExpiredProspectingAction(
|
override fun equals(other: Any?): Boolean {
|
||||||
mob: Player,
|
if (this === other) return true
|
||||||
position: Position
|
if (javaClass != other?.javaClass) return false
|
||||||
) : DistancedAction<Player>(PROSPECT_PULSES, true, mob, position, ORE_SIZE) {
|
|
||||||
|
|
||||||
companion object {
|
other as MiningAction
|
||||||
private val PROSPECT_PULSES = 0
|
return mob == other.mob && target == other.target
|
||||||
private val ORE_SIZE = 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun executeAction() {
|
override fun hashCode(): Int = Objects.hash(mob, target)
|
||||||
mob.sendMessage("There is currently no ore available in this rock.")
|
|
||||||
stop()
|
}
|
||||||
|
|
||||||
|
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 ProspectingAction(
|
private object Actions {
|
||||||
player: Player,
|
const val MINING = 1
|
||||||
position: Position,
|
const val PROSPECTING = 2
|
||||||
private val ore: Ore
|
}
|
||||||
) : AsyncDistancedAction<Player>(PROSPECT_PULSES, true, player, position, ORE_SIZE) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val PROSPECT_PULSES = 3
|
|
||||||
private 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()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,155 +1,154 @@
|
|||||||
package org.apollo.game.plugin.skills.mining
|
package org.apollo.game.plugin.skills.mining
|
||||||
|
|
||||||
import com.google.common.collect.Maps.asMap
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Thanks to Mikey` <http://www.rune-server.org/members/mikey%60/> for helping
|
Thanks to Mikey` <http://www.rune-server.org/members/mikey%60/> for helping
|
||||||
to find some of the item/object IDs, minimum levels and experiences.
|
to find some of the item/object IDs, minimum levels and experiences.
|
||||||
Thanks to Clifton <http://www.rune-server.org/members/clifton/> for helping
|
Thanks to Clifton <http://www.rune-server.org/members/clifton/> for helping
|
||||||
to find some of the expired object IDs.
|
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
|
* 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/
|
* Respawn times and xp thanks to: http://oldschoolrunescape.wikia.com/wiki/
|
||||||
*/
|
*/
|
||||||
enum class Ore(val id: Int, val objects: Map<Int, Int>, val level: Int, val exp: Double, val respawn: Int, val chance: Double, val chanceOffset: Boolean) {
|
enum class Ore(
|
||||||
CLAY(434, CLAY_OBJECTS, 1, 5.0, 1, 0.0085, true),
|
val objects: Map<Int, Int>,
|
||||||
COPPER(436, COPPER_OBJECTS, 1, 17.5, 4, 0.0085, true),
|
val id: Int,
|
||||||
TIN(438, TIN_OBJECTS, 1, 17.5, 4, 0.0085, true),
|
val level: Int,
|
||||||
IRON(440, IRON_OBJECTS, 15, 35.0, 9, 0.0085, true),
|
val exp: Double,
|
||||||
COAL(453, COAL_OBJECTS, 30, 50.0, 50, 0.004, false),
|
val respawn: Int,
|
||||||
GOLD(444, GOLD_OBJECTS, 40, 65.0, 100, 0.003, false),
|
val chance: Double,
|
||||||
SILVER(442, SILVER_OBJECTS, 20, 40.0, 100, 0.0085, false),
|
val chanceOffset: Boolean = false
|
||||||
MITHRIL(447, MITHRIL_OBJECTS, 55, 80.0, 200, 0.002, false),
|
) {
|
||||||
ADAMANT(449, ADAMANT_OBJECTS, 70, 95.0, 800, 0.001, false),
|
CLAY(CLAY_OBJECTS, id = 434, level = 1, exp = 5.0, respawn = 1, chance = 0.0085, chanceOffset = true),
|
||||||
RUNITE(451, RUNITE_OBJECTS, 85, 125.0, 1200, 0.0008, false)
|
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<Ore>()
|
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<Ore>()) {
|
|
||||||
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
|
||||||
|
)
|
||||||
@@ -1,23 +1,27 @@
|
|||||||
package org.apollo.game.plugin.skills.mining
|
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) {
|
enum class Pickaxe(val id: Int, val level: Int, animation: Int, val pulses: Int) {
|
||||||
RUNE(1275, 41, Animation(624), 3),
|
BRONZE(id = 1265, level = 1, animation = 625, pulses = 8),
|
||||||
ADAMANT(1271, 31, Animation(628), 4),
|
ITRON(id = 1267, level = 1, animation = 626, pulses = 7),
|
||||||
MITHRIL(1273, 21, Animation(629), 5),
|
STEEL(id = 1269, level = 1, animation = 627, pulses = 6),
|
||||||
STEEL(1269, 1, Animation(627), 6),
|
MITHRIL(id = 1273, level = 21, animation = 629, pulses = 5),
|
||||||
ITRON(1267, 1, Animation(626), 7),
|
ADAMANT(id = 1271, level = 31, animation = 628, pulses = 4),
|
||||||
BRONZE(1265, 1, Animation(625), 8)
|
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<Pickaxe> {
|
|
||||||
return PICKAXES
|
|
||||||
}
|
|
||||||
|
|
||||||
fun lookupPickaxe(id: Int): Pickaxe? = PICKAXES.find { it.id == id }
|
|
||||||
|
|
||||||
|
|||||||
@@ -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<Player>(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<Player>(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)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,23 +1,28 @@
|
|||||||
package org.apollo.game.plugin.skills.woodcutting
|
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, animation: Int, val pulses: Int) {
|
||||||
enum class Axe(val id: Int, val level: Int, val animation: Animation, val pulses: Int) {
|
BRONZE(id = 1351, level = 1, animation = 879, pulses = 8),
|
||||||
RUNE(1359, 41, Animation(867), 3),
|
IRON(id = 1349, level = 1, animation = 877, pulses = 7),
|
||||||
ADAMANT(1357, 31, Animation(869), 4),
|
STEEL(id = 1353, level = 6, animation = 875, pulses = 6),
|
||||||
MITHRIL(1355, 21, Animation(871), 5),
|
BLACK(id = 1361, level = 11, animation = 873, pulses = 6),
|
||||||
BLACK(1361, 11, Animation(873), 6),
|
MITHRIL(id = 1355, level = 21, animation = 871, pulses = 5),
|
||||||
STEEL(1353, 6, Animation(875), 6),
|
ADAMANT(id = 1357, level = 31, animation = 869, pulses = 4),
|
||||||
IRON(1349, 1, Animation(877), 7),
|
RUNE(id = 1359, level = 41, animation = 867, pulses = 3);
|
||||||
BRONZE(1351, 1, Animation(879), 8);
|
|
||||||
|
val animation = Animation(animation)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val AXES = Axe.values()
|
private val AXES = Axe.values().sortedByDescending { it.level }
|
||||||
fun getAxes(): Array<Axe> {
|
|
||||||
return AXES
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +1,27 @@
|
|||||||
package org.apollo.game.plugin.skills.woodcutting
|
package org.apollo.game.plugin.skills.woodcutting
|
||||||
|
|
||||||
private val NORMAL_STUMP = 1342
|
/*
|
||||||
private val ACHEY_STUMP = 3371
|
* Values thanks to: http://oldschoolrunescape.wikia.com/wiki/Woodcutting
|
||||||
private val OAK_STUMP = 1342
|
* https://twitter.com/JagexKieren/status/713409506273787904
|
||||||
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
|
|
||||||
*/
|
*/
|
||||||
enum class Tree(val id: Int, val objects: HashSet<Int>, val stump: Int, val level: Int, val exp: Double, val chance: Double) {
|
|
||||||
NORMAL(1511, NORMAL_OBJECTS, NORMAL_STUMP, 1, 25.0, 100.0),
|
enum class Tree(
|
||||||
ACHEY(2862, ACHEY_OBJECTS, ACHEY_STUMP, 1, 25.0, 100.0),
|
val objects: Set<Int>,
|
||||||
OAK(1521, OAK_OBJECTS, OAK_STUMP, 15, 37.5, 0.125),
|
val id: Int,
|
||||||
WILLOW(1519, WILLOW_OBJECTS, WILLOW_STUMP, 30, 67.5, 0.125),
|
val stump: Int,
|
||||||
TEAK(6333, TEAK_OBJECTS, TEAK_STUMP, 35, 85.0, 0.125),
|
val level: Int,
|
||||||
MAPLE(1517, MAPLE_OBJECTS, MAPLE_STUMP, 45, 100.0, 0.125),
|
val exp: Double,
|
||||||
MAHOGANY(6332, MAHOGANY_OBJECTS, MAHOGANY_STUMP, 50, 125.0, 0.125),
|
val chance: Double
|
||||||
YEW(1515, YEW_OBJECTS, YEW_STUMP, 60, 175.0, 0.125),
|
) {
|
||||||
MAGIC(1513, MAGIC_OBJECTS, MAGIC_STUMP, 75, 250.0, 0.125);
|
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 {
|
companion object {
|
||||||
private val TREES = Tree.values().flatMap { tree -> tree.objects.map { Pair(it, tree) } }.toMap()
|
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<Int>, 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)
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import org.apollo.game.GameConstants
|
import org.apollo.game.GameConstants
|
||||||
import org.apollo.game.action.ActionBlock
|
import org.apollo.game.action.ActionBlock
|
||||||
import org.apollo.game.action.AsyncDistancedAction
|
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.api.woodcutting
|
||||||
import org.apollo.game.plugin.skills.woodcutting.Axe
|
import org.apollo.game.plugin.skills.woodcutting.Axe
|
||||||
import org.apollo.game.plugin.skills.woodcutting.Tree
|
import org.apollo.game.plugin.skills.woodcutting.Tree
|
||||||
import java.util.Optional
|
|
||||||
import java.util.concurrent.TimeUnit
|
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) {
|
class WoodcuttingTarget(private val objectId: Int, val position: Position, val tree: Tree) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the tree object in the world
|
* Get the tree object in the world
|
||||||
*/
|
*/
|
||||||
fun getObject(world: World): Optional<GameObject> {
|
fun getObject(world: World): GameObject? {
|
||||||
val region = world.regionRepository.fromPosition(position)
|
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 TREE_SIZE = 2
|
||||||
private const val MINIMUM_RESPAWN_TIME = 30L // In seconds
|
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
|
* Starts a [WoodcuttingAction] for the specified [Player], terminating the [ObjectActionMessage] that triggered
|
||||||
* it.
|
* it.
|
||||||
*/
|
*/
|
||||||
fun start(message: ObjectActionMessage, player: Player, wood: Tree) {
|
fun start(message: ObjectActionMessage, player: Player, wood: Tree) {
|
||||||
val axe = axeFor(player)
|
val axe = Axe.bestFor(player)
|
||||||
if (axe != null) {
|
if (axe != null) {
|
||||||
if (player.inventory.freeSlots() == 0) {
|
if (player.inventory.freeSlots() == 0) {
|
||||||
player.inventory.forceCapacityExceeded()
|
player.inventory.forceCapacityExceeded()
|
||||||
@@ -92,33 +89,26 @@ class WoodcuttingAction(
|
|||||||
|
|
||||||
wait(tool.pulses)
|
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)
|
val obj = target.getObject(mob.world)
|
||||||
if (!obj.isPresent) {
|
if (obj == null) {
|
||||||
stop()
|
stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mob.inventory.add(target.tree.id)) {
|
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.sendMessage("You managed to cut some $logName.")
|
||||||
mob.woodcutting.experience += target.tree.exp
|
mob.woodcutting.experience += target.tree.exp
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.isCutDown()) {
|
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
|
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()
|
stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
on { ObjectActionMessage::class }
|
}
|
||||||
.where { option == 1 }
|
|
||||||
.then {
|
|
||||||
val tree = Tree.lookup(id)
|
|
||||||
if (tree != null) {
|
|
||||||
WoodcuttingAction.start(this, it, tree)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user