mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-04 08:39:27 +00:00
Update plugin test framework to junit5
Updates the testing infrastructure to use the latest relesae of junit and leverages the new extension mechanism to create an easy to use testing framework. Also adds additional test coverage for several plugins.
This commit is contained in:
@@ -29,30 +29,28 @@ val Player.runecraft: SkillProxy get() = SkillProxy(skillSet, Skill.RUNECRAFT)
|
||||
/**
|
||||
* A proxy class to allow
|
||||
*/
|
||||
class SkillProxy(val skills: SkillSet, val skill: Int) {
|
||||
class SkillProxy(private val skills: SkillSet, private val skill: Int) {
|
||||
|
||||
/**
|
||||
* The maximum level of this skill.
|
||||
*/
|
||||
val maximum = skills.getMaximumLevel(skill)
|
||||
val maximum: Int
|
||||
get() = skills.getMaximumLevel(skill)
|
||||
|
||||
/**
|
||||
* The current level of this skill.
|
||||
*/
|
||||
val current = skills.getCurrentLevel(skill)
|
||||
|
||||
val experience = ExperienceProxy()
|
||||
val current: Int
|
||||
get() = skills.getCurrentLevel(skill)
|
||||
|
||||
/**
|
||||
* A proxy class to make [experience] (effectively) write-only.
|
||||
* The amount of experience in this skill a player has.
|
||||
*/
|
||||
inner class ExperienceProxy {
|
||||
|
||||
operator fun plusAssign(amount: Int) = skills.addExperience(skill, amount.toDouble())
|
||||
|
||||
operator fun plusAssign(amount: Double) = skills.addExperience(skill, amount)
|
||||
|
||||
}
|
||||
var experience: Double
|
||||
get() = skills.getExperience(skill)
|
||||
set(value) {
|
||||
skills.setExperience(skill, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Boosts the current level of this skill by [amount], if possible (i.e. if `current + amount <= maximum + amount`).
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.apollo.game.plugin.api
|
||||
|
||||
import java.util.Random
|
||||
import java.util.*
|
||||
|
||||
val RAND = Random()
|
||||
public val RAND = Random()
|
||||
|
||||
fun rand(bounds: Int): Int {
|
||||
return RAND.nextInt(bounds)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import org.apollo.cache.def.ItemDefinition
|
||||
import org.apollo.game.plugin.api.Definitions
|
||||
import org.apollo.game.plugin.testing.KotlinPluginTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
|
||||
class NamedLookupTests : KotlinPluginTest() {
|
||||
@Test
|
||||
fun itemLookup() {
|
||||
val testItem = ItemDefinition(0)
|
||||
testItem.name = "sword"
|
||||
|
||||
ItemDefinition.init(arrayOf(testItem))
|
||||
|
||||
assertThat(Definitions.item("sword")).isEqualTo(testItem)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,19 @@
|
||||
import org.apollo.game.model.Position
|
||||
import org.apollo.game.plugin.testing.KotlinPluginTest
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
class OpenBankTest() : KotlinPluginTest() {
|
||||
import org.apollo.game.model.Position
|
||||
import org.apollo.game.model.World
|
||||
import org.apollo.game.model.entity.Player
|
||||
import org.apollo.game.plugin.testing.assertions.verifyAfter
|
||||
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||
import org.apollo.game.plugin.testing.junit.api.ActionCapture
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||
import org.apollo.game.plugin.testing.junit.api.interactions.interactWith
|
||||
import org.apollo.game.plugin.testing.junit.api.interactions.spawnNpc
|
||||
import org.apollo.game.plugin.testing.junit.api.interactions.spawnObject
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
|
||||
@ExtendWith(ApolloTestingExtension::class)
|
||||
class OpenBankTest {
|
||||
|
||||
companion object {
|
||||
const val BANK_BOOTH_ID = 2213
|
||||
@@ -12,15 +22,23 @@ class OpenBankTest() : KotlinPluginTest() {
|
||||
val BANK_POSITION = Position(3200, 3200, 0)
|
||||
}
|
||||
|
||||
@TestMock
|
||||
lateinit var action: ActionCapture
|
||||
|
||||
@TestMock
|
||||
lateinit var player: Player
|
||||
|
||||
@TestMock
|
||||
lateinit var world: World
|
||||
|
||||
@Test
|
||||
fun `Interacting with a bank teller should open the players bank`() {
|
||||
val bankTeller = world.spawnNpc(BANK_TELLER_ID, BANK_POSITION)
|
||||
|
||||
// @todo - these option numbers only match by coincidence, we should be looking up the correct ones
|
||||
player.interactWith(bankTeller, option = 2)
|
||||
player.waitForActionCompletion()
|
||||
|
||||
verify(player).openBank()
|
||||
verifyAfter(action.complete()) { player.openBank() }
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -28,9 +46,8 @@ class OpenBankTest() : KotlinPluginTest() {
|
||||
val bankBooth = world.spawnObject(BANK_BOOTH_ID, BANK_POSITION)
|
||||
|
||||
player.interactWith(bankBooth, option = 2)
|
||||
player.waitForActionCompletion()
|
||||
|
||||
verify(player).openBank()
|
||||
verifyAfter(action.complete()) { player.openBank() }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,21 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'org.junit.platform:junit-platform-gradle-plugin:1.1.0'
|
||||
}
|
||||
}
|
||||
|
||||
subprojects { subproj ->
|
||||
if (subproj.buildFile.exists()) {
|
||||
apply plugin: 'apollo-plugin'
|
||||
apply plugin: 'org.junit.platform.gradle.plugin'
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation group: 'com.google.guava', name: 'guava', version: guavaVersion
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
package org.apollo.plugin.consumables
|
||||
|
||||
import org.apollo.game.message.impl.ItemOptionMessage
|
||||
import io.mockk.verify
|
||||
import org.apollo.game.model.entity.Player
|
||||
import org.apollo.game.model.entity.Skill
|
||||
import org.apollo.game.plugin.testing.KotlinPluginTest
|
||||
import org.apollo.game.plugin.testing.mockito.KotlinMockitoExtensions.matches
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.verify
|
||||
import org.apollo.game.plugin.testing.assertions.contains
|
||||
import org.apollo.game.plugin.testing.assertions.after
|
||||
import org.apollo.game.plugin.testing.assertions.verifyAfter
|
||||
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||
import org.apollo.game.plugin.testing.junit.api.ActionCapture
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||
import org.apollo.game.plugin.testing.junit.api.interactions.interactWithItem
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
|
||||
class FoodOrDrinkTests : KotlinPluginTest() {
|
||||
@ExtendWith(ApolloTestingExtension::class)
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class FoodOrDrinkTests {
|
||||
|
||||
companion object {
|
||||
const val TEST_FOOD_NAME = "test_food"
|
||||
@@ -25,9 +33,14 @@ class FoodOrDrinkTests : KotlinPluginTest() {
|
||||
const val MAX_HP_LEVEL = 10
|
||||
}
|
||||
|
||||
@Before override fun setup() {
|
||||
super.setup()
|
||||
@TestMock
|
||||
lateinit var player: Player
|
||||
|
||||
@TestMock
|
||||
lateinit var action: ActionCapture
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
val skills = player.skillSet
|
||||
skills.setCurrentLevel(Skill.HITPOINTS, HP_LEVEL)
|
||||
skills.setMaximumLevel(Skill.HITPOINTS, MAX_HP_LEVEL)
|
||||
@@ -36,51 +49,47 @@ class FoodOrDrinkTests : KotlinPluginTest() {
|
||||
drink("test_drink", TEST_DRINK_ID, TEST_DRINK_RESTORATION)
|
||||
}
|
||||
|
||||
@Test fun `Consuming food or drink should restore the players hitpoints`() {
|
||||
@Test
|
||||
fun `Consuming food or drink should restore the players hitpoints`() {
|
||||
val expectedHpLevel = TEST_FOOD_RESTORATION + HP_LEVEL
|
||||
|
||||
player.notify(ItemOptionMessage(1, -1, TEST_FOOD_ID, 1))
|
||||
player.waitForActionCompletion()
|
||||
player.interactWithItem(TEST_FOOD_ID, option = 1, slot = 1)
|
||||
|
||||
val currentHpLevel = player.skillSet.getCurrentLevel(Skill.HITPOINTS)
|
||||
assertThat(currentHpLevel).isEqualTo(expectedHpLevel)
|
||||
after(action.complete()) {
|
||||
assertEquals(expectedHpLevel, player.skillSet.getCurrentLevel(Skill.HITPOINTS))
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun `A message should be sent notifying the player if the item restored hitpoints`() {
|
||||
player.notify(ItemOptionMessage(1, -1, TEST_FOOD_ID, 1))
|
||||
player.waitForActionCompletion()
|
||||
@Test
|
||||
fun `A message should be sent notifying the player if the item restored hitpoints`() {
|
||||
player.interactWithItem(TEST_FOOD_ID, option = 1, slot = 1)
|
||||
|
||||
verify(player).sendMessage(matches {
|
||||
assertThat(this).contains("heals some health")
|
||||
})
|
||||
verifyAfter(action.complete()) { player.sendMessage("It heals some health.") }
|
||||
}
|
||||
|
||||
@Test fun `A message should not be sent to the player if the item did not restore hitpoints`() {
|
||||
@Test
|
||||
fun `A message should not be sent to the player if the item did not restore hitpoints`() {
|
||||
player.skillSet.setCurrentLevel(Skill.HITPOINTS, MAX_HP_LEVEL)
|
||||
player.notify(ItemOptionMessage(1, -1, TEST_FOOD_ID, 1))
|
||||
player.waitForActionCompletion()
|
||||
player.interactWithItem(TEST_FOOD_ID, option = 1, slot = 1)
|
||||
|
||||
verify(player, never()).sendMessage(matches {
|
||||
assertThat(this).contains("heals some health")
|
||||
})
|
||||
after(action.complete()) {
|
||||
verify(exactly = 0) { player.sendMessage(contains("it heals some")) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun `A message should be sent saying the player has drank an item when consuming a drink`() {
|
||||
player.notify(ItemOptionMessage(1, -1, TEST_DRINK_ID, 1))
|
||||
player.waitForActionCompletion()
|
||||
@Test
|
||||
fun `A message should be sent saying the player has drank an item when consuming a drink`() {
|
||||
player.interactWithItem(TEST_DRINK_ID, option = 1, slot = 1)
|
||||
|
||||
verifyAfter(action.complete()) { player.sendMessage("You drink the ${TEST_DRINK_NAME}.") }
|
||||
|
||||
verify(player).sendMessage(matches {
|
||||
assertThat(this).contains("You drink the ${TEST_DRINK_NAME}")
|
||||
})
|
||||
}
|
||||
|
||||
@Test fun `A message should be sent saying the player has eaten an item when consuming food`() {
|
||||
player.notify(ItemOptionMessage(1, -1, TEST_FOOD_ID, 1))
|
||||
player.waitForActionCompletion()
|
||||
@Test
|
||||
fun `A message should be sent saying the player has eaten an item when consuming food`() {
|
||||
player.interactWithItem(TEST_FOOD_ID, option = 1, slot = 1)
|
||||
|
||||
verify(player).sendMessage(matches {
|
||||
assertThat(this).contains("You eat the ${TEST_FOOD_NAME}")
|
||||
})
|
||||
verifyAfter(action.complete()) { player.sendMessage("You eat the ${TEST_FOOD_NAME}.") }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +1,64 @@
|
||||
import org.apollo.game.model.Position
|
||||
import org.apollo.game.model.entity.Skill
|
||||
import org.apollo.game.plugin.testing.*
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.contains
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
class TrainingDummyTest : KotlinPluginTest() {
|
||||
import io.mockk.verify
|
||||
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.plugin.testing.assertions.after
|
||||
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||
import org.apollo.game.plugin.testing.junit.api.ActionCapture
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||
import org.apollo.game.plugin.testing.junit.api.interactions.interactWith
|
||||
import org.apollo.game.plugin.testing.junit.api.interactions.spawnObject
|
||||
import org.apollo.game.plugin.testing.assertions.contains
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
|
||||
@ExtendWith(ApolloTestingExtension::class)
|
||||
class TrainingDummyTest {
|
||||
|
||||
companion object {
|
||||
const val DUMMY_ID = 823
|
||||
val DUMMY_POSITION = Position(3200, 3230, 0)
|
||||
}
|
||||
|
||||
@Test fun `Hitting the training dummy should give the player attack experience`() {
|
||||
@TestMock
|
||||
lateinit var action: ActionCapture
|
||||
|
||||
@TestMock
|
||||
lateinit var player: Player
|
||||
|
||||
@TestMock
|
||||
lateinit var world: World
|
||||
|
||||
@Test
|
||||
fun `Hitting the training dummy should give the player attack experience`() {
|
||||
val dummy = world.spawnObject(DUMMY_ID, DUMMY_POSITION)
|
||||
val skills = player.skillSet
|
||||
val beforeExp = skills.getExperience(Skill.ATTACK)
|
||||
|
||||
player.interactWith(dummy, option = 2)
|
||||
player.waitForActionCompletion()
|
||||
|
||||
val afterExp = skills.getExperience(Skill.ATTACK)
|
||||
assertThat(afterExp).isGreaterThan(beforeExp)
|
||||
after(action.complete()) {
|
||||
assertTrue(skills.getExperience(Skill.ATTACK) > beforeExp)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun `The player should stop getting attack experience from the training dummy at level 8`() {
|
||||
@Test
|
||||
fun `The player should stop getting attack experience from the training dummy at level 8`() {
|
||||
val dummy = world.spawnObject(DUMMY_ID, DUMMY_POSITION)
|
||||
val skills = player.skillSet
|
||||
skills.setMaximumLevel(Skill.ATTACK, 8)
|
||||
val beforeExp = skills.getExperience(Skill.ATTACK)
|
||||
|
||||
player.interactWith(dummy, option = 2)
|
||||
player.waitForActionCompletion()
|
||||
|
||||
val afterExp = skills.getExperience(Skill.ATTACK)
|
||||
|
||||
verify(player).sendMessage(contains("nothing more you can learn"))
|
||||
assertThat(afterExp).isEqualTo(beforeExp)
|
||||
after(action.complete()) {
|
||||
verify { player.sendMessage(contains("nothing more you can learn")) }
|
||||
assertEquals(beforeExp, skills.getExperience(Skill.ATTACK))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
import org.apollo.game.message.impl.ButtonMessage
|
||||
import org.apollo.game.plugin.testing.KotlinPluginTest
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
|
||||
class LogoutTests : KotlinPluginTest() {
|
||||
import io.mockk.verify
|
||||
import org.apollo.game.message.impl.ButtonMessage
|
||||
import org.apollo.game.model.entity.Player
|
||||
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
|
||||
@ExtendWith(ApolloTestingExtension::class)
|
||||
class LogoutTests {
|
||||
|
||||
companion object {
|
||||
const val LOGOUT_BUTTON_ID = 2458
|
||||
}
|
||||
|
||||
@Test fun `The player should be logged out when they click the logout button`() {
|
||||
player.notify(ButtonMessage(LOGOUT_BUTTON_ID))
|
||||
@TestMock
|
||||
lateinit var player: Player
|
||||
|
||||
verify(player, times(1)).logout()
|
||||
@Test
|
||||
fun `The player should be logged out when they click the logout button`() {
|
||||
player.send(ButtonMessage(LOGOUT_BUTTON_ID))
|
||||
|
||||
verify { player.logout() }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,12 +9,12 @@ import org.apollo.game.plugin.skills.fishing.FishingTool.*
|
||||
/**
|
||||
* A fish that can be gathered using the fishing skill.
|
||||
*/
|
||||
enum class Fish(val id: Int, val level: Int, val experience: Double) {
|
||||
SHRIMP(id = 317, level = 1, experience = 10.0),
|
||||
enum class Fish(val id: Int, val level: Int, val experience: Double, catchSuffix: String? = null) {
|
||||
SHRIMPS(id = 317, level = 1, experience = 10.0, catchSuffix = "some shrimp."),
|
||||
SARDINE(id = 327, level = 5, experience = 20.0),
|
||||
MACKEREL(id = 353, level = 16, experience = 20.0),
|
||||
HERRING(id = 345, level = 10, experience = 30.0),
|
||||
ANCHOVY(id = 321, level = 15, experience = 40.0),
|
||||
ANCHOVIES(id = 321, level = 15, experience = 40.0, catchSuffix = "some anchovies."),
|
||||
TROUT(id = 335, level = 20, experience = 50.0),
|
||||
COD(id = 341, level = 23, experience = 45.0),
|
||||
PIKE(id = 349, level = 25, experience = 60.0),
|
||||
@@ -23,13 +23,14 @@ enum class Fish(val id: Int, val level: Int, val experience: Double) {
|
||||
LOBSTER(id = 377, level = 40, experience = 90.0),
|
||||
BASS(id = 363, level = 46, experience = 100.0),
|
||||
SWORDFISH(id = 371, level = 50, experience = 100.0),
|
||||
SHARK(id = 383, level = 76, experience = 110.0);
|
||||
SHARK(id = 383, level = 76, experience = 110.0, catchSuffix = "a shark!");
|
||||
|
||||
/**
|
||||
* The name of this fish, formatted so it can be inserted into a message.
|
||||
*/
|
||||
val formattedName = Definitions.item(id)!!.name.toLowerCase()
|
||||
// TODO this leads to incorrect messages, e.g. 'You catch a raw shrimps'.
|
||||
val catchMessage = "You catch ${catchSuffix ?: "a ${catchName()}."}"
|
||||
|
||||
private fun catchName() = Definitions.item(id)!!.name.toLowerCase().removePrefix("raw ")
|
||||
|
||||
}
|
||||
|
||||
@@ -69,16 +70,7 @@ enum class FishingSpot(val npc: Int, private val first: Option, private val seco
|
||||
ROD(309, Option.of(FLY_FISHING_ROD, TROUT, SALMON), Option.of(FISHING_ROD, PIKE)),
|
||||
CAGE_HARPOON(312, Option.of(LOBSTER_CAGE, LOBSTER), Option.of(HARPOON, TUNA, SWORDFISH)),
|
||||
NET_HARPOON(313, Option.of(BIG_NET, MACKEREL, COD), Option.of(HARPOON, BASS, SHARK)),
|
||||
NET_ROD(316, Option.of(SMALL_NET, SHRIMP, ANCHOVY), Option.of(FISHING_ROD, SARDINE, HERRING));
|
||||
|
||||
companion object {
|
||||
private val FISHING_SPOTS = FishingSpot.values().associateBy({ it.npc }, { it })
|
||||
|
||||
/**
|
||||
* Returns the [FishingSpot] with the specified [id], or `null` if the spot does not exist.
|
||||
*/
|
||||
fun lookup(id: Int): FishingSpot? = FISHING_SPOTS[id]
|
||||
}
|
||||
NET_ROD(316, Option.of(SMALL_NET, SHRIMPS, ANCHOVIES), Option.of(FISHING_ROD, SARDINE, HERRING));
|
||||
|
||||
/**
|
||||
* Returns the [FishingSpot.Option] associated with the specified action id.
|
||||
@@ -159,4 +151,13 @@ enum class FishingSpot(val npc: Int, private val first: Option, private val seco
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val FISHING_SPOTS = FishingSpot.values().associateBy(FishingSpot::npc)
|
||||
|
||||
/**
|
||||
* Returns the [FishingSpot] with the specified [id], or `null` if the spot does not exist.
|
||||
*/
|
||||
fun lookup(id: Int): FishingSpot? = FISHING_SPOTS[id]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ class FishingAction(player: Player, position: Position, val option: FishingSpot.
|
||||
}
|
||||
|
||||
mob.inventory.add(fish.id)
|
||||
mob.sendMessage("You catch a ${fish.formattedName}.")
|
||||
mob.sendMessage(fish.catchMessage)
|
||||
mob.fishing.experience += fish.experience
|
||||
|
||||
if (mob.inventory.freeSlots() == 0) {
|
||||
|
||||
@@ -10,5 +10,5 @@ plugin {
|
||||
"tlf30"
|
||||
]
|
||||
|
||||
dependencies = ["api"]
|
||||
dependencies = ["api", "util:lookup"]
|
||||
}
|
||||
@@ -8,6 +8,6 @@ enum class Gem(val id: Int) { // TODO add gem drop chances
|
||||
|
||||
companion object {
|
||||
private val GEMS = Gem.values().associateBy({ it.id }, { it })
|
||||
fun lookup(id: Int): Gem? = GEMS[id]
|
||||
operator fun get(id: Int): Gem? = GEMS[id]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import org.apollo.game.action.ActionBlock
|
||||
import org.apollo.game.action.AsyncDistancedAction
|
||||
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.obj.GameObject
|
||||
import org.apollo.game.plugin.api.*
|
||||
import org.apollo.game.plugin.skills.mining.Ore
|
||||
import org.apollo.game.plugin.skills.mining.Pickaxe
|
||||
import org.apollo.net.message.Message
|
||||
import java.util.*
|
||||
|
||||
class MiningAction(
|
||||
player: Player,
|
||||
private val tool: Pickaxe,
|
||||
private val target: MiningTarget
|
||||
) : AsyncDistancedAction<Player>(PULSES, true, player, target.position, ORE_SIZE) {
|
||||
|
||||
companion object {
|
||||
private const val PULSES = 0
|
||||
private const val ORE_SIZE = 1
|
||||
|
||||
/**
|
||||
* Starts a [MiningAction] for the specified [Player], terminating the [Message] that triggered it.
|
||||
*/
|
||||
fun start(message: ObjectActionMessage, player: Player, ore: Ore) {
|
||||
val pickaxe = Pickaxe.bestFor(player)
|
||||
|
||||
if (pickaxe == null) {
|
||||
player.sendMessage("You do not have a pickaxe for which you have the level to use.")
|
||||
} else {
|
||||
val target = MiningTarget(message.id, message.position, ore)
|
||||
val action = MiningAction(player, pickaxe, target)
|
||||
|
||||
player.startAction(action)
|
||||
}
|
||||
|
||||
message.terminate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun action(): ActionBlock = {
|
||||
mob.turnTo(position)
|
||||
|
||||
if (!target.skillRequirementsMet(mob)) {
|
||||
mob.sendMessage("You do not have the required level to mine this rock.")
|
||||
stop()
|
||||
}
|
||||
|
||||
mob.sendMessage("You swing your pick at the rock.")
|
||||
mob.playAnimation(tool.animation)
|
||||
|
||||
wait(tool.pulses)
|
||||
|
||||
if (!target.isValid(mob.world)) {
|
||||
stop()
|
||||
}
|
||||
|
||||
val successChance = rand(100)
|
||||
|
||||
if (target.isSuccessful(mob, successChance)) {
|
||||
if (mob.inventory.freeSlots() == 0) {
|
||||
mob.inventory.forceCapacityExceeded()
|
||||
stop()
|
||||
}
|
||||
|
||||
if (target.reward(mob)) {
|
||||
mob.sendMessage("You manage to mine some ${target.oreName()}")
|
||||
target.deplete(mob.world)
|
||||
|
||||
stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as MiningAction
|
||||
return mob == other.mob && target == other.target
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(mob, target)
|
||||
|
||||
}
|
||||
|
||||
data class MiningTarget(val objectId: Int, val position: Position, val ore: Ore) {
|
||||
|
||||
/**
|
||||
* Get the [GameObject] represented by this target.
|
||||
*
|
||||
* @todo: api: shouldn't be as verbose
|
||||
*/
|
||||
private fun getObject(world: World): GameObject? {
|
||||
val region = world.regionRepository.fromPosition(position)
|
||||
return region.findObject(position, objectId).orElse(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deplete this mining resource from the [World], and schedule it to be respawned
|
||||
* in a number of ticks specified by the [Ore].
|
||||
*/
|
||||
fun deplete(world: World) {
|
||||
world.expireObject(getObject(world)!!, ore.objects[objectId]!!, ore.respawn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the [Player] was successful in mining this ore with a random success [chance] value between 0 and 100.
|
||||
*/
|
||||
fun isSuccessful(mob: Player, chance: Int): Boolean {
|
||||
val percent = (ore.chance * mob.mining.current + ore.chanceOffset) * 100
|
||||
return chance < percent
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this target is still valid in the [World] (i.e. has not been [deplete]d).
|
||||
*/
|
||||
fun isValid(world: World) = getObject(world) != null
|
||||
|
||||
/**
|
||||
* Get the normalized name of the [Ore] represented by this target.
|
||||
*/
|
||||
fun oreName() = Definitions.item(ore.id)!!.name.toLowerCase()
|
||||
|
||||
/**
|
||||
* Reward a [player] with experience and ore if they have the inventory capacity to take a new ore.
|
||||
*/
|
||||
fun reward(player: Player): Boolean {
|
||||
val hasInventorySpace = player.inventory.add(ore.id)
|
||||
|
||||
if (hasInventorySpace) {
|
||||
player.mining.experience += ore.exp
|
||||
}
|
||||
|
||||
return hasInventorySpace
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the [mob] has met the skill requirements to mine te [Ore] represented by
|
||||
* this [MiningTarget].
|
||||
*/
|
||||
fun skillRequirementsMet(mob: Player) = mob.mining.current < ore.level
|
||||
}
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
import org.apollo.game.action.ActionBlock
|
||||
import org.apollo.game.action.AsyncDistancedAction
|
||||
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.obj.GameObject
|
||||
import org.apollo.game.plugin.api.Definitions
|
||||
import org.apollo.game.plugin.api.expireObject
|
||||
import org.apollo.game.plugin.api.findObject
|
||||
import org.apollo.game.plugin.api.mining
|
||||
import org.apollo.game.plugin.api.rand
|
||||
import org.apollo.game.plugin.skills.mining.Ore
|
||||
import org.apollo.game.plugin.skills.mining.Pickaxe
|
||||
import org.apollo.net.message.Message
|
||||
import java.util.Objects
|
||||
|
||||
on { ObjectActionMessage::class }
|
||||
.where { option == Actions.MINING }
|
||||
@@ -35,99 +21,6 @@ on { ObjectActionMessage::class }
|
||||
}
|
||||
}
|
||||
|
||||
class MiningAction(
|
||||
player: Player,
|
||||
private val tool: Pickaxe,
|
||||
private val target: MiningTarget
|
||||
) : AsyncDistancedAction<Player>(PULSES, true, player, target.position, ORE_SIZE) {
|
||||
|
||||
companion object {
|
||||
private const val PULSES = 0
|
||||
private const val ORE_SIZE = 1
|
||||
|
||||
/**
|
||||
* Starts a [MiningAction] for the specified [Player], terminating the [Message] that triggered it.
|
||||
*/
|
||||
fun start(message: ObjectActionMessage, player: Player, ore: Ore) {
|
||||
val pickaxe = Pickaxe.bestFor(player)
|
||||
|
||||
if (pickaxe == null) {
|
||||
player.sendMessage("You do not have a pickaxe for which you have the level to use.")
|
||||
} else {
|
||||
val target = MiningTarget(message.id, message.position, ore)
|
||||
val action = MiningAction(player, pickaxe, target)
|
||||
|
||||
player.startAction(action)
|
||||
}
|
||||
|
||||
message.terminate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun action(): ActionBlock = {
|
||||
mob.turnTo(position)
|
||||
|
||||
val level = mob.mining.current
|
||||
if (level < target.ore.level) {
|
||||
mob.sendMessage("You do not have the required level to mine this rock.")
|
||||
stop()
|
||||
}
|
||||
|
||||
mob.sendMessage("You swing your pick at the rock.")
|
||||
mob.playAnimation(tool.animation)
|
||||
|
||||
wait(tool.pulses)
|
||||
|
||||
val obj = target.getObject(mob.world)
|
||||
if (obj == null) {
|
||||
stop()
|
||||
}
|
||||
|
||||
if (target.isSuccessful(mob)) {
|
||||
if (mob.inventory.freeSlots() == 0) {
|
||||
mob.inventory.forceCapacityExceeded()
|
||||
stop()
|
||||
}
|
||||
|
||||
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.mining.experience += target.ore.exp
|
||||
mob.world.expireObject(obj!!, target.ore.objects[target.objectId]!!, target.ore.respawn)
|
||||
stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as MiningAction
|
||||
return mob == other.mob && target == other.target
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(mob, target)
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private object Actions {
|
||||
const val MINING = 1
|
||||
const val PROSPECTING = 2
|
||||
|
||||
@@ -17,12 +17,12 @@ enum class Ore(
|
||||
val exp: Double,
|
||||
val respawn: Int,
|
||||
val chance: Double,
|
||||
val chanceOffset: Boolean = false
|
||||
val chanceOffset: Double = 0.0
|
||||
) {
|
||||
CLAY(CLAY_OBJECTS, id = 434, level = 1, exp = 5.0, respawn = 1, chance = 0.0085, chanceOffset = true),
|
||||
COPPER(COPPER_OBJECTS, id = 436, level = 1, exp = 17.5, respawn = 4, chance = 0.0085, chanceOffset = true),
|
||||
TIN(TIN_OBJECTS, id = 438, level = 1, exp = 17.5, respawn = 4, chance = 0.0085, chanceOffset = true),
|
||||
IRON(IRON_OBJECTS, id = 440, level = 15, exp = 35.0, respawn = 9, chance = 0.0085, chanceOffset = true),
|
||||
CLAY(CLAY_OBJECTS, id = 434, level = 1, exp = 5.0, respawn = 1, chance = 0.0085, chanceOffset = 0.45),
|
||||
COPPER(COPPER_OBJECTS, id = 436, level = 1, exp = 17.5, respawn = 4, chance = 0.0085, chanceOffset = 0.45),
|
||||
TIN(TIN_OBJECTS, id = 438, level = 1, exp = 17.5, respawn = 4, chance = 0.0085, chanceOffset = 0.45),
|
||||
IRON(IRON_OBJECTS, id = 440, level = 15, exp = 35.0, respawn = 9, chance = 0.0085, chanceOffset = 0.45),
|
||||
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),
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.apollo.game.plugin.api.mining
|
||||
enum class Pickaxe(val id: Int, val level: Int, animation: Int, val pulses: Int) {
|
||||
BRONZE(id = 1265, level = 1, animation = 625, pulses = 8),
|
||||
ITRON(id = 1267, level = 1, animation = 626, pulses = 7),
|
||||
STEEL(id = 1269, level = 1, animation = 627, pulses = 6),
|
||||
STEEL(id = 1269, level = 6, animation = 627, pulses = 6),
|
||||
MITHRIL(id = 1273, level = 21, animation = 629, pulses = 5),
|
||||
ADAMANT(id = 1271, level = 31, animation = 628, pulses = 4),
|
||||
RUNE(id = 1275, level = 41, animation = 624, pulses = 3);
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.spyk
|
||||
import io.mockk.staticMockk
|
||||
import io.mockk.verify
|
||||
import org.apollo.cache.def.ItemDefinition
|
||||
import org.apollo.game.model.World
|
||||
import org.apollo.game.model.entity.Player
|
||||
import org.apollo.game.model.entity.Skill
|
||||
import org.apollo.game.plugin.api.expireObject
|
||||
import org.apollo.game.plugin.skills.mining.Ore
|
||||
import org.apollo.game.plugin.skills.mining.Pickaxe
|
||||
import org.apollo.game.plugin.skills.mining.TIN_OBJECTS
|
||||
import org.apollo.game.plugin.testing.assertions.after
|
||||
import org.apollo.game.plugin.testing.assertions.verifyAfter
|
||||
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||
import org.apollo.game.plugin.testing.junit.api.ActionCapture
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.ItemDefinitions
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||
import org.apollo.game.plugin.testing.junit.api.interactions.spawnObject
|
||||
import org.apollo.game.plugin.testing.assertions.contains
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
|
||||
@ExtendWith(ApolloTestingExtension::class)
|
||||
class MiningActionTests {
|
||||
private val TIN_OBJ_IDS = TIN_OBJECTS.entries.first()
|
||||
|
||||
@ItemDefinitions
|
||||
fun ores() = Ore.values()
|
||||
.map { ItemDefinition(it.id).also { it.name = "<ore_type>" } }
|
||||
|
||||
@ItemDefinitions
|
||||
fun pickaxes() = listOf(ItemDefinition(Pickaxe.BRONZE.id))
|
||||
|
||||
@TestMock
|
||||
lateinit var world: World
|
||||
|
||||
@TestMock
|
||||
lateinit var player: Player
|
||||
|
||||
@TestMock
|
||||
lateinit var action: ActionCapture
|
||||
|
||||
@Test
|
||||
fun `Attempting to mine a rock we don't have the skill to should send the player a message`() {
|
||||
val obj = world.spawnObject(1, player.position)
|
||||
val target = spyk(MiningTarget(obj.id, obj.position, Ore.TIN))
|
||||
|
||||
every { target.skillRequirementsMet(player) } returns false
|
||||
player.startAction(MiningAction(player, Pickaxe.BRONZE, target))
|
||||
verifyAfter(action.complete()) { player.sendMessage(contains("do not have the required level")) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Mining a rock we have the skill to mine should eventually reward ore and experience`() {
|
||||
val (tinId, expiredTinId) = TIN_OBJ_IDS
|
||||
val obj = world.spawnObject(tinId, player.position)
|
||||
val target = spyk(MiningTarget(obj.id, obj.position, Ore.TIN))
|
||||
staticMockk("org.apollo.game.plugin.api.WorldKt").mock()
|
||||
|
||||
every { target.skillRequirementsMet(player) } returns true
|
||||
every { target.isSuccessful(player, any()) } returns true
|
||||
every { world.expireObject(obj, any(), any()) } answers {}
|
||||
|
||||
player.skillSet.setCurrentLevel(Skill.MINING, Ore.TIN.level)
|
||||
player.startAction(MiningAction(player, Pickaxe.BRONZE, target))
|
||||
|
||||
verifyAfter(action.ticks(1)) { player.sendMessage(contains("You swing your pick")) }
|
||||
after(action.complete()) {
|
||||
verify { player.sendMessage("You manage to mine some <ore_type>") }
|
||||
verify { world.expireObject(obj, expiredTinId, Ore.TIN.respawn) }
|
||||
|
||||
assertTrue(player.inventory.contains(Ore.TIN.id))
|
||||
assertEquals(player.skillSet.getExperience(Skill.MINING), Ore.TIN.exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import org.apollo.cache.def.ItemDefinition
|
||||
import org.apollo.game.model.entity.Player
|
||||
import org.apollo.game.model.entity.Skill
|
||||
import org.apollo.game.plugin.skills.mining.Pickaxe
|
||||
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.ItemDefinitions
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.EnumSource
|
||||
|
||||
@ExtendWith(ApolloTestingExtension::class)
|
||||
class PickaxeTests {
|
||||
|
||||
@ItemDefinitions
|
||||
fun pickaxes() = Pickaxe.values().map {
|
||||
ItemDefinition(it.id).apply { isStackable = false }
|
||||
}
|
||||
|
||||
@TestMock
|
||||
lateinit var player: Player
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Pickaxe::class)
|
||||
fun `No pickaxe is chosen if none are available`(pickaxe: Pickaxe) {
|
||||
player.skillSet.setCurrentLevel(Skill.MINING, pickaxe.level)
|
||||
|
||||
assertEquals(null, Pickaxe.bestFor(player))
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Pickaxe::class)
|
||||
fun `The highest level pickaxe is chosen when available`(pickaxe: Pickaxe) {
|
||||
player.skillSet.setCurrentLevel(Skill.MINING, pickaxe.level)
|
||||
player.inventory.add(pickaxe.id)
|
||||
|
||||
assertEquals(pickaxe, Pickaxe.bestFor(player))
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Pickaxe::class)
|
||||
fun `Only pickaxes the player has are chosen`(pickaxe: Pickaxe) {
|
||||
player.skillSet.setCurrentLevel(Skill.MINING, pickaxe.level)
|
||||
player.inventory.add(Pickaxe.BRONZE.id)
|
||||
|
||||
assertEquals(Pickaxe.BRONZE, Pickaxe.bestFor(player))
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = Pickaxe::class)
|
||||
fun `Pickaxes can be chosen from equipment as well as inventory`(pickaxe: Pickaxe) {
|
||||
player.skillSet.setCurrentLevel(Skill.MINING, pickaxe.level)
|
||||
player.inventory.add(pickaxe.id)
|
||||
|
||||
assertEquals(pickaxe, Pickaxe.bestFor(player))
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = Pickaxe::class)
|
||||
fun `Pickaxes with a level requirement higher than the player's are ignored`(pickaxe: Pickaxe) {
|
||||
player.skillSet.setCurrentLevel(Skill.MINING, pickaxe.level)
|
||||
player.inventory.add(pickaxe.id)
|
||||
|
||||
Pickaxe.values()
|
||||
.filter { it.level > pickaxe.level }
|
||||
.forEach { player.inventory.add(it.id) }
|
||||
|
||||
assertEquals(pickaxe, Pickaxe.bestFor(player))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
import org.apollo.cache.def.ItemDefinition
|
||||
import org.apollo.game.model.entity.Player
|
||||
import org.apollo.game.plugin.skills.mining.Ore
|
||||
import org.apollo.game.plugin.testing.assertions.verifyAfter
|
||||
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||
import org.apollo.game.plugin.testing.junit.api.ActionCapture
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.ItemDefinitions
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||
import org.apollo.game.plugin.testing.junit.api.interactions.interactWithObject
|
||||
import org.apollo.game.plugin.testing.assertions.contains
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource
|
||||
|
||||
@ExtendWith(ApolloTestingExtension::class)
|
||||
class ProspectingTests {
|
||||
|
||||
@ItemDefinitions
|
||||
fun ores() = Ore.values().map {
|
||||
ItemDefinition(it.id).also { it.name = "<ore_type>" }
|
||||
}
|
||||
|
||||
@TestMock
|
||||
lateinit var player: Player
|
||||
|
||||
@TestMock
|
||||
lateinit var action: ActionCapture
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(MiningTestDataProvider::class)
|
||||
fun `Prospecting a rock should reveal the type of ore it contains`(data: MiningTestData) {
|
||||
player.interactWithObject(data.rockId, 2)
|
||||
|
||||
verifyAfter(action.ticks(1)) { player.sendMessage(contains("examine the rock")) }
|
||||
verifyAfter(action.complete()) { player.sendMessage(contains("This rock contains <ore_type>")) }
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(MiningTestDataProvider::class)
|
||||
fun `Prospecting an expired rock should reveal it contains no ore`(data: MiningTestData) {
|
||||
player.interactWithObject(data.expiredRockId, 2)
|
||||
|
||||
verifyAfter(action.complete()) { player.sendMessage(contains("no ore available in this rock")) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import org.apollo.game.plugin.skills.mining.Ore
|
||||
import org.junit.jupiter.api.extension.ExtensionContext
|
||||
import org.junit.jupiter.params.provider.Arguments
|
||||
import org.junit.jupiter.params.provider.ArgumentsProvider
|
||||
import java.util.stream.Stream
|
||||
|
||||
data class MiningTestData(val rockId: Int, val expiredRockId: Int, val ore: Ore)
|
||||
|
||||
fun miningTestData(): Collection<MiningTestData> = Ore.values()
|
||||
.flatMap { ore -> ore.objects.map { MiningTestData(it.key, it.value, ore) } }
|
||||
.toList()
|
||||
|
||||
class MiningTestDataProvider : ArgumentsProvider {
|
||||
override fun provideArguments(context: ExtensionContext?): Stream<out Arguments> {
|
||||
return miningTestData().map { Arguments { arrayOf(it) } }.stream()
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ class BuryBoneAction(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val BURY_BONE_ANIMATION = Animation(827)
|
||||
public val BURY_BONE_ANIMATION = Animation(827)
|
||||
internal const val BURY_OPTION = 1
|
||||
}
|
||||
|
||||
|
||||
@@ -34,4 +34,4 @@ on { ItemOptionMessage::class }
|
||||
|
||||
player.startAction(BuryBoneAction(player, slot, bone))
|
||||
terminate()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
import BuryBoneAction.Companion.BURY_BONE_ANIMATION
|
||||
import io.mockk.verify
|
||||
import org.apollo.cache.def.ItemDefinition
|
||||
import org.apollo.game.model.entity.Player
|
||||
import org.apollo.game.plugin.api.prayer
|
||||
import org.apollo.game.plugin.testing.assertions.after
|
||||
import org.apollo.game.plugin.testing.assertions.startsWith
|
||||
import org.apollo.game.plugin.testing.assertions.verifyAfter
|
||||
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||
import org.apollo.game.plugin.testing.junit.api.ActionCapture
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.ItemDefinitions
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||
import org.apollo.game.plugin.testing.junit.api.interactions.interactWithItem
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.EnumSource
|
||||
|
||||
@ExtendWith(ApolloTestingExtension::class)
|
||||
class BuryBoneTests {
|
||||
|
||||
@TestMock
|
||||
lateinit var player: Player
|
||||
|
||||
@TestMock
|
||||
lateinit var action: ActionCapture
|
||||
|
||||
@ItemDefinitions
|
||||
fun bones(): Collection<ItemDefinition> {
|
||||
return Bone.values().map { ItemDefinition(it.id) }
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = Bone::class)
|
||||
fun `Burying a bone should send a message`(bone: Bone) {
|
||||
player.inventory.add(bone.id)
|
||||
player.interactWithItem(bone.id, option = 1)
|
||||
|
||||
verifyAfter(action.ticks(1), "message is sent") {
|
||||
player.sendMessage(startsWith("You dig a hole"))
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = Bone::class)
|
||||
fun `Burying a bone should play an animation`(bone: Bone) {
|
||||
player.inventory.add(bone.id)
|
||||
player.interactWithItem(bone.id, option = 1)
|
||||
|
||||
verifyAfter(action.ticks(1), "animation is played") {
|
||||
player.playAnimation(eq(BURY_BONE_ANIMATION))
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = Bone::class)
|
||||
fun `Burying a bone should give the player experience`(bone: Bone) {
|
||||
player.inventory.add(bone.id)
|
||||
player.interactWithItem(bone.id, option = 1)
|
||||
|
||||
action.ticks(1)
|
||||
|
||||
after(action.complete(), "experience is granted after bone burial") {
|
||||
verify { player.sendMessage(startsWith("You bury the bones")) }
|
||||
|
||||
assertEquals(bone.xp, player.prayer.experience)
|
||||
assertEquals(player.inventory.getAmount(bone.id), 0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -90,10 +90,7 @@ class WoodcuttingAction(
|
||||
wait(tool.pulses)
|
||||
|
||||
// Check that the object exists in the world
|
||||
val obj = target.getObject(mob.world)
|
||||
if (obj == null) {
|
||||
stop()
|
||||
}
|
||||
val obj = target.getObject(mob.world) ?: stop()
|
||||
|
||||
if (mob.inventory.add(target.tree.id)) {
|
||||
val logName = Definitions.item(target.tree.id)!!.name.toLowerCase()
|
||||
@@ -105,7 +102,7 @@ class WoodcuttingAction(
|
||||
// respawn time: http://runescape.wikia.com/wiki/Trees
|
||||
val respawn = TimeUnit.SECONDS.toMillis(MINIMUM_RESPAWN_TIME + rand(150)) / GameConstants.PULSE_DELAY
|
||||
|
||||
mob.world.expireObject(obj!!, target.tree.stump, respawn.toInt())
|
||||
mob.world.expireObject(obj, target.tree.stump, respawn.toInt())
|
||||
stop()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import org.apollo.cache.def.ItemDefinition
|
||||
import org.apollo.game.model.entity.Player
|
||||
import org.apollo.game.model.entity.Skill
|
||||
import org.apollo.game.plugin.skills.woodcutting.Axe
|
||||
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.ItemDefinitions
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.EnumSource
|
||||
|
||||
@ExtendWith(ApolloTestingExtension::class)
|
||||
class AxeTests {
|
||||
|
||||
@ItemDefinitions
|
||||
fun axes() = Axe.values().map {
|
||||
ItemDefinition(it.id).apply { isStackable = false }
|
||||
}
|
||||
|
||||
@TestMock
|
||||
lateinit var player: Player
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Axe::class)
|
||||
fun `No axe is chosen if none are available`(axe: Axe) {
|
||||
player.skillSet.setCurrentLevel(Skill.WOODCUTTING, axe.level)
|
||||
|
||||
assertEquals(null, Axe.bestFor(player))
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Axe::class)
|
||||
fun `The highest level axe is chosen when available`(axe: Axe) {
|
||||
player.skillSet.setCurrentLevel(Skill.WOODCUTTING, axe.level)
|
||||
player.inventory.add(axe.id)
|
||||
|
||||
assertEquals(axe, Axe.bestFor(player))
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Axe::class)
|
||||
fun `Only axes the player has are chosen`(axe: Axe) {
|
||||
player.skillSet.setCurrentLevel(Skill.WOODCUTTING, axe.level)
|
||||
player.inventory.add(Axe.BRONZE.id)
|
||||
|
||||
assertEquals(Axe.BRONZE, Axe.bestFor(player))
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Axe::class)
|
||||
fun `Axes can be chosen from equipment as well as inventory`(axe: Axe) {
|
||||
player.skillSet.setCurrentLevel(Skill.WOODCUTTING, axe.level)
|
||||
player.inventory.add(axe.id)
|
||||
|
||||
assertEquals(axe, Axe.bestFor(player))
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(Axe::class)
|
||||
fun `Axes with a level requirement higher than the player's are ignored`(axe: Axe) {
|
||||
player.skillSet.setCurrentLevel(Skill.WOODCUTTING, axe.level)
|
||||
player.inventory.add(axe.id)
|
||||
|
||||
Axe.values()
|
||||
.filter { it.level > axe.level }
|
||||
.forEach { player.inventory.add(it.id) }
|
||||
|
||||
assertEquals(axe, Axe.bestFor(player))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import org.apollo.game.plugin.skills.woodcutting.Tree
|
||||
import org.junit.jupiter.api.extension.ExtensionContext
|
||||
import org.junit.jupiter.params.provider.Arguments
|
||||
import org.junit.jupiter.params.provider.ArgumentsProvider
|
||||
import java.util.stream.Stream
|
||||
|
||||
data class WoodcuttingTestData(val treeId: Int, val stumpId: Int, val tree: Tree)
|
||||
|
||||
fun woodcuttingTestData(): Collection<WoodcuttingTestData> = Tree.values()
|
||||
.flatMap { tree -> tree.objects.map { WoodcuttingTestData(it, tree.stump, tree) } }
|
||||
.toList()
|
||||
|
||||
class WoodcuttingTestDataProvider : ArgumentsProvider {
|
||||
override fun provideArguments(context: ExtensionContext?): Stream<out Arguments> {
|
||||
return woodcuttingTestData().map { Arguments { arrayOf(it) } }.stream()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.apollo.cache.def.ItemDefinition
|
||||
import org.apollo.game.model.entity.Player
|
||||
import org.apollo.game.model.entity.Skill
|
||||
import org.apollo.game.plugin.skills.woodcutting.Axe
|
||||
import org.apollo.game.plugin.testing.assertions.after
|
||||
import org.apollo.game.plugin.testing.assertions.contains
|
||||
import org.apollo.game.plugin.testing.assertions.verifyAfter
|
||||
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||
import org.apollo.game.plugin.testing.junit.api.ActionCapture
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.ItemDefinitions
|
||||
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||
import org.apollo.game.plugin.testing.junit.api.interactions.interactWithObject
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assumptions.assumeTrue
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource
|
||||
import java.util.*
|
||||
|
||||
@ExtendWith(ApolloTestingExtension::class)
|
||||
class WoodcuttingTests {
|
||||
|
||||
@ItemDefinitions
|
||||
fun logs() = woodcuttingTestData().map {
|
||||
ItemDefinition(it.tree.id).also { it.name = "<tree_type>" }
|
||||
}
|
||||
|
||||
@ItemDefinitions
|
||||
fun tools() = listOf(ItemDefinition(Axe.BRONZE.id))
|
||||
|
||||
@TestMock
|
||||
lateinit var action: ActionCapture
|
||||
|
||||
@TestMock
|
||||
lateinit var player: Player
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(WoodcuttingTestDataProvider::class)
|
||||
fun `Attempting to cut a tree when the player has no axe should send a message`(data: WoodcuttingTestData) {
|
||||
player.interactWithObject(data.treeId, 1)
|
||||
|
||||
verify { player.sendMessage(contains("do not have an axe")) }
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(WoodcuttingTestDataProvider::class)
|
||||
fun `Attempting to cut a tree when the player is too low levelled should send a message`(data: WoodcuttingTestData) {
|
||||
assumeTrue(data.tree.level > 1, "Normal trees are covered by axe requirements")
|
||||
|
||||
player.inventory.add(Axe.BRONZE.id)
|
||||
player.skillSet.setCurrentLevel(Skill.WOODCUTTING, data.tree.level - 1)
|
||||
|
||||
player.interactWithObject(data.treeId, 1)
|
||||
|
||||
verifyAfter(action.complete()) { player.sendMessage(contains("do not have the required level")) }
|
||||
}
|
||||
|
||||
@Disabled("Mocking constructors is not supported in mockk. Update WoodcuttingAction to pass a chance value")
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(WoodcuttingTestDataProvider::class)
|
||||
fun `Cutting a tree we have the skill to cut should eventually reward logs and experience`(
|
||||
data: WoodcuttingTestData
|
||||
) {
|
||||
// Mock RNG instances used by mining internally to determine success
|
||||
// @todo - improve this so we don't have to mock Random
|
||||
val rng = mockk<Random>()
|
||||
every { rng.nextInt(100) } answers { 0 }
|
||||
|
||||
player.inventory.add(Axe.BRONZE.id)
|
||||
player.skillSet.setCurrentLevel(Skill.WOODCUTTING, data.tree.level)
|
||||
|
||||
player.interactWithObject(data.treeId, 1)
|
||||
|
||||
verifyAfter(action.ticks(1)) { player.sendMessage(contains("You swing your axe")) }
|
||||
|
||||
after(action.ticks(Axe.BRONZE.pulses)) {
|
||||
// @todo - cummulative ticks() calls?
|
||||
verify { player.sendMessage("You manage to cut some <tree_type>") }
|
||||
assertEquals(data.tree.exp, player.skillSet.getExperience(Skill.WOODCUTTING))
|
||||
assertEquals(1, player.inventory.getAmount(data.tree.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
plugin {
|
||||
name = "entity_lookup"
|
||||
}
|
||||
Reference in New Issue
Block a user