diff --git a/data/skill/runecrafting/runecrafting.items.toml b/data/skill/runecrafting/runecrafting.items.toml index ac1bf2df3c..4afa6f95d3 100644 --- a/data/skill/runecrafting/runecrafting.items.toml +++ b/data/skill/runecrafting/runecrafting.items.toml @@ -182,6 +182,7 @@ limit = 100 weight = 1.0 slot = "Hat" type = "HairMid" +talisman_tiara = { xp = 25.0, level = 1 } examine = "A tiara infused with the properties of air." kept = "Wilderness" @@ -193,6 +194,7 @@ id = 5529 price = 14 limit = 100 weight = 1.0 +talisman_tiara = { xp = 27.5, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of the mind." @@ -206,6 +208,7 @@ id = 5531 price = 2481 limit = 100 weight = 1.0 +talisman_tiara = { xp = 30.0, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of water." @@ -219,6 +222,7 @@ id = 5533 price = 3 limit = 100 weight = 1.0 +talisman_tiara = { xp = 37.5, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of the body." @@ -232,6 +236,7 @@ id = 5535 price = 4 limit = 100 weight = 1.0 +talisman_tiara = { xp = 32.5, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of the earth." @@ -245,6 +250,7 @@ id = 5537 price = 10 limit = 100 weight = 1.0 +talisman_tiara = { xp = 35.0, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of fire." @@ -258,6 +264,7 @@ id = 5539 price = 4 limit = 100 weight = 1.0 +talisman_tiara = { xp = 40.0, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of the cosmos." @@ -270,6 +277,7 @@ id = 5541 price = 8 limit = 100 weight = 1.0 +talisman_tiara = { xp = 45.0, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of nature." @@ -282,6 +290,7 @@ id = 5543 price = 5 limit = 100 weight = 1.0 +talisman_tiara = { xp = 42.5, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of chaos." @@ -294,6 +303,7 @@ id = 5545 price = 3 limit = 100 weight = 1.0 +talisman_tiara = { xp = 47.5, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of law." @@ -306,6 +316,7 @@ id = 5547 price = 213 limit = 100 weight = 1.0 +talisman_tiara = { xp = 50.0, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of death." @@ -318,6 +329,7 @@ id = 5549 price = 11900 limit = 100 weight = 1.0 +talisman_tiara = { xp = 52.5, level = 1 } slot = "Hat" type = "HairMid" examine = "A tiara infused with the properties of blood." diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ItemDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ItemDefinitions.kt index 69aae8cea5..b6d533f500 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ItemDefinitions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/ItemDefinitions.kt @@ -82,6 +82,7 @@ object ItemDefinitions : DefinitionsDecoder { "jewellery" -> extras[key] = Jewellery(this) "silver_jewellery" -> extras[key] = Silver(this) "runecrafting" -> extras[key] = Rune(this) + "talisman_tiara" -> extras[key] = Tiara(this) "cleaning" -> extras[key] = Cleaning(this) "fletch_dart" -> extras[key] = FletchDarts(this) "fletch_bolts" -> extras[key] = FletchBolts(this) diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/data/Tiara.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/data/Tiara.kt new file mode 100644 index 0000000000..c19f15d5f4 --- /dev/null +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/data/Tiara.kt @@ -0,0 +1,42 @@ +package world.gregs.voidps.engine.data.definition.data + +import world.gregs.config.ConfigReader + +data class Tiara( + val xp: Double = 0.0, + val level: Int = 1, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Tiara + + if (xp != other.xp) return false + return level == other.level + } + + override fun hashCode(): Int { + var result = xp.hashCode() + result = 31 * result + level + + return result + } + + companion object { + operator fun invoke(reader: ConfigReader): Tiara { + var xp = 0.0 + var level = 1 + + while (reader.nextEntry()) { + when (val key = reader.key()) { + "xp" -> xp = reader.double() + "level" -> level = reader.int() + else -> throw IllegalArgumentException("Unexpected key: '$key' ${reader.exception()}") + } + } + + return Tiara(xp = xp, level = level) + } + } +} \ No newline at end of file diff --git a/game/src/main/kotlin/content/skill/runecrafting/Tiaras.kt b/game/src/main/kotlin/content/skill/runecrafting/Tiaras.kt new file mode 100644 index 0000000000..2f4a22181e --- /dev/null +++ b/game/src/main/kotlin/content/skill/runecrafting/Tiaras.kt @@ -0,0 +1,55 @@ +package content.skill.runecrafting + +import com.github.michaelbull.logging.InlineLogger +import world.gregs.voidps.cache.definition.data.ItemDefinition +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.client.variable.start +import world.gregs.voidps.engine.data.definition.ItemDefinitions +import world.gregs.voidps.engine.data.definition.data.Tiara +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.character.player.skill.exp.exp +import world.gregs.voidps.engine.entity.character.sound +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.transact.TransactionError +import world.gregs.voidps.engine.inv.transact.operation.AddItem.add +import world.gregs.voidps.engine.inv.transact.operation.RemoveItem.remove + +class Tiaras : Script { + + val logger = InlineLogger() + + init { + itemOnObjectOperate("tiara", "*_altar") { (target) -> + val id = target.id.replace("_altar", "_tiara") + bindTiara(this, id, ItemDefinitions.get(id)) + } + } + + fun Tiaras.bindTiara(player: Player, id: String, itemDefinition: ItemDefinition) { + val tiara: Tiara = itemDefinition.getOrNull("talisman_tiara") ?: return + player.softTimers.start("runecrafting") + val tiaraId = "tiara" + val talismanId = id.replace("_tiara", "_talisman") + player.inventory.transaction { + remove(tiaraId, 1) + remove(talismanId, 1) + add(id, 1) + } + player.start("movement_delay", 3) + when (player.inventory.transaction.error) { + is TransactionError.Deficient, is TransactionError.Invalid -> { + player.message("You don't have a talisman to bind.") + } + TransactionError.None -> { + player.exp(Skill.Runecrafting, tiara.xp) + player.anim("bind_runes") + player.gfx("bind_runes") + player.sound("bind_runes") + } + else -> logger.warn { "Error binding talisman with tiara $player $tiara ${player.levels.get(Skill.Runecrafting)} $talismanId" } + } + player.softTimers.stop("runecrafting") + } +} diff --git a/game/src/test/kotlin/content/skill/runecrafting/TiaraTest.kt b/game/src/test/kotlin/content/skill/runecrafting/TiaraTest.kt new file mode 100644 index 0000000000..55e86493a4 --- /dev/null +++ b/game/src/test/kotlin/content/skill/runecrafting/TiaraTest.kt @@ -0,0 +1,64 @@ +package content.skill.runecrafting + +import WorldTest +import content.entity.obj.ObjectTeleports +import itemOnObject +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DynamicTest.dynamicTest +import org.junit.jupiter.api.TestFactory +import org.koin.test.get +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.obj.GameObjects +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.type.Tile + +class TiaraTest : WorldTest() { + + private lateinit var teleports: ObjectTeleports + + @BeforeEach + fun setup() { + teleports = get() + } + + @TestFactory + fun `Bind tiara with a talisman at an altar`() = altars.map { (type, _, altarTile) -> + dynamicTest("Craft $type tiara") { + val tile = teleports.get("${type}_altar_ruins_enter", "Enter").first().to + val player = createPlayer(tile) + player.inventory.add("tiara", "${type}_talisman") + + val altar = GameObjects.find(altarTile, "${type}_altar") + player.itemOnObject(altar, 0) + tick(1) + tickIf { player.visuals.moved } + + assertFalse(player.inventory.contains("tiara")) + assertFalse(player.inventory.contains("${type}_talisman")) + assertTrue(player.inventory.contains("${type}_tiara")) + assertTrue(player.experience.get(Skill.Runecrafting) > 0) + } + } + + companion object { + internal data class Altar(val type: String, val ruinsTile: Tile, val altarTile: Tile) + + internal val altars = listOf( + Altar("air", Tile(3126, 3404), Tile(2843, 4833)), + Altar("water", Tile(3184, 3164), Tile(3483, 4835)), + Altar("earth", Tile(3305, 3473), Tile(2657, 4840)), + Altar("fire", Tile(3312, 3254), Tile(2584, 4837)), + Altar("mind", Tile(2981, 3513), Tile(2785, 4840)), + Altar("body", Tile(3052, 3444), Tile(2522, 4839)), + Altar("cosmic", Tile(2407, 4376), Tile(2141, 4832)), + Altar("law", Tile(2857, 3380), Tile(2463, 4831)), + Altar("nature", Tile(2868, 3018), Tile(2399, 4840)), + Altar("chaos", Tile(3059, 3590), Tile(2270, 4841)), + Altar("death", Tile(1860, 4638), Tile(2204, 4835)), + Altar("blood", Tile(3560, 9780), Tile(2461, 4894, 1)), + ) + } +}