mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-03 00:38:21 +00:00
Add tests for api plugin
This commit is contained in:
@@ -5,27 +5,73 @@ import org.apollo.cache.def.NpcDefinition
|
|||||||
import org.apollo.cache.def.ObjectDefinition
|
import org.apollo.cache.def.ObjectDefinition
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides plugins with access to item, npc, and object definitions
|
||||||
|
*/
|
||||||
object Definitions {
|
object Definitions {
|
||||||
fun item(id: Int): ItemDefinition? {
|
|
||||||
|
/**
|
||||||
|
* Returns the [ItemDefinition] with the specified [id]. Callers of this function must perform bounds checking on
|
||||||
|
* the [id] prior to invoking this method (i.e. verify that `id >= 0 && id < ItemDefinition.count()`).
|
||||||
|
*
|
||||||
|
* @throws IndexOutOfBoundsException If the id is out of bounds.
|
||||||
|
*/
|
||||||
|
fun item(id: Int): ItemDefinition {
|
||||||
return ItemDefinition.lookup(id)
|
return ItemDefinition.lookup(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the [ItemDefinition] with the specified name, performing case-insensitive matching. If multiple items
|
||||||
|
* share the same name, the item with the lowest id is returned.
|
||||||
|
*
|
||||||
|
* The name may be suffixed with an explicit item id (as a way to disambiguate in the above case), by ending the
|
||||||
|
* name with `_id`, e.g. `monks_robe_42`. If an explicit id is attached, it must be bounds checked (in the same
|
||||||
|
* manner as [item(id: Int)][item]).
|
||||||
|
*/
|
||||||
fun item(name: String): ItemDefinition? {
|
fun item(name: String): ItemDefinition? {
|
||||||
return findEntity(ItemDefinition::getDefinitions, ItemDefinition::getName, name)
|
return findEntity(ItemDefinition::getDefinitions, ItemDefinition::getName, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun obj(id: Int): ObjectDefinition? {
|
/**
|
||||||
|
* Returns the [ObjectDefinition] with the specified [id]. Callers of this function must perform bounds checking on
|
||||||
|
* the [id] prior to invoking this method (i.e. verify that `id >= 0 && id < ObjectDefinition.count()`).
|
||||||
|
*
|
||||||
|
* @throws IndexOutOfBoundsException If the id is out of bounds.
|
||||||
|
*/
|
||||||
|
fun obj(id: Int): ObjectDefinition {
|
||||||
return ObjectDefinition.lookup(id)
|
return ObjectDefinition.lookup(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the [ObjectDefinition] with the specified name, performing case-insensitive matching. If multiple objects
|
||||||
|
* share the same name, the object with the lowest id is returned.
|
||||||
|
*
|
||||||
|
* The name may be suffixed with an explicit object id (as a way to disambiguate in the above case), by ending the
|
||||||
|
* name with `_id`, e.g. `man_2`. If an explicit id is attached, it must be bounds checked (in the same
|
||||||
|
* manner as [object(id: Int)][object]).
|
||||||
|
*/
|
||||||
fun obj(name: String): ObjectDefinition? {
|
fun obj(name: String): ObjectDefinition? {
|
||||||
return findEntity(ObjectDefinition::getDefinitions, ObjectDefinition::getName, name)
|
return findEntity(ObjectDefinition::getDefinitions, ObjectDefinition::getName, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun npc(id: Int): NpcDefinition? {
|
/**
|
||||||
|
* Returns the [NpcDefinition] with the specified [id]. Callers of this function must perform bounds checking on
|
||||||
|
* the [id] prior to invoking this method (i.e. verify that `id >= 0 && id < NpcDefinition.count()`).
|
||||||
|
*
|
||||||
|
* @throws IndexOutOfBoundsException If the id is out of bounds.
|
||||||
|
*/
|
||||||
|
fun npc(id: Int): NpcDefinition {
|
||||||
return NpcDefinition.lookup(id)
|
return NpcDefinition.lookup(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the [NpcDefinition] with the specified name, performing case-insensitive matching. If multiple npcs
|
||||||
|
* share the same name, the npc with the lowest id is returned.
|
||||||
|
*
|
||||||
|
* The name may be suffixed with an explicit npc id (as a way to disambiguate in the above case), by ending the
|
||||||
|
* name with `_id`, e.g. `man_2`. If an explicit id is attached, it must be bounds checked (in the same
|
||||||
|
* manner as [npc(id: Int)][npc]).
|
||||||
|
*/
|
||||||
fun npc(name: String): NpcDefinition? {
|
fun npc(name: String): NpcDefinition? {
|
||||||
return findEntity(NpcDefinition::getDefinitions, NpcDefinition::getName, name)
|
return findEntity(NpcDefinition::getDefinitions, NpcDefinition::getName, name)
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-5
@@ -53,26 +53,37 @@ class SkillProxy(private val skills: SkillSet, private val skill: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boosts the current level of this skill by [amount], if possible (i.e. if `current + amount <= maximum + amount`).
|
* Boosts the current level of this skill by [amount], if possible.
|
||||||
*/
|
*/
|
||||||
fun boost(amount: Int) {
|
fun boost(amount: Int) {
|
||||||
val new = Math.min(current + amount, maximum + amount)
|
require(amount >= 1) { "Can only boost skills by positive values." }
|
||||||
|
|
||||||
|
val new = if (current - maximum > amount) {
|
||||||
|
current
|
||||||
|
} else {
|
||||||
|
Math.min(current + amount, maximum + amount)
|
||||||
|
}
|
||||||
|
|
||||||
skills.setCurrentLevel(skill, new)
|
skills.setCurrentLevel(skill, new)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Drains the current level of this skill by [amount], if possible (i.e. if `current - amount >= 0`).
|
* Drains the current level of this skill by [amount], if possible.
|
||||||
*/
|
*/
|
||||||
fun drain(amount: Int) {
|
fun drain(amount: Int) {
|
||||||
|
require(amount >= 1) { "Can only drain skills by positive values." }
|
||||||
|
|
||||||
val new = Math.max(current - amount, 0)
|
val new = Math.max(current - amount, 0)
|
||||||
skills.setCurrentLevel(skill, new)
|
skills.setCurrentLevel(skill, new)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restores the current level of this skill by [amount], if possible (i.e. if `current + amount < maximum`).
|
* Restores the current level of this skill by [amount], if possible.
|
||||||
*/
|
*/
|
||||||
fun restore(amount: Int) {
|
fun restore(amount: Int) {
|
||||||
val new = Math.max(current + amount, maximum)
|
require(amount >= 1) { "Can only restore skills by positive values." }
|
||||||
|
|
||||||
|
val new = Math.min(current + amount, maximum)
|
||||||
skills.setCurrentLevel(skill, new)
|
skills.setCurrentLevel(skill, new)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package org.apollo.game.plugin.api
|
||||||
|
|
||||||
|
import org.apollo.game.model.Position
|
||||||
|
import org.apollo.game.model.World
|
||||||
|
import org.apollo.game.model.area.Region
|
||||||
|
import org.apollo.game.model.entity.Entity
|
||||||
|
import org.apollo.game.model.entity.EntityType
|
||||||
|
import org.apollo.game.model.entity.EntityType.DYNAMIC_OBJECT
|
||||||
|
import org.apollo.game.model.entity.EntityType.STATIC_OBJECT
|
||||||
|
import org.apollo.game.model.entity.obj.DynamicGameObject
|
||||||
|
import org.apollo.game.model.entity.obj.GameObject
|
||||||
|
import org.apollo.game.scheduling.ScheduledTask
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all of the [Entities][Entity] with the specified [EntityTypes][EntityType] at the specified [position], that
|
||||||
|
* match the provided [predicate].
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* const val GOLD_COINS = 995
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* val allCoins: Sequence<GroundItem> = region.find(position, EntityType.GROUND_ITEM) { item -> item.id == GOLD_COINS }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
fun <T : Entity> Region.find(position: Position, vararg types: EntityType, predicate: (T) -> Boolean): Sequence<T> {
|
||||||
|
return getEntities<T>(position, *types).asSequence().filter(predicate)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first [GameObject]s with the specified [id] at the specified [position].
|
||||||
|
*
|
||||||
|
* Note that the iteration order of entities in a [Region] is not defined - this function should not be used if there
|
||||||
|
* may be more than [GameObject] with the specified [id] (see [Region.findObjects]).
|
||||||
|
*/
|
||||||
|
fun Region.findObject(position: Position, id: Int): GameObject? {
|
||||||
|
return find<GameObject>(position, DYNAMIC_OBJECT, STATIC_OBJECT) { it.id == id }
|
||||||
|
.firstOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds **all** [GameObject]s with the specified [id] at the specified [position].
|
||||||
|
*/
|
||||||
|
fun Region.findObjects(position: Position, id: Int): Sequence<GameObject> {
|
||||||
|
return find(position, DYNAMIC_OBJECT, STATIC_OBJECT) { it.id == id }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first [GameObject]s with the specified [id] at the specified [position].
|
||||||
|
*
|
||||||
|
* Note that the iteration order of entities in a [Region] is not defined - this function should not be used if there
|
||||||
|
* may be more than [GameObject] with the specified [id] (see [World.findObjects]).
|
||||||
|
*/
|
||||||
|
fun World.findObject(position: Position, id: Int): GameObject? {
|
||||||
|
return regionRepository.fromPosition(position).findObject(position, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds **all** [GameObject]s with the specified [id] at the specified [position].
|
||||||
|
*/
|
||||||
|
fun World.findObjects(position: Position, id: Int): Sequence<GameObject> {
|
||||||
|
return regionRepository.fromPosition(position).findObjects(position, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified [GameObject] from the world, replacing it with [replacement] object for [delay] **pulses**.
|
||||||
|
*/
|
||||||
|
fun World.replaceObject(obj: GameObject, replacement: Int, delay: Int) {
|
||||||
|
val replacementObj = DynamicGameObject.createPublic(this, replacement, obj.position, obj.type, obj.orientation)
|
||||||
|
|
||||||
|
schedule(ExpireObjectTask(this, obj, replacementObj, delay))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ScheduledTask] that temporarily replaces the [existing] [GameObject] with the [replacement] [GameObject] for the
|
||||||
|
* specified [duration].
|
||||||
|
*
|
||||||
|
* @param existing The [GameObject] that already exists and should be replaced.
|
||||||
|
* @param replacement The [GameObject] to replace the [existing] object with.
|
||||||
|
* @param duration The time, in **pulses**, for the [replacement] object to exist in the game world.
|
||||||
|
*/
|
||||||
|
private class ExpireObjectTask(
|
||||||
|
private val world: World,
|
||||||
|
private val existing: GameObject,
|
||||||
|
private val replacement: GameObject,
|
||||||
|
private val duration: Int
|
||||||
|
) : ScheduledTask(0, true) {
|
||||||
|
|
||||||
|
private var respawning: Boolean = false
|
||||||
|
|
||||||
|
override fun execute() {
|
||||||
|
val region = world.regionRepository.fromPosition(existing.position)
|
||||||
|
|
||||||
|
if (!respawning) {
|
||||||
|
world.spawn(replacement)
|
||||||
|
respawning = true
|
||||||
|
setDelay(duration)
|
||||||
|
} else {
|
||||||
|
region.removeEntity(replacement)
|
||||||
|
world.spawn(existing)
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
package org.apollo.game.plugin.api
|
|
||||||
|
|
||||||
import org.apollo.game.model.Position
|
|
||||||
import org.apollo.game.model.World
|
|
||||||
import org.apollo.game.model.area.Region
|
|
||||||
import org.apollo.game.model.entity.Entity
|
|
||||||
import org.apollo.game.model.entity.EntityType
|
|
||||||
import org.apollo.game.model.entity.EntityType.DYNAMIC_OBJECT
|
|
||||||
import org.apollo.game.model.entity.EntityType.STATIC_OBJECT
|
|
||||||
import org.apollo.game.model.entity.obj.DynamicGameObject
|
|
||||||
import org.apollo.game.model.entity.obj.GameObject
|
|
||||||
import org.apollo.game.scheduling.ScheduledTask
|
|
||||||
|
|
||||||
fun <T : Entity> Region.find(position: Position, predicate: (T) -> Boolean, vararg types: EntityType): Sequence<T> {
|
|
||||||
return getEntities<T>(position, *types).asSequence().filter(predicate)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Region.findObjects(position: Position, id: Int): Sequence<GameObject> {
|
|
||||||
return find(position, { it.id == id }, DYNAMIC_OBJECT, STATIC_OBJECT)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Region.findObject(position: Position, id: Int): GameObject? {
|
|
||||||
return find<GameObject>(position, { it.id == id }, DYNAMIC_OBJECT, STATIC_OBJECT).firstOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun World.findObject(position: Position, objectId: Int): GameObject? {
|
|
||||||
return regionRepository.fromPosition(position).findObject(position, objectId)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun World.findObjects(position: Position, id: Int): Sequence<GameObject> {
|
|
||||||
return regionRepository.fromPosition(position).findObjects(position, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun World.expireObject(obj: GameObject, replacement: Int, respawnDelay: Int) {
|
|
||||||
val replacementObj = DynamicGameObject.createPublic(this, replacement, obj.position, obj.type, obj.orientation)
|
|
||||||
|
|
||||||
schedule(ExpireObjectTask(this, obj, replacementObj, respawnDelay))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ExpireObjectTask(
|
|
||||||
private val world: World,
|
|
||||||
private val existing: GameObject,
|
|
||||||
private val replacement: GameObject,
|
|
||||||
private val respawnDelay: Int
|
|
||||||
) : ScheduledTask(0, true) {
|
|
||||||
|
|
||||||
private var respawning: Boolean = false
|
|
||||||
|
|
||||||
override fun execute() {
|
|
||||||
val region = world.regionRepository.fromPosition(existing.position)
|
|
||||||
|
|
||||||
if (!respawning) {
|
|
||||||
world.spawn(replacement)
|
|
||||||
respawning = true
|
|
||||||
setDelay(respawnDelay)
|
|
||||||
} else {
|
|
||||||
region.removeEntity(replacement)
|
|
||||||
world.spawn(existing)
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package org.apollo.game.plugin.api
|
||||||
|
|
||||||
|
import org.apollo.cache.def.ItemDefinition
|
||||||
|
import org.apollo.cache.def.NpcDefinition
|
||||||
|
import org.apollo.cache.def.ObjectDefinition
|
||||||
|
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.NpcDefinitions
|
||||||
|
import org.apollo.game.plugin.testing.junit.api.annotations.ObjectDefinitions
|
||||||
|
import org.apollo.game.plugin.testing.junit.params.ItemDefinitionSource
|
||||||
|
import org.apollo.game.plugin.testing.junit.params.NpcDefinitionSource
|
||||||
|
import org.apollo.game.plugin.testing.junit.params.ObjectDefinitionSource
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
|
|
||||||
|
@ExtendWith(ApolloTestingExtension::class)
|
||||||
|
class DefinitionsTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can find an ItemDefinition directly using its id`() {
|
||||||
|
val searched = Definitions.item(0)
|
||||||
|
assertEquals(items.first().id, searched.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can find an ItemDefinition using its name`() {
|
||||||
|
val searched = Definitions.item("item_two")
|
||||||
|
assertEquals(items[2].id, searched?.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ItemDefinitionSource
|
||||||
|
fun `can find ItemDefinitions directly using id suffixing`(item: ItemDefinition) {
|
||||||
|
val searched = Definitions.item("${item.name}_${item.id}")
|
||||||
|
assertEquals(item.id, searched?.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can find an NpcDefinition directly using its id`() {
|
||||||
|
val searched = Definitions.npc(0)
|
||||||
|
assertEquals(npcs.first().id, searched.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can find an NpcDefinition using its name`() {
|
||||||
|
val searched = Definitions.npc("npc_two")
|
||||||
|
assertEquals(items[2].id, searched?.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@NpcDefinitionSource
|
||||||
|
fun `can find NpcDefinitions directly using id suffixing`(npc: NpcDefinition) {
|
||||||
|
val searched = Definitions.npc("${npc.name}_${npc.id}")
|
||||||
|
assertEquals(npc.id, searched?.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can find an ObjectDefinition directly using its id`() {
|
||||||
|
val searched = Definitions.obj(0)
|
||||||
|
assertEquals(objs.first().id, searched.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can find an ObjectDefinition using its name`() {
|
||||||
|
val searched = Definitions.obj("obj_two")
|
||||||
|
assertEquals(items[2].id, searched?.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ObjectDefinitionSource
|
||||||
|
fun `can find ObjectDefinitions directly using id suffixing`(obj: ObjectDefinition) {
|
||||||
|
val searched = Definitions.obj("${obj.name}_${obj.id}")
|
||||||
|
assertEquals(obj.id, searched?.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
|
||||||
|
@ItemDefinitions
|
||||||
|
val items = listOf("item zero", "item one", "item two", "item duplicate name", "item duplicate name")
|
||||||
|
.mapIndexed { id, name -> ItemDefinition(id).also { it.name = name } }
|
||||||
|
|
||||||
|
@NpcDefinitions
|
||||||
|
val npcs = listOf("npc zero", "npc one", "npc two", "npc duplicate name", "npc duplicate name")
|
||||||
|
.mapIndexed { id, name -> NpcDefinition(id).also { it.name = name } }
|
||||||
|
|
||||||
|
@ObjectDefinitions
|
||||||
|
val objs = listOf("obj zero", "obj one", "obj two", "obj duplicate name", "obj duplicate name")
|
||||||
|
.mapIndexed { id, name -> ObjectDefinition(id).also { it.name = name } }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package org.apollo.game.plugin.api
|
||||||
|
|
||||||
|
import org.apollo.game.model.entity.Player
|
||||||
|
import org.apollo.game.model.entity.Skill
|
||||||
|
import org.apollo.game.plugin.testing.junit.ApolloTestingExtension
|
||||||
|
import org.apollo.game.plugin.testing.junit.api.annotations.TestMock
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
|
|
||||||
|
@ExtendWith(ApolloTestingExtension::class)
|
||||||
|
class PlayerTests {
|
||||||
|
|
||||||
|
@TestMock
|
||||||
|
lateinit var player: Player
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setHitpointsLevel() {
|
||||||
|
player.skillSet.setSkill(Skill.HITPOINTS, Skill(1_154.0, 10, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can boost skill above maximum level`() {
|
||||||
|
player.apply {
|
||||||
|
hitpoints.boost(5)
|
||||||
|
|
||||||
|
assertEquals(15, hitpoints.current)
|
||||||
|
assertEquals(10, hitpoints.maximum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `boosts to the same skill do not accumulate`() {
|
||||||
|
player.apply {
|
||||||
|
hitpoints.boost(5)
|
||||||
|
hitpoints.boost(4)
|
||||||
|
|
||||||
|
assertEquals(15, hitpoints.current)
|
||||||
|
assertEquals(10, hitpoints.maximum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `greater boosts can override earlier boosts`() {
|
||||||
|
player.apply {
|
||||||
|
hitpoints.boost(5)
|
||||||
|
hitpoints.boost(7)
|
||||||
|
|
||||||
|
assertEquals(17, hitpoints.current)
|
||||||
|
assertEquals(10, hitpoints.maximum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can drain skills`() {
|
||||||
|
player.apply {
|
||||||
|
hitpoints.drain(5)
|
||||||
|
|
||||||
|
assertEquals(5, hitpoints.current)
|
||||||
|
assertEquals(10, hitpoints.maximum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `repeated drains on the same skill accumulate`() {
|
||||||
|
player.apply {
|
||||||
|
hitpoints.drain(4)
|
||||||
|
hitpoints.drain(5)
|
||||||
|
|
||||||
|
assertEquals(1, hitpoints.current)
|
||||||
|
assertEquals(10, hitpoints.maximum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `cannot drain skills below zero`() {
|
||||||
|
player.apply {
|
||||||
|
hitpoints.drain(99)
|
||||||
|
|
||||||
|
assertEquals(0, hitpoints.current)
|
||||||
|
assertEquals(10, hitpoints.maximum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `can restore previously-drained skills`() {
|
||||||
|
player.skillSet.setCurrentLevel(Skill.HITPOINTS, 1)
|
||||||
|
|
||||||
|
player.apply {
|
||||||
|
hitpoints.restore(5)
|
||||||
|
|
||||||
|
assertEquals(6, hitpoints.current)
|
||||||
|
assertEquals(10, hitpoints.maximum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `repeated restores on the same skill accumulate`() {
|
||||||
|
player.skillSet.setCurrentLevel(Skill.HITPOINTS, 1)
|
||||||
|
|
||||||
|
player.apply {
|
||||||
|
hitpoints.restore(3)
|
||||||
|
hitpoints.restore(4)
|
||||||
|
|
||||||
|
assertEquals(8, hitpoints.current)
|
||||||
|
assertEquals(10, hitpoints.maximum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `cannot restore skills above their maximum level`() {
|
||||||
|
player.skillSet.setCurrentLevel(Skill.HITPOINTS, 1)
|
||||||
|
|
||||||
|
player.apply {
|
||||||
|
hitpoints.restore(99)
|
||||||
|
|
||||||
|
assertEquals(10, hitpoints.current)
|
||||||
|
assertEquals(10, hitpoints.maximum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package org.apollo.game.plugin.api
|
||||||
|
|
||||||
|
import org.apollo.game.model.Position
|
||||||
|
import org.apollo.game.plugin.api.Position.component1
|
||||||
|
import org.apollo.game.plugin.api.Position.component2
|
||||||
|
import org.apollo.game.plugin.api.Position.component3
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class PositionTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Positions are destructured in the correct order`() {
|
||||||
|
val x = 10
|
||||||
|
val y = 20
|
||||||
|
val z = 1
|
||||||
|
|
||||||
|
val position = Position(x, y, z)
|
||||||
|
val (x2, y2, z2) = position
|
||||||
|
|
||||||
|
assertEquals(x, x2) { "x coordinate mismatch in Position destructuring." }
|
||||||
|
assertEquals(y, y2) { "y coordinate mismatch in Position destructuring." }
|
||||||
|
assertEquals(z, z2) { "z coordinate mismatch in Position destructuring." }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -28,9 +28,9 @@ enum class Fish(val id: Int, val level: Int, val experience: Double, catchSuffix
|
|||||||
/**
|
/**
|
||||||
* The name of this fish, formatted so it can be inserted into a message.
|
* The name of this fish, formatted so it can be inserted into a message.
|
||||||
*/
|
*/
|
||||||
val catchMessage = "You catch ${catchSuffix ?: "a ${catchName()}."}"
|
val catchMessage by lazy { "You catch ${catchSuffix ?: "a ${catchName()}."}" }
|
||||||
|
|
||||||
private fun catchName() = Definitions.item(id)!!.name.toLowerCase().removePrefix("raw ")
|
private fun catchName() = Definitions.item(id).name.toLowerCase().removePrefix("raw ")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ enum class FishingTool(
|
|||||||
/**
|
/**
|
||||||
* The name of this tool, formatted so it can be inserted into a message.
|
* The name of this tool, formatted so it can be inserted into a message.
|
||||||
*/
|
*/
|
||||||
val formattedName = Definitions.item(id)!!.name.toLowerCase()
|
val formattedName by lazy { Definitions.item(id).name.toLowerCase() }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ data class MiningTarget(val objectId: Int, val position: Position, val ore: Ore)
|
|||||||
fun deplete(world: World) {
|
fun deplete(world: World) {
|
||||||
val obj = world.findObject(position, objectId)!!
|
val obj = world.findObject(position, objectId)!!
|
||||||
|
|
||||||
world.expireObject(obj, ore.objects[objectId]!!, ore.respawn)
|
world.replaceObject(obj, ore.objects[objectId]!!, ore.respawn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,7 +113,7 @@ data class MiningTarget(val objectId: Int, val position: Position, val ore: Ore)
|
|||||||
/**
|
/**
|
||||||
* Get the normalized name of the [Ore] represented by this target.
|
* Get the normalized name of the [Ore] represented by this target.
|
||||||
*/
|
*/
|
||||||
fun oreName() = Definitions.item(ore.id)!!.name.toLowerCase()
|
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.
|
* Reward a [player] with experience and ore if they have the inventory capacity to take a new ore.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import org.apollo.cache.def.ItemDefinition
|
|||||||
import org.apollo.game.model.World
|
import org.apollo.game.model.World
|
||||||
import org.apollo.game.model.entity.Player
|
import org.apollo.game.model.entity.Player
|
||||||
import org.apollo.game.model.entity.Skill
|
import org.apollo.game.model.entity.Skill
|
||||||
import org.apollo.game.plugin.api.expireObject
|
import org.apollo.game.plugin.api.replaceObject
|
||||||
import org.apollo.game.plugin.skills.mining.Ore
|
import org.apollo.game.plugin.skills.mining.Ore
|
||||||
import org.apollo.game.plugin.skills.mining.Pickaxe
|
import org.apollo.game.plugin.skills.mining.Pickaxe
|
||||||
import org.apollo.game.plugin.skills.mining.TIN_OBJECTS
|
import org.apollo.game.plugin.skills.mining.TIN_OBJECTS
|
||||||
@@ -59,7 +59,7 @@ class MiningActionTests {
|
|||||||
|
|
||||||
every { target.skillRequirementsMet(player) } returns true
|
every { target.skillRequirementsMet(player) } returns true
|
||||||
every { target.isSuccessful(player, any()) } returns true
|
every { target.isSuccessful(player, any()) } returns true
|
||||||
every { world.expireObject(obj, any(), any()) } answers { }
|
every { world.replaceObject(obj, any(), any()) } answers { }
|
||||||
|
|
||||||
player.skillSet.setCurrentLevel(Skill.MINING, Ore.TIN.level)
|
player.skillSet.setCurrentLevel(Skill.MINING, Ore.TIN.level)
|
||||||
player.startAction(MiningAction(player, Pickaxe.BRONZE, target))
|
player.startAction(MiningAction(player, Pickaxe.BRONZE, target))
|
||||||
@@ -70,7 +70,7 @@ class MiningActionTests {
|
|||||||
|
|
||||||
after(action.complete()) {
|
after(action.complete()) {
|
||||||
verify { player.sendMessage("You manage to mine some <ore_type>") }
|
verify { player.sendMessage("You manage to mine some <ore_type>") }
|
||||||
verify { world.expireObject(obj, expiredTinId, Ore.TIN.respawn) }
|
verify { world.replaceObject(obj, expiredTinId, Ore.TIN.respawn) }
|
||||||
|
|
||||||
assertTrue(player.inventory.contains(Ore.TIN.id))
|
assertTrue(player.inventory.contains(Ore.TIN.id))
|
||||||
assertEquals(player.skillSet.getExperience(Skill.MINING), Ore.TIN.exp)
|
assertEquals(player.skillSet.getExperience(Skill.MINING), Ore.TIN.exp)
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class WoodcuttingAction(
|
|||||||
val obj = target.getObject(mob.world) ?: stop()
|
val obj = target.getObject(mob.world) ?: stop()
|
||||||
|
|
||||||
if (mob.inventory.add(target.tree.id)) {
|
if (mob.inventory.add(target.tree.id)) {
|
||||||
val logName = Definitions.item(target.tree.id)!!.name.toLowerCase()
|
val logName = Definitions.item(target.tree.id).name.toLowerCase()
|
||||||
mob.sendMessage("You managed to cut some $logName.")
|
mob.sendMessage("You managed to cut some $logName.")
|
||||||
mob.woodcutting.experience += target.tree.exp
|
mob.woodcutting.experience += target.tree.exp
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ class WoodcuttingAction(
|
|||||||
// respawn time: http://runescape.wikia.com/wiki/Trees
|
// respawn time: http://runescape.wikia.com/wiki/Trees
|
||||||
val respawn = TimeUnit.SECONDS.toMillis(MINIMUM_RESPAWN_TIME + rand(150)) / GameConstants.PULSE_DELAY
|
val respawn = TimeUnit.SECONDS.toMillis(MINIMUM_RESPAWN_TIME + rand(150)) / GameConstants.PULSE_DELAY
|
||||||
|
|
||||||
mob.world.expireObject(obj, target.tree.stump, respawn.toInt())
|
mob.world.replaceObject(obj, target.tree.stump, respawn.toInt())
|
||||||
stop()
|
stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user