Refactor mining to use async actions

This commit is contained in:
Gary Tierney
2017-09-17 03:10:15 +01:00
parent a54485e263
commit 0dc6879bdc
7 changed files with 221 additions and 233 deletions
+14
View File
@@ -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.
*/
/**
* 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(
2108 to 450,
2109 to 451,
@@ -153,3 +120,36 @@ val RUNITE_OBJECTS = mapOf(
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)!!))
}
}