mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-05 16:49:04 +00:00
Refactor mining to use async actions
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
plugin {
|
||||||
|
name = "mining-skill"
|
||||||
|
packageName = "org.apollo.game.plugin.skills.mining"
|
||||||
|
authors = [
|
||||||
|
"Graham",
|
||||||
|
"Mikey`",
|
||||||
|
"WH:II:DOW",
|
||||||
|
"Requa",
|
||||||
|
"Clifton",
|
||||||
|
"tlf30"
|
||||||
|
]
|
||||||
|
|
||||||
|
dependencies = ["api"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
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
|
||||||
|
import org.apollo.game.model.entity.Player
|
||||||
|
import org.apollo.game.model.entity.Skill
|
||||||
|
import org.apollo.game.model.entity.obj.GameObject
|
||||||
|
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.game.plugins.api.Definitions
|
||||||
|
import org.apollo.game.plugins.api.mining
|
||||||
|
import org.apollo.game.plugins.api.skills
|
||||||
|
import org.apollo.net.message.Message
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class MiningTarget(val objectId: Int, val position: Position, val ore: Ore) {
|
||||||
|
fun getObject(world: World): Optional<GameObject> {
|
||||||
|
val region = world.regionRepository.fromPosition(position)
|
||||||
|
val obj = region.findObject(position, objectId)
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSuccessful(mob: Player): Boolean {
|
||||||
|
val offset = if (ore.chanceOffset) 1 else 0
|
||||||
|
val percent = (ore.chance * mob.skills.mining.currentLevel + offset) * 100
|
||||||
|
|
||||||
|
return rand(100) < percent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MiningAction(player: Player, val tool: Pickaxe, val target: MiningTarget) : AsyncDistancedAction<Player>(
|
||||||
|
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.skills.mining.currentLevel }
|
||||||
|
.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.
|
||||||
|
*/
|
||||||
|
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 {
|
||||||
|
player.sendMessage("You do not have a pickaxe for which you have the level to use.")
|
||||||
|
}
|
||||||
|
|
||||||
|
message.terminate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun start() {
|
||||||
|
mob.turnTo(position)
|
||||||
|
|
||||||
|
val level = mob.skills.mining.currentLevel
|
||||||
|
|
||||||
|
if (level < target.ore.level) {
|
||||||
|
mob.sendMessage("You do not have the required level to mine this rock.")
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun executeActionAsync() {
|
||||||
|
mob.sendMessage("You swing your pick at the rock.")
|
||||||
|
mob.playAnimation(tool.animation)
|
||||||
|
|
||||||
|
wait(tool.pulses)
|
||||||
|
|
||||||
|
val obj = target.getObject(mob.world)
|
||||||
|
if (!obj.isPresent) {
|
||||||
|
stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.isSuccessful(mob)) {
|
||||||
|
if (mob.inventory.freeSlots() == 0) {
|
||||||
|
mob.inventory.forceCapacityExceeded()
|
||||||
|
stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mob.inventory.add(target.ore.id)) {
|
||||||
|
val oreName = Definitions.item(target.ore.id)?.name?.toLowerCase()
|
||||||
|
mob.sendMessage("You manage to mine some $oreName")
|
||||||
|
|
||||||
|
mob.skills.addExperience(Skill.MINING, target.ore.exp)
|
||||||
|
mob.world.expireObject(obj.get(), target.ore.objects[target.objectId]!!, target.ore.respawn)
|
||||||
|
stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExpiredProspectingAction : DistancedAction<Player> {
|
||||||
|
|
||||||
|
constructor(mob: Player, position: Position) : super(PROSPECT_PULSES, true, mob, position, ORE_SIZE)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val PROSPECT_PULSES = 0
|
||||||
|
private val ORE_SIZE = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun executeAction() {
|
||||||
|
mob.sendMessage("There is currently no ore available in this rock.")
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProspectingAction(val m: Player, val p: Position, val ore: Ore) : AsyncDistancedAction<Player>(PROSPECT_PULSES, true, m, p, 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend override fun executeActionAsync() {
|
||||||
|
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 {
|
||||||
|
var ore = lookupOreRock(id)
|
||||||
|
if (ore != null) {
|
||||||
|
ProspectingAction.start(this, it, ore)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,39 +9,6 @@ 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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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<Int, Int>, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
val ORES = Ore.values()
|
|
||||||
|
|
||||||
fun lookupOre(id: Int): Ore? = ORES.find { it.id == id }
|
|
||||||
|
|
||||||
fun lookupOreRock(id: Int): Ore? {
|
|
||||||
for (ore in ORES) {
|
|
||||||
for (rock in ore.objects) {
|
|
||||||
if (rock.key == id) {
|
|
||||||
return ore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val CLAY_OBJECTS = mapOf(
|
val CLAY_OBJECTS = mapOf(
|
||||||
2108 to 450,
|
2108 to 450,
|
||||||
2109 to 451,
|
2109 to 451,
|
||||||
@@ -153,3 +120,36 @@ val RUNITE_OBJECTS = mapOf(
|
|||||||
14861 to 14834
|
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<Int, Int>, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
val ORES = enumValues<Ore>()
|
||||||
|
|
||||||
|
fun lookupOre(id: Int): Ore? = ORES.find { it.id == id }
|
||||||
|
|
||||||
|
fun lookupOreRock(id: Int): Ore? {
|
||||||
|
for (ore in enumValues<Ore>()) {
|
||||||
|
for (rock in ore.objects) {
|
||||||
|
if (rock.key == id) {
|
||||||
|
return ore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
import org.apollo.cache.def.ItemDefinition
|
|
||||||
import org.apollo.cache.def.ObjectDefinition
|
|
||||||
import org.apollo.game.action.DistancedAction
|
|
||||||
import org.apollo.game.message.impl.ObjectActionMessage
|
|
||||||
import org.apollo.game.model.Animation
|
|
||||||
import org.apollo.game.model.Position
|
|
||||||
import org.apollo.game.model.entity.Entity
|
|
||||||
import org.apollo.game.model.entity.EquipmentConstants
|
|
||||||
import org.apollo.game.model.entity.Player
|
|
||||||
import org.apollo.game.model.entity.Skill
|
|
||||||
import org.apollo.game.model.entity.obj.StaticGameObject
|
|
||||||
import org.apollo.game.plugin.skills.mining.*
|
|
||||||
import org.apollo.game.scheduling.ScheduledTask
|
|
||||||
import org.apollo.game.scheduling.Scheduler
|
|
||||||
import java.util.*
|
|
||||||
import kotlin.properties.Delegates
|
|
||||||
|
|
||||||
class MiningAction(val player: Player, val objectID: Int, val p: Position, val ore: Ore) : DistancedAction<Player>(PULSES, true, player, p, ORE_SIZE) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val PULSES = 0
|
|
||||||
private val ORE_SIZE = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private var counter: Int = 0
|
|
||||||
private var started: Boolean = false
|
|
||||||
private val rand: Random = Random()
|
|
||||||
|
|
||||||
override fun executeAction() {
|
|
||||||
val level = mob.skillSet.getSkill(Skill.MINING).currentLevel
|
|
||||||
val pickaxe = findPickaxe()
|
|
||||||
|
|
||||||
|
|
||||||
//check that our pick can mine the ore
|
|
||||||
if (pickaxe == null || level < pickaxe.level) {
|
|
||||||
mob.sendMessage("You do not have a pickaxe for which you have the level to use.")
|
|
||||||
stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//check that we can mine the ore
|
|
||||||
if (level < ore.level) {
|
|
||||||
mob.sendMessage("You do not have the required level to mine this rock.")
|
|
||||||
stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//start the process of mining
|
|
||||||
if (started) {
|
|
||||||
if (counter == 0) {
|
|
||||||
if (!miningSuccessful(ore.chance, ore.chanceOffset, level)) {
|
|
||||||
mine(pickaxe)
|
|
||||||
return //We did not mine the ore... Keep going
|
|
||||||
}
|
|
||||||
//Check inv capacity
|
|
||||||
if (mob.inventory.freeSlots() == 0) {
|
|
||||||
mob.inventory.forceCapacityExceeded()
|
|
||||||
stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (mob.inventory.add(ore.id)) {
|
|
||||||
//TODO: Use lookup from utils once it has a lookup function for IDs
|
|
||||||
val oreName = ItemDefinition.lookup(ore.id).name.toLowerCase();
|
|
||||||
mob.sendMessage("You managed to mine some " + oreName + ".")
|
|
||||||
mob.skillSet.addExperience(Skill.MINING, ore.exp)
|
|
||||||
//Expire ore
|
|
||||||
var rockEntity: StaticGameObject? = null
|
|
||||||
val region = mob.world.regionRepository.fromPosition(position)
|
|
||||||
val entities = region.getEntities(position)
|
|
||||||
for (entity: Entity in entities) {
|
|
||||||
if (entity is StaticGameObject) {
|
|
||||||
if (entity.id == objectID) {
|
|
||||||
rockEntity = entity
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rockEntity == null) { //Mining entity not found at location...
|
|
||||||
stop()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//Get ID of exipred ore
|
|
||||||
val expiredObjectID = ore.objects.get(objectID);
|
|
||||||
val expiredRockEntity = StaticGameObject(mob.world, expiredObjectID!!, position, rockEntity!!.type, rockEntity!!.orientation)
|
|
||||||
//add task to remove normal ore and replace with depleted
|
|
||||||
mob.world.schedule(object: ScheduledTask(0, true) {
|
|
||||||
override fun execute() {
|
|
||||||
//Replace normal ore with expired ore
|
|
||||||
region.removeEntity(rockEntity);
|
|
||||||
region.addEntity(expiredRockEntity)
|
|
||||||
this.stop() //Makes task run once
|
|
||||||
}
|
|
||||||
})
|
|
||||||
//add task to respawn normal ore
|
|
||||||
mob.world.schedule(object: ScheduledTask(ore.respawn, false) {
|
|
||||||
override fun execute() {
|
|
||||||
//Replace expired ore with normal ore
|
|
||||||
region.removeEntity(expiredRockEntity)
|
|
||||||
region.addEntity(rockEntity);
|
|
||||||
this.stop() //Makes task run once
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} //If we did not add to inv, the action will still stop
|
|
||||||
stop(); //Force this action to stop after we are done
|
|
||||||
}
|
|
||||||
counter -= 1
|
|
||||||
} else {
|
|
||||||
started = true
|
|
||||||
mine(pickaxe)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findPickaxe(): Pickaxe? {
|
|
||||||
for (pick in getPickaxes()) {
|
|
||||||
if (pick!!.level > mob.skillSet.getSkill(Skill.MINING).currentLevel) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
val weaponSlot = mob.equipment.get(EquipmentConstants.WEAPON)
|
|
||||||
if (weaponSlot != null && weaponSlot.id == pick.id) {
|
|
||||||
return pick;
|
|
||||||
} else if (mob.inventory.contains(pick.id)) {
|
|
||||||
return pick;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun mine(pickaxe: Pickaxe) {
|
|
||||||
mob.sendMessage("You swing your pick at the rock.")
|
|
||||||
mob.playAnimation(pickaxe.animation)
|
|
||||||
counter = pickaxe.pulses
|
|
||||||
mob.turnTo(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the chance of mining being successful.
|
|
||||||
* Algorithm comes from: http://runescape.wikia.com/wiki/Talk:Mining#Mining_success_rate_formula
|
|
||||||
*/
|
|
||||||
private fun miningSuccessful(oreChance: Double, oreChanceOffset: Boolean, playerLevel: Int): Boolean {
|
|
||||||
val percent: Double
|
|
||||||
if (oreChanceOffset) {
|
|
||||||
percent = (oreChance * playerLevel + 1) * 100
|
|
||||||
} else {
|
|
||||||
percent = (oreChance * playerLevel) * 100
|
|
||||||
}
|
|
||||||
return rand.nextInt(100) < percent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExpiredProspectingAction : DistancedAction<Player> {
|
|
||||||
|
|
||||||
constructor(mob: Player, position: Position) : super(PROSPECT_PULSES, true, mob, position, ORE_SIZE)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val PROSPECT_PULSES = 0
|
|
||||||
private val ORE_SIZE = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun executeAction() {
|
|
||||||
mob.sendMessage("There is currently no ore available in this rock.")
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ProspectingAction(val m: Player, val p: Position, val ore: Ore) : DistancedAction<Player>(PROSPECT_PULSES, true, m, p, ORE_SIZE) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val PROSPECT_PULSES = 3
|
|
||||||
private val ORE_SIZE = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var started = false;
|
|
||||||
|
|
||||||
override fun executeAction() {
|
|
||||||
if (started) {
|
|
||||||
val oreName = ItemDefinition.lookup(ore.id).name.toLowerCase()
|
|
||||||
mob.sendMessage("This rock contains " + oreName + ".")
|
|
||||||
stop();
|
|
||||||
} else {
|
|
||||||
started = true
|
|
||||||
mob.sendMessage("You examine the rock for ores...")
|
|
||||||
mob.turnTo(position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
on {ObjectActionMessage::class}
|
|
||||||
.where {option == 1}
|
|
||||||
.then {
|
|
||||||
if (lookupOreRock(id) != null) {
|
|
||||||
it.startAction(MiningAction(it, id, this.position, lookupOreRock(id)!!))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
on {ObjectActionMessage::class}
|
|
||||||
.where {option == 2}
|
|
||||||
.then {
|
|
||||||
if (lookupOreRock(id) != null) {
|
|
||||||
it.startAction(ProspectingAction(it, this.position, lookupOreRock(id)!!))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user