diff --git a/game/plugin/skills/mining/build.gradle b/game/plugin/skills/mining/build.gradle new file mode 100644 index 00000000..00a16537 --- /dev/null +++ b/game/plugin/skills/mining/build.gradle @@ -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"] +} \ No newline at end of file diff --git a/game/src/plugins/skills/mining/meta.toml b/game/plugin/skills/mining/meta.toml similarity index 100% rename from game/src/plugins/skills/mining/meta.toml rename to game/plugin/skills/mining/meta.toml diff --git a/game/src/plugins/skills/mining/src/gem.kt b/game/plugin/skills/mining/src/gem.kt similarity index 100% rename from game/src/plugins/skills/mining/src/gem.kt rename to game/plugin/skills/mining/src/gem.kt diff --git a/game/plugin/skills/mining/src/mining.plugin.kts b/game/plugin/skills/mining/src/mining.plugin.kts new file mode 100644 index 00000000..f44020b9 --- /dev/null +++ b/game/plugin/skills/mining/src/mining.plugin.kts @@ -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 { + 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( + 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 { + + 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(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) + } + } diff --git a/game/src/plugins/skills/mining/src/ore.kt b/game/plugin/skills/mining/src/ore.kt similarity index 98% rename from game/src/plugins/skills/mining/src/ore.kt rename to game/plugin/skills/mining/src/ore.kt index 8a67cd80..90001fdc 100644 --- a/game/src/plugins/skills/mining/src/ore.kt +++ b/game/plugin/skills/mining/src/ore.kt @@ -9,39 +9,6 @@ Thanks to 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, 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, 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() + +fun lookupOre(id: Int): Ore? = ORES.find { it.id == id } + +fun lookupOreRock(id: Int): Ore? { + for (ore in enumValues()) { + for (rock in ore.objects) { + if (rock.key == id) { + return ore + } + } + } + return null +} + diff --git a/game/src/plugins/skills/mining/src/pickaxe.kt b/game/plugin/skills/mining/src/pickaxe.kt similarity index 100% rename from game/src/plugins/skills/mining/src/pickaxe.kt rename to game/plugin/skills/mining/src/pickaxe.kt diff --git a/game/src/plugins/skills/mining/src/mining.plugin.kts b/game/src/plugins/skills/mining/src/mining.plugin.kts deleted file mode 100644 index a0cd3e40..00000000 --- a/game/src/plugins/skills/mining/src/mining.plugin.kts +++ /dev/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(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 { - - 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(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)!!)) - } - }