diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/EnumDefinition.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/EnumDefinition.kt index c2aa55a2e3..d3d2f6af55 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/EnumDefinition.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/EnumDefinition.kt @@ -2,6 +2,7 @@ package world.gregs.voidps.cache.definition.data import world.gregs.voidps.cache.Definition import world.gregs.voidps.cache.definition.Extra +import world.gregs.voidps.type.random data class EnumDefinition( override var id: Int = -1, @@ -17,11 +18,15 @@ data class EnumDefinition( Extra { fun getKey(value: Any) = map?.filterValues { it == value }?.keys?.lastOrNull() ?: -1 - fun getInt(id: Int) = map?.get(id) as? Int ?: defaultInt + fun int(id: Int) = map?.get(id) as? Int ?: defaultInt - fun randomInt() = map?.values?.random() as? Int ?: defaultInt + fun randomInt() = map?.values?.random(random) as? Int ?: defaultInt - fun getString(id: Int) = map?.get(id) as? String ?: defaultString + fun string(id: Int) = map?.get(id) as? String ?: defaultString + + override fun toString(): String { + return "EnumDefinition(id=$id, keyType=${EnumTypes.name(keyType)}, valueType=${EnumTypes.name(valueType)}, defaultString=$defaultString, defaultInt=$defaultInt, length=$length, map=$map, stringId=$stringId, extras=$extras)" + } companion object { val EMPTY = EnumDefinition() diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/EnumTypes.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/EnumTypes.kt new file mode 100644 index 0000000000..a15d94bbf6 --- /dev/null +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/EnumTypes.kt @@ -0,0 +1,65 @@ +package world.gregs.voidps.cache.definition.data + +object EnumTypes { + const val STRING = 's' + const val INT = 'i' + const val STRUCT = 'J' + const val JINGLE = 'j' + const val ITEM = 'o' + const val ITEM_2 = 'O' + const val SPRITE = 'd' + const val MODEL = 'm' + const val ID_KIT = 'K' + const val COMPONENT = 'I' + const val MAP_AREA = '`' + const val SKILL = 'S' + const val TILE = 'C' + const val CHAT_TYPE = 'c' + const val ANIM = 'A' + const val NPC = 'n' + const val ENUM = 'g' + const val INV = 'v' + + fun name(char: Char) = when (char) { + STRING -> "string" + INT -> "int" + STRUCT -> "struct" + JINGLE -> "jingle" + ITEM -> "item" + ITEM_2 -> "item" + SPRITE -> "sprite" + MODEL -> "model" + ID_KIT -> "idkit" + COMPONENT -> "interface" + MAP_AREA -> "map_area" + SKILL -> "skill" + TILE -> "tile" + CHAT_TYPE -> "chat type" + ANIM -> "anim" + NPC -> "npc" + ENUM -> "enum" + INV -> "inv" + else -> "null" + } + + fun char(name: String) = when (name) { + "string" -> STRING + "int" -> INT + "struct" -> STRUCT + "jingle" -> JINGLE + "item" -> ITEM + "sprite" -> SPRITE + "model" -> MODEL + "id_kit" -> ID_KIT + "interface" -> COMPONENT + "map_area" -> MAP_AREA + "skill" -> SKILL + "tile" -> TILE + "chat_type" -> CHAT_TYPE + "anim" -> ANIM + "npc" -> NPC + "enum" -> ENUM + "inv" -> INV + else -> null + } +} \ No newline at end of file diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/FontDefinition.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/FontDefinition.kt index 6da33a75ff..102328c07e 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/FontDefinition.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/FontDefinition.kt @@ -148,6 +148,7 @@ data class FontDefinition( var wordStart = 0 var tagStart = -1 var lastChar = -1 + var colour: String? = null for (index in input.indices) { var current: Int = charToByte(input[index]) and 0xff var extraWidth = 0 @@ -194,7 +195,12 @@ data class FontDefinition( "euro" -> addKernelWidth(8364) "copy" -> addKernelWidth(169) "reg" -> addKernelWidth(174) - else -> { + "blue", "orange", "green", "red", "red_orange", "yellow", "lime", "gold", "white", + "black", "navy", "maroon", "purple", "brown", "violet", "dark_green", "dark_red", + -> colour = tag + else -> if (tag.startsWith("col=")) { + colour = tag + } else { totalWidth += spriteWidth(tag, icons) ?: continue lastChar = -1 } @@ -215,13 +221,21 @@ data class FontDefinition( } if (totalWidth > widths[if (widths.size > output.size) output.size else widths.size - 1]) { if (lineLength >= 0) { - output.add(input.substring(lineStart, lineLength + 1 - wordStart)) + if (colour != null && output.isNotEmpty()) { + output.add("<${colour}>${input.substring(lineStart, lineLength + 1 - wordStart)}") + } else { + output.add(input.substring(lineStart, lineLength + 1 - wordStart)) + } lineStart = lineLength + 1 lastChar = -1 lineLength = -1 totalWidth -= wordWidth } else { - output.add(input.substring(lineStart, currentWidth)) + if (colour != null && output.isNotEmpty()) { + output.add("<${colour}>${input.substring(lineStart, currentWidth)}") + } else { + output.add(input.substring(lineStart, currentWidth)) + } lineStart = currentWidth lastChar = -1 lineLength = -1 @@ -235,7 +249,11 @@ data class FontDefinition( } } if (lineStart < input.length) { - output.add(input.substring(lineStart)) + if (colour != null && output.isNotEmpty()) { + output.add("<${colour}>${input.substring(lineStart)}") + } else { + output.add(input.substring(lineStart)) + } } return output } diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/QuickChatPhraseDefinition.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/QuickChatPhraseDefinition.kt index c9e7085a1f..a42da22d3f 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/QuickChatPhraseDefinition.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/data/QuickChatPhraseDefinition.kt @@ -30,12 +30,12 @@ data class QuickChatPhraseDefinition( else -> 0 } val string = when (type) { - QuickChatType.MultipleChoice -> enums[ids[index].first()].getString(key) + QuickChatType.MultipleChoice -> enums[ids[index].first()].string(key) QuickChatType.AllItems, QuickChatType.TradeItems -> items[key].name QuickChatType.SlayerAssignment -> { - enums[ids[index].first()].getString(key) + enums[ids[index].first()].string(key) } - QuickChatType.ClanRank, QuickChatType.SkillExperience -> enums[ids[index].first()].getString(key) + QuickChatType.ClanRank, QuickChatType.SkillExperience -> enums[ids[index].first()].string(key) else -> key.toString() } append(string) diff --git a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/NPCDecoder.kt b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/NPCDecoder.kt index ba28683e40..f96f6e92e3 100644 --- a/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/NPCDecoder.kt +++ b/cache/src/main/kotlin/world/gregs/voidps/cache/definition/decoder/NPCDecoder.kt @@ -7,7 +7,7 @@ import world.gregs.voidps.cache.definition.Parameters import world.gregs.voidps.cache.definition.data.NPCDefinition class NPCDecoder( - val member: Boolean, + val member: Boolean = true, private val parameters: Parameters = Parameters.EMPTY, ) : DefinitionDecoder(NPCS) { diff --git a/data/area/asgarnia/port_sarim/port_sarim.areas.toml b/data/area/asgarnia/port_sarim/port_sarim.areas.toml index adc54f2774..8f60c4e11d 100644 --- a/data/area/asgarnia/port_sarim/port_sarim.areas.toml +++ b/data/area/asgarnia/port_sarim/port_sarim.areas.toml @@ -3,7 +3,19 @@ x = [3008, 3064] y = [3171, 3263] [greater_port_sarim] -x = [2962,2962,2909,2909,3071,3071] -y = [3136,3187,3187,3263,3263,3136] +x = [2962, 2962, 2909, 2909, 3071, 3071] +y = [3136, 3187, 3187, 3263, 3263, 3136] tags = ["penguin_area"] hint = "near Port Sarim." + +[gerrants_fish_shop] +x = [3011, 3017] +y = [3223, 3229] + +[brians_battleaxe_shop] +x = [3023, 3030] +y = [3245, 3253] + +[betties_magic_shop] +x = [3011, 3016] +y = [3256, 3261] diff --git a/data/area/asgarnia/port_sarim/port_sarim.nav-edges.toml b/data/area/asgarnia/port_sarim/port_sarim.nav-edges.toml new file mode 100644 index 0000000000..7785d6bff7 --- /dev/null +++ b/data/area/asgarnia/port_sarim/port_sarim.nav-edges.toml @@ -0,0 +1,136 @@ +edges = [ + { from = { x = 3071, y = 3266 }, to = { x = 3071, y = 3276 } }, + { from = { x = 3071, y = 3276 }, to = { x = 3069, y = 3276 } }, + { from = { x = 3069, y = 3276 }, to = { x = 3066, y = 3269 } }, + { from = { x = 3066, y = 3269 }, to = { x = 3063, y = 3260 } }, + { from = { x = 3066, y = 3269 }, to = { x = 3057, y = 3265 } }, + { from = { x = 3057, y = 3265 }, to = { x = 3051, y = 3265 } }, + { from = { x = 3063, y = 3260 }, to = { x = 3060, y = 3254 } }, + { from = { x = 3063, y = 3260 }, to = { x = 3053, y = 3261 } }, + { from = { x = 3060, y = 3254 }, to = { x = 3053, y = 3247 } }, + { from = { x = 3053, y = 3247 }, to = { x = 3042, y = 3247 } }, + { from = { x = 3053, y = 3261 }, to = { x = 3051, y = 3265 } }, + { from = { x = 3051, y = 3265 }, to = { x = 3041, y = 3263 } }, + { from = { x = 3041, y = 3263 }, to = { x = 3036, y = 3255 } }, + { from = { x = 3041, y = 3263 }, to = { x = 3032, y = 3265 } }, + { from = { x = 3032, y = 3265 }, to = { x = 3027, y = 3268 } }, + { from = { x = 3027, y = 3268 }, to = { x = 3019, y = 3263 } }, + { from = { x = 3027, y = 3268 }, to = { x = 3038, y = 3276 } }, + { from = { x = 3038, y = 3276 }, to = { x = 3055, y = 3276 } }, + { from = { x = 3055, y = 3276 }, to = { x = 3069, y = 3276 } }, + { from = { x = 3055, y = 3276 }, to = { x = 3057, y = 3265 } }, + { from = { x = 3069, y = 3276 }, to = { x = 3071, y = 3276 } }, + { from = { x = 3019, y = 3263 }, to = { x = 3019, y = 3258 } }, + { from = { x = 3053, y = 3261 }, to = { x = 3057, y = 3265 } }, + { from = { x = 3042, y = 3247 }, to = { x = 3035, y = 3248 } }, + { from = { x = 3042, y = 3247 }, to = { x = 3036, y = 3255 } }, + { from = { x = 3036, y = 3255 }, to = { x = 3026, y = 3256 } }, + { from = { x = 3026, y = 3256 }, to = { x = 3019, y = 3258 } }, + { from = { x = 3019, y = 3258 }, to = { x = 3017, y = 3258 } }, + { from = { x = 3017, y = 3258 }, to = { x = 3016, y = 3258 }, + actions = [ + { object = { + option = "Open", + id = "door_668_closed", + x = 3017, + y = 3259, + success = { object = { id = "door_668_opened", x = 3016, y = 3259 } } + } }, + { tile = { x = 3016, y = 3258, radius = 1 } } + ] + }, + { from = { x = 3016, y = 3258 }, to = { x = 3017, y = 3258 }, + actions = [ + { object = { + option = "Open", + id = "door_668_closed", + x = 3017, + y = 3259, + success = { object = { id = "door_668_opened", x = 3016, y = 3259 } } + } }, + { tile = { x = 3017, y = 3258, radius = 1 } } + ] + }, + { from = { x = 3019, y = 3258 }, to = { x = 3019, y = 3247 } }, + { from = { x = 3035, y = 3248 }, to = { x = 3031, y = 3248 } }, + { from = { x = 3035, y = 3248 }, to = { x = 3026, y = 3242 } }, + { from = { x = 3031, y = 3248 }, to = { x = 3026, y = 3242 } }, + { from = { x = 3031, y = 3248 }, to = { x = 3030, y = 3248 }, + actions = [ + { object = { + option = "Open", + id = "door_668_closed", + x = 3031, + y = 3248, + success = { object = { id = "door_668_opened", x = 3030, y = 3248 } } + } }, + { tile = { x = 3030, y = 3248, radius = 1 } } + ] + }, + { from = { x = 3030, y = 3248 }, to = { x = 3031, y = 3248 }, + actions = [ + { object = { + option = "Open", + id = "door_668_closed", + x = 3031, + y = 3248, + success = { object = { id = "door_668_opened", x = 3030, y = 3248 } } + } }, + { tile = { x = 3031, y = 3248, radius = 1 } } + ] + }, + { from = { x = 3030, y = 3248 }, to = { x = 3026, y = 3245 } }, + { from = { x = 3026, y = 3242 }, to = { x = 3026, y = 3244 } }, + { from = { x = 3026, y = 3242 }, to = { x = 3019, y = 3247 } }, + { from = { x = 3026, y = 3242 }, to = { x = 3021, y = 3237 } }, + { from = { x = 3026, y = 3244 }, to = { x = 3026, y = 3245 }, + actions = [ + { object = { + option = "Open", + id = "door_668_closed", + x = 3026, + y = 3244, + success = { object = { id = "door_668_opened", x = 3026, y = 3245 } } + } }, + { tile = { x = 3026, y = 3245, radius = 1 } } + ] + }, + { from = { x = 3026, y = 3245 }, to = { x = 3026, y = 3244 }, + actions = [ + { object = { + option = "Open", + id = "door_668_closed", + x = 3026, + y = 3244, + success = { object = { id = "door_668_opened", x = 3026, y = 3245 } } + } }, + { tile = { x = 3026, y = 3244, radius = 1 } } + ] + }, + { from = { x = 3019, y = 3247 }, to = { x = 3021, y = 3237 } }, + { from = { x = 3021, y = 3237 }, to = { x = 3019, y = 3224 } }, + { from = { x = 3019, y = 3224 }, to = { x = 3016, y = 3217 } }, + { from = { x = 3016, y = 3217 }, to = { x = 3013, y = 3219 } }, + { from = { x = 3013, y = 3219 }, to = { x = 3013, y = 3220 }, + actions = [ + { object = { + option = "Open", + id = "door_668_closed", + x = 3013, + y = 3219, + success = { object = { id = "door_668_opened", x = 3013, y = 3220 } } + } }, + { tile = { x = 3013, y = 3220, radius = 1 } } + ] + }, + { from = { x = 3013, y = 3220 }, to = { x = 3013, y = 3219 }, actions = [ + { object = { + option = "Open", + id = "door_668_closed", + x = 3013, + y = 3219, + success = { object = { id = "door_668_opened", x = 3013, y = 3220 } } + } }, + { tile = { x = 3013, y = 3219, radius = 1 } } + ] }, +] \ No newline at end of file diff --git a/data/area/misthalin/draynor/draynor.areas.toml b/data/area/misthalin/draynor/draynor.areas.toml index c643e90bb3..94a960e087 100644 --- a/data/area/misthalin/draynor/draynor.areas.toml +++ b/data/area/misthalin/draynor/draynor.areas.toml @@ -25,8 +25,8 @@ x = [3075, 3085] y = [3265, 3274] [draynor_bank] -x = [3088, 3097] -y = [3240, 3246] +x = [3092, 3095] +y = [3241, 3245] tags = ["bank"] [draynor_fishing_area] diff --git a/data/area/misthalin/draynor/draynor.npcs.toml b/data/area/misthalin/draynor/draynor.npcs.toml index ba11f0e1f3..46c96c9e42 100644 --- a/data/area/misthalin/draynor/draynor.npcs.toml +++ b/data/area/misthalin/draynor/draynor.npcs.toml @@ -152,9 +152,6 @@ attack_bonus = 9 respawn_delay = 50 examine = "He guards the Draynor Market stalls from thieves." -[bed_draynor] -id = 2254 - [bank_guard_draynor] id = 2574 examine = "He's guarding the bank." @@ -168,9 +165,6 @@ id = 3299 pickpocket = { level = 38, stun_ticks = 8, stun_hit = 30, xp = 43.0, chance_min = 90, chance_max = 240, table = "master_farmer" } examine = "A master at gardening." -[wise_old_man_2] -id = 3820 - [fortunato] id = 3671 shop = "fortunatos_fine_wine" @@ -225,25 +219,12 @@ examine = "The hat's a dead giveaway." [chicken_draynor_2] id = 288 -[wise_old_man_2_2] -id = 2253 - -[wise_old_man_2_3] -id = 11569 - [ava_2] id = 5198 [scout_draynor_2] id = 5569 -[thing_under_the_bed] -id = 2255 -hitpoints = 250 -def = 5 -style = "melee" -max_hit_melee = 10 - [fishing_spot_small_net_bait_draynor] id = 327 fishing_net = { items = ["small_fishing_net"], bait = { none = ["raw_shrimps", "raw_anchovies"] } } diff --git a/data/area/misthalin/draynor/wise_old_man/wise_old_man.anims.toml b/data/area/misthalin/draynor/wise_old_man/wise_old_man.anims.toml new file mode 100644 index 0000000000..b8209a90a9 --- /dev/null +++ b/data/area/misthalin/draynor/wise_old_man/wise_old_man.anims.toml @@ -0,0 +1,8 @@ +[bed_block] +id = 2168 + +[bed_attack] +id = 2167 + +[bed_death] +id = 2169 diff --git a/data/area/misthalin/draynor/wise_old_man/wise_old_man.combat.toml b/data/area/misthalin/draynor/wise_old_man/wise_old_man.combat.toml new file mode 100644 index 0000000000..12ab5a8ebb --- /dev/null +++ b/data/area/misthalin/draynor/wise_old_man/wise_old_man.combat.toml @@ -0,0 +1,11 @@ +[thing_under_the_bed] +attack_speed = 4 +defend_anim = "bed_defend" +defend_sound = "bed_defend" +death_anim = "bed_death" +death_sound = "bed_death" + +[thing_under_the_bed.attack] +anim = "bed_attack" +target_sound = "bed_attack" +target_hit = { offense = "melee", max = 10 } diff --git a/data/area/misthalin/draynor/wise_old_man/wise_old_man.drops.toml b/data/area/misthalin/draynor/wise_old_man/wise_old_man.drops.toml new file mode 100644 index 0000000000..a57a0daf96 --- /dev/null +++ b/data/area/misthalin/draynor/wise_old_man/wise_old_man.drops.toml @@ -0,0 +1,68 @@ +[wise_old_man_gems] +roll = 25 +drops = [ + { id = "uncut_red_topaz", chance = 2 }, + { id = "uncut_sapphire", chance = 4 }, + { id = "uncut_opal", chance = 5 }, + { id = "uncut_emerald", chance = 4 }, + { id = "uncut_ruby", chance = 4 }, + { id = "uncut_jade", chance = 5 }, + { id = "uncut_diamond", chance = 1 }, +] + +[wise_old_man_runes] +roll = 9 +drops = [ + { id = "air_rune", amount = 3 }, + { id = "water_rune", amount = 2 }, + { id = "earth_rune", amount = 2 }, + { id = "body_rune", amount = 2 }, + { id = "mind_rune", amount = 2 }, + { id = "fire_rune", amount = 1 }, + { id = "law_rune", amount = 1 }, + { id = "nature_rune", amount = 1 }, + { id = "chaos_rune", amount = 1 }, +] + +[wise_old_man_herbs] +drops = [ + { id = "grimy_guam_noted" }, + { id = "grimy_marrentill_noted" }, + { id = "grimy_tarromin_noted" }, + { id = "grimy_ranarr_noted" }, + { id = "grimy_harralander_noted" }, +] + +[wise_old_man_seeds] +roll = 67 +drops = [ + { id = "potato_seed", chance = 4 }, + { id = "onion_seed", chance = 4 }, + { id = "tomato_seed", chance = 4 }, + { id = "cabbage_seed", chance = 4 }, + { id = "marigold_seed", chance = 4 }, + { id = "yanillian_seed", chance = 2 }, + { id = "cactus_seed", chance = 2 }, + { id = "cadavaberry_seed", chance = 2 }, + { id = "hammerstone_seed", chance = 2 }, + { id = "jangerberry_seed", chance = 2 }, + { id = "barley_seed", chance = 2 }, + { id = "nasturtium_seed", chance = 2 }, + { id = "strawberry_seed", chance = 2 }, + { id = "jute_seed", chance = 2 }, + { id = "woad_seed", chance = 2 }, + { id = "guam_seed", chance = 2 }, + { id = "marrentill_seed", chance = 2 }, + { id = "tarromin_seed", chance = 2 }, + { id = "toadflax_seed", chance = 2 }, + { id = "harralander_seed", chance = 2 }, + { id = "watermelon_seed", chance = 2 }, + { id = "rosemary_seed", chance = 2 }, + { id = "redberry_seed", chance = 2 }, + { id = "dwellberry_seed", chance = 2 }, + { id = "asgarnian_seed", chance = 2 }, + { id = "sweetcorn_seed", chance = 2 }, + { id = "whiteberry_seed", chance = 2 }, + { id = "wildblood_seed", chance = 2 }, + { id = "limpwurt_seed", chance = 1 }, +] \ No newline at end of file diff --git a/data/area/misthalin/draynor/wise_old_man/wise_old_man.enums.toml b/data/area/misthalin/draynor/wise_old_man/wise_old_man.enums.toml new file mode 100644 index 0000000000..fc40cce3ca --- /dev/null +++ b/data/area/misthalin/draynor/wise_old_man/wise_old_man.enums.toml @@ -0,0 +1,141 @@ +[wise_old_man_items] +keyType = "item" +valueType = "string" +values = { + anchovies = "I could do with some freshly cooked anchovies for a salad I'm planning.", + beer_glass = "My glassware got damaged when I moved here, so I need some new beer glasses.", + bones = "My plans for today require some sets of bones, the normal-sized sort you get from goblins.", + bronze_arrow = "I'm short of ammunition, so I could do with some bronze arrows. That way I can shoot at goblins through my window.", + bronze_bar = "I need some bronze bars.", + bronze_dagger = "I need a few bronze daggers to keep the goblins at bay.", + bronze_hatchet = "The head fell off my hatchet when I was chopping wood last week, so I need some more bronze hachets.", + bronze_mace = "I'd like some maces made of bronze.", + bronze_med_helm = "I could do with some medium-sized helmets. I'd like bronze ones.", + bronze_spear = "I need some bronze spears.", + bronze_sword = "My weapons collection isn't all it used to be. Some short swords made of bronze would be much appreciated.", + beer = "Strange as it may seem, I want a lot of beer!", + cadava_berries = "I've found a use for cadava berries.", + cooked_chicken = "I'm running short of food, so I'd like some cooked chickens.", + cooked_meat = "I'm a bit hungry. I'd like some cooked meat, not chicken or any sort of bird. And definitely not camel or rabbit either!", + copper_ore = "I need a few lumps of copper ore.", + cowhide = "I'd like a few cowhides.", + egg = "I'm going to make an omelette, so I'll need some eggs.", + feather = "I need a handful of feathers to stick in my beard.", + grain = "I'd like a bit of grain.", + iron_bar = "I need some iron bars.", + iron_mace = "Some iron maces would be useful.", + iron_ore = "A few lumps of iron ore would be nice.", + soft_clay = "I'll need some clay that's been softened so I can craft it.", + leather_gloves = "It's a bit nippy in this house, and my hands are cold. I need some leather gloves.", + logs = "This house is a bit cold, so I could do with some normal logs to burn.", + molten_glass = "Some chunks of molten glass would be the ideal patch for my cracked window.", + raw_potato = "I need some potatoes, if you'd be so kind.", + raw_rat_meat = "I hear pet cats are getting popular these days. I'd like some raw rat in case I get one.", + rune_essence = "I'd like to study the rune essence that the wizards have been talking about recently, so I need a few pieces.", + shrimps = "I could do with some freshly cooked shrimps for a salad I'm planning.", + silk = "My undergarments are getting a bit worn out. I'll be needing some sheets of silk to patch them.", + leather = "I'll be needing a few pieces of soft leather.", + tin_ore = "I need a few lumps of tin ore.", + ball_of_wool = "I saw an interesting bed in Karamja called a 'hammock'. It seemed to be made out of string, and I'd like some balls of wool so I can make my own.", + bowstring = "My shortbow's string is getting a bit worn out, so could you fetch me some bow strings?", + bread = "I don't have a decent larder here, so I can't store food very well. Now I'm out of loaves of bread.", + bronze_arrowtips = "I need a few bronze arrowheads to complete some arrows I was making.", + bronze_knife = "I'd like some bronze knives to throw at goblins.", + bronze_warhammer = "Could you fetch me some big warhammers made of bronze?", + bronze_wire = "I need some lengths of bronze wire to repair something.", + headless_arrow = "I want to make some arrows, so I'll need some headless arrows that have the feathers attached.", + swamp_paste = "My roof is leaking, so I need some swamp paste to fix it.", + iron_arrowtips = "I need some iron arrowheads to put on the arrows I was making.", + iron_knife = "I could do with some iron throwing knives to keep the goblins away from my house.", + iron_warhammer = "I'd like some chunky iron warhammers.", + leather_cowl = "The hail can be very heavy here, so I'd like a few leather cowls.", + pot_of_flour = "I'm out of flour, so I could do with a few pots of that.", + unfired_pie_dish = "Strange as this may seem, I need some unfired pie dishes.", + unfired_pot = "Believe it or not, I need a few unfired clay pots.", + leather_boots = "My footwear is getting a bit worn out, so I need a few pairs of leather boots.", +} + +[wise_old_man_item_hints] +keyType = "item" +valueType = "string" +values = { + anchovies = "Use a small fishing net to get anchovies from the sea south of here. Then cook them on a fire or range.", + beer_glass = "If you get some seaweed and a bucket of sand, you'll be able to mix them at a furnace to make molten glass. Then use a glassblowing pipe to make beer glasses. Most of what you need can be found on Entrana.", + bones = "You'll find bones easily enough if you fight creatures.", + bronze_arrow = "You can make them by chopping a log into arrowshafts and adding feathers to them, then smithing a bronze bar into arrowheads to complete the arrows.", + bronze_bar = "Mine some copper ore and tin ore. The furnace in Lumbridge can smelt them into bars of bronze.", + bronze_dagger = "Try smelting some copper and tin ore to make bronze. Then hammer the bronze on an anvil to make daggers.", + bronze_hatchet = "Try smelting some copper and tin ore to make bronze. Then hammer the bronze on an anvil to make hatchets.", + bronze_mace = "Try smelting some copper and tin ore to make bronze. Then hammer the bronze on an anvil to make maces.", + bronze_med_helm = "Try smelting some copper and tin ore to make bronze. Then hammer the bronze on an anvil to make helmets.", + bronze_spear = "If you kill some of those pesky goblins, you're bound to get some bronze spears.", + bronze_sword = "Try smelting some copper and tin ore to make bronze. Then hammer the bronze on an anvil to make short swords.", + beer = "There's a pub not far from here that will sell you plenty of that. Go west to Port Sarim.", + cadava_berries = "Just look around the woods south-west of Varrock.", + cooked_chicken = "You could try killing some chickens, then cooking their meat.", + cooked_meat = "Just cook some beef, rat or bear.", + copper_ore = "It should be easy enough for you to mine some copper ore.", + cowhide = "If you slaughter some cows, you'll get cowhides easily enough.", + egg = "Eggs are usually found where chickens are farmed.", + feather = "The cheapest way to get feathers is to kill chickens.", + grain = "There's a field full of it north of here.", + iron_bar = "Mine some iron ore. The furnace in Lumbridge can smelt them into bars of iron.", + iron_mace = "Try smelting some iron. Then hammer the bronze on an anvil to make maces.", + iron_ore = "It should be easy enough for you to mine some iron ore.", + soft_clay = "If you mine some clay, you can use containers of water on it to soften it.", + leather_gloves = "If you get the tanner in Al Kharid to turn some cowhides into leather, you'll be able to sew the gloves yourself. Buy a needle & thread from the crafting shop in Al Kharid.", + logs = "I suggest you take a hatchet and chop down some standard trees.", + molten_glass = "Use a bucket of sand with some soda ash on a furnace.", + raw_potato = "There's a field of those north of Lumbridge.", + raw_rat_meat = "You should find some big rats south-east of here in the swamp.", + rune_essence = "Ask the Archmage in the tower south of here to teleport you to the Rune Essence mine. Then you can mine me some pieces.", + shrimps = "Use a small fishing net to get shrimps from the sea south of here. Then cook them on a fire or range.", + silk = "There's a man in Al Kharid who sells it very cheaply. If you ever take any to Ardougne you'll get a good profit.", + leather = "There's a tanner in Al Kharid who will turn cowhides into leather.", + tin_ore = "You shouldn't have any trouble mining tin ore.", + ball_of_wool = "You can buy shears from the general store east of here. Shear some sheep, then spin the wool into balls on the spinning wheel in Lumbridge Castle.", + bowstring = "If you pick some flax, you can use the spinning wheel in Lumbridge Castle to spin it into bowstrings.", + bread = "Mix some flour and water to make bread dough, then bake it on a cooking range.", + bronze_arrowtips = "Try smelting some copper and tin ore to make bronze. Then hammer the bronze on an anvil to make arrowtips.", + bronze_knife = "Try smelting some copper and tin ore to make bronze. Then hammer the bronze on an anvil to make knives.", + bronze_warhammer = "Try smelting some copper and tin ore to make bronze. Then hammer the bronze on an anvil to make warhammers.", + bronze_wire = "Try smelting some copper and tin ore to make bronze. Then hammer the bronze on an anvil to make wire.", + headless_arrow = "Use a knife to chop some logs into arrowshafts. Then add a feather to each arrowshaft.", + swamp_paste = "Swamp tar is found south-east of here. Add it to a pot of flour and cook it on an open fire to make the paste.", + iron_arrowtips = "Try smelting some iron. Then hammer the bronze on an anvil to make arrowtips.", + iron_knife = "Try smelting some iron. Then hammer the bronze on an anvil to make knives.", + iron_warhammer = "Try smelting some iron. Then hammer the bronze on an anvil to make warhammers.", + leather_cowl = "If you get the tanner in Al Kharid to turn some cowhides into leather, you'll be able to make the cowls yourself. Buy needles & thread from the crafting shop in Al Kharid if you need any.", + pot_of_flour = "There's a windmill north of here. You can use it to grind grain into flour. Then use an empty pot to collect the flour.", + unfired_pie_dish = "Mine some clay. Then use a container full of water to soften it. In the Barbarian Village you'll be able to form this into pie dishes. Just don't bake them.", + unfired_pot = "Mine some clay. Then use a container full of water to soften it. In the Barbarian Village you'll be able to form this into pots. Just don't bake them.", + leather_boots = "If you get the tanner in Al Kharid to turn some cowhides into leather, you'll be able to make the boots yourself. Buy needles & thread from the crafting shop in Al Kharid if you need any.", +} + +[wise_old_man_npcs] +keyType = "npc" +valueType = "string" +values = { + father_aereck = "Father Aereck in Lumbridge is sure to reward you if you take a note to him for me.", + high_priest_entrana = "Could you please take a message to the High Priest of Entrana for me?", + reldo = "Reldo, the librarian in Varrock Palace, wrote to me recently about something. I need you to take my reply to him.", + thurgo = "Thurgo the dwarf is from an ancient tribe. I've found some of its history that he was asking about, and I need you to deliver the information to him.", + father_lawrence = "I hear my old friend Fr Lawrence in Varrock is drinking a bit too much. I'd like to get a letter to him about it.", + abbot_langley = "I've written a letter to my old friend Abbot Langley that I'd like you to deliver.", + oracle = "I'd like to get a note up to the Oracle on Ice Mountain.", + thing_under_the_bed = "Well, this is rather embarrassing, but I think there's some kind of monster in my house. Could you go upstairs and get rid of it, please?" +} + +[wise_old_man_npc_hints] +keyType = "npc" +valueType = "string" +values = { + father_aereck = "Just head eastwards to Lumbridge. He'll be in the church.", + high_priest_entrana = "There are some monks in Port Sarim, west of here, who will take you there so long as you're not carrying any weapons or armour.", + reldo = "The library is on the ground floor of the Palace in Varrock.", + thurgo = "Go west to Port Sarim, then southwards to some cliffs. He lives on the beach there.", + father_lawrence = "Varrock is a fairly long way north-east of here. The church is at the eastern side of the town, near the Palace.", + abbot_langley = "Walk northwards, past Draynor Manor. When you reach the Barbarian Village, go north-west until you find his Monastery. He'll be downstairs.", + oracle = "Walk northwards, past Draynor Manor. When you reach the Barbarian Village, go north-west until you see the Ice Mountain. It'll be at the top.", + thing_under_the_bed = "I think it's somewhere upstairs. It kept me awake all last night.", +} \ No newline at end of file diff --git a/data/area/misthalin/draynor/wise_old_man/wise_old_man.jingles.toml b/data/area/misthalin/draynor/wise_old_man/wise_old_man.jingles.toml new file mode 100644 index 0000000000..ec309170cb --- /dev/null +++ b/data/area/misthalin/draynor/wise_old_man/wise_old_man.jingles.toml @@ -0,0 +1,2 @@ +[wise_old_man] +id = 201 diff --git a/data/area/misthalin/draynor/wise_old_man/wise_old_man.npcs.toml b/data/area/misthalin/draynor/wise_old_man/wise_old_man.npcs.toml new file mode 100644 index 0000000000..ac9d44433b --- /dev/null +++ b/data/area/misthalin/draynor/wise_old_man/wise_old_man.npcs.toml @@ -0,0 +1,20 @@ +[wise_old_man_draynor] +id = 2253 + +[wise_old_man_2_3] +id = 11569 + +[wise_old_man_2] +id = 3820 + +[bed_draynor] +id = 2254 +hitpoints = 250 +def = 5 +combat_def = "thing_under_the_bed" +respawn_delay = 0 +examine = "It's a fairly ordinary bed, but..." + +[thing_under_the_bed] +id = 2255 +examine = "It's just like that dream I use to have when I was little." \ No newline at end of file diff --git a/data/area/misthalin/draynor/wise_old_man/wise_old_man.sounds.toml b/data/area/misthalin/draynor/wise_old_man/wise_old_man.sounds.toml new file mode 100644 index 0000000000..98140359df --- /dev/null +++ b/data/area/misthalin/draynor/wise_old_man/wise_old_man.sounds.toml @@ -0,0 +1,8 @@ +[bed_attack] +id = 1371 + +[bed_block] +id = 1373 + +[bed_death] +id = 1372 \ No newline at end of file diff --git a/data/area/misthalin/draynor/wise_old_man/wise_old_man.varbits.toml b/data/area/misthalin/draynor/wise_old_man/wise_old_man.varbits.toml new file mode 100644 index 0000000000..7a353e395c --- /dev/null +++ b/data/area/misthalin/draynor/wise_old_man/wise_old_man.varbits.toml @@ -0,0 +1,4 @@ +[wise_old_man_met] +id = 596 +persist = true +format = "boolean" \ No newline at end of file diff --git a/data/area/misthalin/draynor/wise_old_man/wise_old_man.vars.toml b/data/area/misthalin/draynor/wise_old_man/wise_old_man.vars.toml new file mode 100644 index 0000000000..663d44293a --- /dev/null +++ b/data/area/misthalin/draynor/wise_old_man/wise_old_man.vars.toml @@ -0,0 +1,24 @@ +[wise_old_man_task] +format = "string" +persist = true + +[wise_old_man_npc] +format = "list" +persist = true +values = [ -1, "father_aereck", "high_priest_entrana", "reldo", "thurgo", "father_lawrence", "abbot_langley", "oracle", "thing_under_the_bed" ] + +[wise_old_man_remaining] +format = "int" +persist = true + +[wise_old_man_tasks_completed] +format = "int" +persist = true + +[wise_old_man_letters_completed] +format = "int" +persist = true + +[wise_old_man_bed_kills] +format = "int" +persist = true diff --git a/data/area/misthalin/edgeville/edgeville.teles.toml b/data/area/misthalin/edgeville/edgeville.teles.toml index 74256b1b70..0bb286a41b 100644 --- a/data/area/misthalin/edgeville/edgeville.teles.toml +++ b/data/area/misthalin/edgeville/edgeville.teles.toml @@ -114,7 +114,7 @@ option = "Climb-down" tile = { x = 3069, y = 3517, level = 1 } delta = { level = -1 } -[2641] +[monastery_ladder_up] option = "Climb-up" tile = { x = 3057, y = 3483 } delta = { level = 1 } @@ -124,7 +124,7 @@ option = "Climb-down" tile = { x = 3057, y = 3483, level = 1 } delta = { level = -1 } -[2641] +[monastery_ladder_up] option = "Climb-up" tile = { x = 3046, y = 3483 } delta = { level = 1 } diff --git a/data/area/misthalin/edgeville/monastery/monastery.gfx.toml b/data/area/misthalin/edgeville/monastery/monastery.gfx.toml new file mode 100644 index 0000000000..a75c750230 --- /dev/null +++ b/data/area/misthalin/edgeville/monastery/monastery.gfx.toml @@ -0,0 +1,3 @@ +[heal] +id = 84 +height = 120 diff --git a/data/area/misthalin/edgeville/monastery/monastery.objs.toml b/data/area/misthalin/edgeville/monastery/monastery.objs.toml new file mode 100644 index 0000000000..f8a0a4040c --- /dev/null +++ b/data/area/misthalin/edgeville/monastery/monastery.objs.toml @@ -0,0 +1,3 @@ +[monastery_ladder_up] +id = 2641 +examine = "I can climb this." \ No newline at end of file diff --git a/data/area/misthalin/edgeville/monastery/monastery.sounds.toml b/data/area/misthalin/edgeville/monastery/monastery.sounds.toml new file mode 100644 index 0000000000..76161d9aa7 --- /dev/null +++ b/data/area/misthalin/edgeville/monastery/monastery.sounds.toml @@ -0,0 +1,2 @@ +[heal] +id = 166 diff --git a/data/area/misthalin/edgeville/monastery/monastery.vars.toml b/data/area/misthalin/edgeville/monastery/monastery.vars.toml new file mode 100644 index 0000000000..9e49151923 --- /dev/null +++ b/data/area/misthalin/edgeville/monastery/monastery.vars.toml @@ -0,0 +1,3 @@ +[edgeville_monastery_order_member] +format = "boolean" +persist = true diff --git a/data/client/enums.toml b/data/client/all.enums.toml similarity index 100% rename from data/client/enums.toml rename to data/client/all.enums.toml diff --git a/data/entity/player/dialogue/dialogue.ifaces.toml b/data/entity/player/dialogue/dialogue.ifaces.toml index 47708b4c43..87d21a2c32 100644 --- a/data/entity/player/dialogue/dialogue.ifaces.toml +++ b/data/entity/player/dialogue/dialogue.ifaces.toml @@ -961,6 +961,9 @@ id = 0 [.line2] id = 1 +[.line3] +id = 2 + [.continue] id = 3 diff --git a/data/entity/player/player.sounds.toml b/data/entity/player/player.sounds.toml index df988d2c1e..241885bf0f 100644 --- a/data/entity/player/player.sounds.toml +++ b/data/entity/player/player.sounds.toml @@ -36,3 +36,6 @@ id = 2393 [shearing] id = 761 + +[unarmed_kick] +id = 2565 diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/client/instruction/InterfaceHandler.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/client/instruction/InterfaceHandler.kt index 3a9b1583db..b6d3c74c4d 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/client/instruction/InterfaceHandler.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/client/instruction/InterfaceHandler.kt @@ -24,7 +24,7 @@ class InterfaceHandler( id.startsWith("summoning_") && id.endsWith("_creation") -> item = Item(ItemDefinitions.get(itemId).stringId) id == "summoning_trade_in" -> item = Item(ItemDefinitions.get(itemId).stringId) id == "exchange_item_sets" -> { - val expected = EnumDefinitions.get("exchange_item_sets").getInt(itemSlot + 1) + val expected = EnumDefinitions.get("exchange_item_sets").int(itemSlot + 1) if (expected != itemId) { logger.info { "Exchange item sets don't match [$player, expected=$expected, actual=$itemId]" } return null diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/client/ui/Interfaces.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/client/ui/Interfaces.kt index da9a396545..5b0a309f1f 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/client/ui/Interfaces.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/client/ui/Interfaces.kt @@ -281,8 +281,8 @@ fun Player.closeInterfaces(): Boolean { } fun Player.playTrack(trackIndex: Int) { - playMusicTrack(EnumDefinitions.get("music_tracks").getInt(trackIndex)) - val name = EnumDefinitions.get("music_track_names").getString(trackIndex) + playMusicTrack(EnumDefinitions.get("music_tracks").int(trackIndex)) + val name = EnumDefinitions.get("music_track_names").string(trackIndex) interfaces.sendText("music_player", "currently_playing", name) this["playing_song"] = true this["current_track"] = trackIndex diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/EnumDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/EnumDefinitions.kt index 5625cbb4b6..5c166e306b 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/EnumDefinitions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/EnumDefinitions.kt @@ -5,9 +5,11 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import org.jetbrains.annotations.TestOnly import world.gregs.config.Config +import world.gregs.voidps.cache.config.data.StructDefinition import world.gregs.voidps.cache.definition.data.EnumDefinition -import world.gregs.voidps.engine.data.definition.ItemDefinitions.loaded +import world.gregs.voidps.cache.definition.data.EnumTypes import world.gregs.voidps.engine.timedLoad +import world.gregs.voidps.type.Tile /** * Also known as DataMap in cs2 or tables @@ -18,6 +20,8 @@ object EnumDefinitions : DefinitionsDecoder { override var ids: Map = emptyMap() + var loaded = false + fun init(definitions: Array): EnumDefinitions { this.definitions = definitions loaded = true @@ -37,44 +41,149 @@ object EnumDefinitions : DefinitionsDecoder { loaded = false } + private fun key(keyType: Char, key: String) = when (keyType) { + EnumTypes.ITEM -> ItemDefinitions.get(key).id + EnumTypes.COMPONENT -> InterfaceDefinitions.get(key).id + EnumTypes.INV -> InventoryDefinitions.get(key).id + EnumTypes.NPC -> NPCDefinitions.get(key).id + EnumTypes.STRUCT -> StructDefinitions.get(key).id + else -> error("Unsupported enum type: ${keyType.code}") + } + + fun string(enum: String, key: String): String { + val definition = get(enum) + val key = key(definition.keyType, key) + return definition.string(key) + } + + fun int(enum: String, key: String): Int { + val definition = get(enum) + val key = key(definition.keyType, key) + return definition.int(key) + } + + fun item(enum: String, key: String): String { + val definition = get(enum) + assert(definition.valueType == EnumTypes.ITEM || definition.valueType == EnumTypes.ITEM_2) { "Enum $enum value type not Item, found: ${EnumTypes.name(definition.valueType)}" } + val key = key(definition.keyType, key) + return ItemDefinitions.get(definition.int(key)).stringId + } + + fun tile(enum: String, key: String): Tile { + val definition = get(enum) + assert(definition.valueType == EnumTypes.TILE) { "Enum $enum value type not Tile, found: ${EnumTypes.name(definition.valueType)}" } + val key = key(definition.keyType, key) + return Tile(definition.int(key)) + } + + fun struct(enum: String, key: String): StructDefinition { + val definition = get(enum) + assert(definition.valueType == EnumTypes.TILE) { "Enum $enum value type not Tile, found: ${EnumTypes.name(definition.valueType)}" } + val key = key(definition.keyType, key) + return StructDefinitions.get(definition.int(key)) + } + fun getStruct(id: String, index: Int, param: String): T { val enum = get(id) - val struct = enum.getInt(index) + val struct = enum.int(index) return StructDefinitions.get(struct)[param] } fun getStructOrNull(id: String, index: Int, param: String): T? { val enum = get(id) - val struct = enum.getInt(index) + val struct = enum.int(index) return StructDefinitions.getOrNull(struct)?.getOrNull(param) } fun getStruct(id: String, index: Int, param: String, default: T): T { val enum = get(id) - val struct = enum.getInt(index) + val struct = enum.int(index) return StructDefinitions.get(struct)[param, default] } - fun load(path: String): EnumDefinitions { + fun load(list: List): EnumDefinitions { timedLoad("enum extra") { + require(ItemDefinitions.loaded) { "Item definitions must be loaded before enum definitions" } + require(InterfaceDefinitions.loaded) { "Interface definitions must be loaded before enum definitions" } + require(InventoryDefinitions.loaded) { "Inventory definitions must be loaded before enum definitions" } + require(NPCDefinitions.loaded) { "NPC definitions must be loaded before enum definitions" } + require(StructDefinitions.loaded) { "Struct definitions must be loaded before enum definitions" } val ids = Object2IntOpenHashMap(definitions.size, Hash.VERY_FAST_LOAD_FACTOR) - Config.fileReader(path, 50) { - while (nextSection()) { - val stringId = section() - var id = 0 - val extras = Object2ObjectOpenHashMap(2, Hash.VERY_FAST_LOAD_FACTOR) - while (nextPair()) { - when (val key = key()) { - "id" -> { - id = int() - require(!ids.containsKey(stringId)) { "Duplicate enum id found '$stringId' at $path." } - ids[stringId] = id - definitions[id].stringId = stringId + val custom = mutableListOf() + for (path in list) { + Config.fileReader(path, 250) { + while (nextSection()) { + val stringId = section() + var id = -1 + var keyType: Char = 0.toChar() + var valueType: Char = 0.toChar() + var defaultString = "null" + var defaultInt = 0 + val extras = Object2ObjectOpenHashMap(2, Hash.VERY_FAST_LOAD_FACTOR) + val map = mutableMapOf() + while (nextPair()) { + when (val key = key()) { + "id" -> { + id = int() + require(!ids.containsKey(stringId)) { "Duplicate enum id found '$stringId' at $path." } + ids[stringId] = id + definitions[id].stringId = stringId + } + "keyType" -> { + val string = string() + keyType = EnumTypes.char(string) ?: error("Unknown enum type: $string") + } + "valueType" -> { + val string = string() + valueType = EnumTypes.char(string) ?: error("Unknown enum type: $string") + } + "defaultString" -> defaultString = string() + "defaultInt" -> defaultInt = int() + "values" -> while (nextEntry()) { + val key = key() + val keyInt = when (keyType) { + EnumTypes.ITEM, EnumTypes.ITEM_2 -> ItemDefinitions.getOrNull(key)?.id ?: error("Unknown item '$key' ${exception()}") + EnumTypes.COMPONENT -> InterfaceDefinitions.getOrNull(key)?.id ?: error("Unknown interface '$key' ${exception()}") + EnumTypes.INV -> InventoryDefinitions.getOrNull(key)?.id ?: error("Unknown inventory '$key' ${exception()}") + EnumTypes.NPC -> NPCDefinitions.getOrNull(key)?.id ?: error("Unknown npc '$key' ${exception()}") + EnumTypes.STRUCT -> StructDefinitions.getOrNull(key)?.id ?: error("Unknown struct '$key' ${exception()}") + else -> key.toInt() + } + map[keyInt] = value() + } + else -> extras[key] = value() } - else -> extras[key] = value() + } + if (id == -1 && map.isNotEmpty()) { + custom.add( + EnumDefinition( + keyType = keyType, + valueType = valueType, + defaultString = defaultString, + defaultInt = defaultInt, + length = map.size, + map = map, + extras = extras, + stringId = stringId, + ) + ) + } else { + definitions[id].extras = extras } } - definitions[id].extras = extras + } + } + if (custom.isNotEmpty()) { + val index = definitions.size + definitions = Array(index + custom.size) { i -> + if (i >= index) { + custom[i - index].also { + ids[it.stringId] = i + it.id = i + } + } else { + definitions[i] + } } } this.ids = ids @@ -84,4 +193,5 @@ object EnumDefinitions : DefinitionsDecoder { } override fun empty() = EnumDefinition.EMPTY + } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/InterfaceDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/InterfaceDefinitions.kt index a9f6d77e24..ff7c0913d4 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/InterfaceDefinitions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/InterfaceDefinitions.kt @@ -18,8 +18,12 @@ object InterfaceDefinitions : DefinitionsDecoder { override var ids: Map = emptyMap() var componentIds: Map = emptyMap() + var loaded = false + private set + fun init(definitions: Array): InterfaceDefinitions { this.definitions = definitions + loaded = true return this } @@ -28,12 +32,14 @@ object InterfaceDefinitions : DefinitionsDecoder { this.definitions = definitions this.ids = ids this.componentIds = components + loaded = true } fun clear() { this.definitions = emptyArray() this.ids = emptyMap() this.componentIds = emptyMap() + loaded = false } fun getComponentId(id: String, component: String) = componentIds["$id:$component"] diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/InventoryDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/InventoryDefinitions.kt index 432802989b..08b4f93b27 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/InventoryDefinitions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/InventoryDefinitions.kt @@ -13,8 +13,12 @@ object InventoryDefinitions : DefinitionsDecoder { override var definitions: Array = emptyArray() override var ids: Map = emptyMap() + var loaded = false + private set + fun init(definitions: Array): InventoryDefinitions { this.definitions = definitions + loaded = true return this } @@ -22,11 +26,13 @@ object InventoryDefinitions : DefinitionsDecoder { fun set(definitions: Array, ids: Map) { this.definitions = definitions this.ids = ids + loaded = true } fun clear() { this.definitions = emptyArray() this.ids = emptyMap() + loaded = false } override fun empty() = InventoryDefinition.EMPTY 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 885774626f..935b1c42e4 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 @@ -21,6 +21,7 @@ object ItemDefinitions : DefinitionsDecoder { override var definitions: Array = emptyArray() var loaded = false + private set val size: Int get() = definitions.size diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/NPCDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/NPCDefinitions.kt index e05becff88..52cf181c81 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/NPCDefinitions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/NPCDefinitions.kt @@ -20,8 +20,12 @@ object NPCDefinitions : DefinitionsDecoder { override lateinit var definitions: Array + var loaded = false + private set + fun init(definitions: Array): NPCDefinitions { this.definitions = definitions + loaded = true return this } @@ -29,17 +33,19 @@ object NPCDefinitions : DefinitionsDecoder { fun set(definitions: Array, map: Map) { this.definitions = definitions this.ids = map + loaded = true } fun clear() { definitions = emptyArray() ids = emptyMap() + loaded = false } fun load( paths: List, dropTables: DropTables? = null, - ) { + ): NPCDefinitions { timedLoad("npc extra") { val ids = Object2IntOpenHashMap() val refs = Object2IntOpenHashMap() @@ -97,5 +103,6 @@ object NPCDefinitions : DefinitionsDecoder { this.ids = ids ids.size } + return this } } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/StructDefinitions.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/StructDefinitions.kt index 7fe408ea95..cd19221652 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/StructDefinitions.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/data/definition/StructDefinitions.kt @@ -18,6 +18,9 @@ object StructDefinitions : DefinitionsDecoder { override var ids: Map = emptyMap() + var loaded = false + private set + fun init(definitions: Array): StructDefinitions { this.definitions = definitions loaded = true diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt index ff8bf46cf5..128918b72d 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/Movement.kt @@ -46,7 +46,7 @@ open class Movement( if (character is Player && !tile.noCollision) { val route = pathFinder.findPath(character, strategy, shape) character.steps.queueRoute(route, tile, tile.noCollision, tile.noRun) - } else { + } else if (tile != Tile.EMPTY) { character.steps.queueStep(tile, tile.noCollision, tile.noRun) } needsCalculation = false diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/target/NPCCharacterTargetStrategy.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/target/NPCCharacterTargetStrategy.kt index 4e346499cf..8949d8f40a 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/target/NPCCharacterTargetStrategy.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/character/mode/move/target/NPCCharacterTargetStrategy.kt @@ -2,6 +2,7 @@ package world.gregs.voidps.engine.entity.character.mode.move.target import org.rsmod.game.pathfinder.PathFinder import world.gregs.voidps.engine.entity.character.Character +import world.gregs.voidps.engine.entity.character.npc.NPC import world.gregs.voidps.type.Tile data class NPCCharacterTargetStrategy( @@ -21,14 +22,21 @@ data class NPCCharacterTargetStrategy( override val sizeY: Int get() = character.size - override fun destination(source: Character) = Tile(PathFinder.naiveDestination( - sourceX = source.tile.x, - sourceZ = source.tile.y, - sourceWidth = source.size, - sourceHeight = source.size, - targetX = character.tile.x, - targetZ = character.tile.y, - targetWidth = character.size, - targetHeight = character.size - ).packed) + override fun destination(source: Character): Tile { + if (source is NPC && source.id == "bed_draynor") { + return Tile.EMPTY + } + return Tile( + PathFinder.naiveDestination( + sourceX = source.tile.x, + sourceZ = source.tile.y, + sourceWidth = source.size, + sourceHeight = source.size, + targetX = character.tile.x, + targetZ = character.tile.y, + targetWidth = character.size, + targetHeight = character.size + ).packed + ) + } } diff --git a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/item/drop/DropTable.kt b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/item/drop/DropTable.kt index e651b1d8ca..507937fd2c 100644 --- a/engine/src/main/kotlin/world/gregs/voidps/engine/entity/item/drop/DropTable.kt +++ b/engine/src/main/kotlin/world/gregs/voidps/engine/entity/item/drop/DropTable.kt @@ -26,7 +26,7 @@ data class DropTable( * @param list optional list to add the drop to * @param player the player for [ItemDrop.predicate]'s */ - fun role(maximumRoll: Int = -1, list: MutableList = mutableListOf(), player: Player? = null): MutableList { + fun roll(maximumRoll: Int = -1, list: MutableList = mutableListOf(), player: Player? = null): MutableList { collect(list, maximumRoll, player, random(maximumRoll)) return list } diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/entity/character/mode/move/MovementTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/entity/character/mode/move/MovementTest.kt index 42ed837eb1..efd894ea4b 100644 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/entity/character/mode/move/MovementTest.kt +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/entity/character/mode/move/MovementTest.kt @@ -53,7 +53,7 @@ internal class MovementTest : KoinMock() { @Test fun `Player queues smart route`() { player.tile = Tile(10, 10) - val movement = Movement(player, TileTargetStrategy(Tile.EMPTY)) + val movement = Movement(player, TileTargetStrategy(Tile(1, 1))) movement.calculate() assertTrue(player.steps.isNotEmpty()) } @@ -61,7 +61,7 @@ internal class MovementTest : KoinMock() { @Test fun `Npc queues step`() { val npc = NPC(tile = Tile(10, 10)) - val movement = Movement(npc, TileTargetStrategy(Tile.EMPTY)) + val movement = Movement(npc, TileTargetStrategy(Tile(1, 1))) movement.calculate() assertTrue(npc.steps.isNotEmpty()) verify(exactly = 0) { diff --git a/engine/src/test/kotlin/world/gregs/voidps/engine/entity/item/drop/DropTableTest.kt b/engine/src/test/kotlin/world/gregs/voidps/engine/entity/item/drop/DropTableTest.kt index c1d0a494f6..36074defdc 100644 --- a/engine/src/test/kotlin/world/gregs/voidps/engine/entity/item/drop/DropTableTest.kt +++ b/engine/src/test/kotlin/world/gregs/voidps/engine/entity/item/drop/DropTableTest.kt @@ -90,7 +90,7 @@ internal class DropTableTest { val item2 = drop("2", 1) val root = DropTable(TableType.First, 5, listOf(item1, item2), 1) - val list = root.role() + val list = root.roll() assertEquals(listOf(item2), list) } @@ -104,7 +104,7 @@ internal class DropTableTest { val item2 = drop("2", 1) val root = DropTable(TableType.First, -1, listOf(item1, item2), 1) - val list = root.role(maximumRoll = 10) + val list = root.roll(maximumRoll = 10) assertTrue(list.isEmpty()) } diff --git a/game/src/main/kotlin/Main.kt b/game/src/main/kotlin/Main.kt index 60b4553e4b..33a2d583d7 100644 --- a/game/src/main/kotlin/Main.kt +++ b/game/src/main/kotlin/Main.kt @@ -124,7 +124,14 @@ object Main { single(createdAtStart = true) { NPCDefinitions.init(NPCDecoder(members, get()).load(cache)).load(files.list(Settings["definitions.npcs"]), get()) } single(createdAtStart = true) { ItemDefinitions.init(ItemDecoder(get()).load(cache)).load(files.list(Settings["definitions.items"])) } single(createdAtStart = true) { AnimationDefinitions(AnimationDecoder().load(cache)).load(files.list(Settings["definitions.animations"])) } - single(createdAtStart = true) { EnumDefinitions.init(EnumDecoder().load(cache)).load(files.find(Settings["definitions.enums"])) } + single(createdAtStart = true) { + get() + get() + get() + get() + get() + EnumDefinitions.init(EnumDecoder().load(cache)).load(files.list(Settings["definitions.enums"])) + } single(createdAtStart = true) { GraphicDefinitions(GraphicDecoder().load(cache)).load(files.list(Settings["definitions.graphics"])) } single(createdAtStart = true) { InterfaceDefinitions.init(InterfaceDecoder().load(cache)).load(files.list(Settings["definitions.interfaces"]), files.find(Settings["definitions.interfaces.types"])) } single(createdAtStart = true) { diff --git a/game/src/main/kotlin/content/achievement/TaskSystem.kt b/game/src/main/kotlin/content/achievement/TaskSystem.kt index 18c3f401d5..de4c577ba3 100644 --- a/game/src/main/kotlin/content/achievement/TaskSystem.kt +++ b/game/src/main/kotlin/content/achievement/TaskSystem.kt @@ -206,8 +206,8 @@ class TaskSystem( player["task_popup"] = index val difficulty = definition["task_difficulty", 0] val area = definition["task_area", 61] - val areaName = EnumDefinitions.get("task_area_names").getString(area) - val difficultyName = EnumDefinitions.get("task_difficulties").getString(difficulty) + val areaName = EnumDefinitions.get("task_area_names").string(area) + val difficultyName = EnumDefinitions.get("task_difficulties").string(difficulty) if (areaName.isNotBlank() && difficultyName.isNotBlank()) { player.message("You have completed the Task '${definition["task_name", ""]}' in the $difficultyName $areaName set!") } else { diff --git a/game/src/main/kotlin/content/achievement/Tasks.kt b/game/src/main/kotlin/content/achievement/Tasks.kt index a83afa95e9..d3d94ba09e 100644 --- a/game/src/main/kotlin/content/achievement/Tasks.kt +++ b/game/src/main/kotlin/content/achievement/Tasks.kt @@ -37,11 +37,11 @@ object Tasks { } fun forEach(areaId: Int, block: TaskIterator.() -> R?): R? { - var next = EnumDefinitions.get("task_area_start_indices").getInt(areaId) + var next = EnumDefinitions.get("task_area_start_indices").int(areaId) val structs = EnumDefinitions.get("task_structs") val iterator = TaskIterator() while (next != 4091 && next != 450 && next != 4094) { - val struct = structs.getInt(next) + val struct = structs.int(next) iterator.definition = StructDefinitions.getOrNull(struct) ?: break iterator.index = next iterator.skip = false diff --git a/game/src/main/kotlin/content/area/asgarnia/entrana/HighPriest.kt b/game/src/main/kotlin/content/area/asgarnia/entrana/HighPriest.kt new file mode 100644 index 0000000000..fd491a8e47 --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/entrana/HighPriest.kt @@ -0,0 +1,53 @@ +package content.area.asgarnia.entrana + +import content.area.misthalin.draynor_village.wise_old_man.OldMansMessage +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.item +import content.entity.player.dialogue.type.items +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import net.pearx.kasechange.toLowerSpaceCase +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.inv.carriesItem + +class HighPriest : Script { + init { + npcOperate("Talk-to", "high_priest_entrana") { + npc("Many greetings. Welcome to our fair island.") + wiseOldManLetter() + npc("Enjoy your stay here. May it be spiritually uplifting!") + } + + itemOnNPCOperate(npc = "high_priest_entrana") { + npc("No thank you, I am not accepting donations for the church at this time.") + } + } + + private suspend fun Player.wiseOldManLetter() { + if (get("wise_old_man_npc", "") != "high_priest_entrana" || !carriesItem("old_mans_message")) { + return + } + player("I've got a message for you from the Wise Old Man in Draynor Village.") + npc("How kind of you to bring me a message to this remote island!") + val reward = OldMansMessage.rewardLetter(this) ?: return + when (reward) { + "runes" -> items("nature_rune", "water_rune", "The High Priest gives you some runes.") // TODO proper message + "herbs" -> npc("Here, let me give you some herbs.") + "seeds" -> item("potato_seed", 400, "The High Priest gives you some seeds.") // TODO proper message + "prayer" -> { + item(167, "The High Priest blesses you.
You gain some Prayer xp.") + npc("In the name of Saradomin I shall bless you...") + } + "coins" -> { + item("coins_8", 400, "The High Priest gives you some coins.") + npc("I don't have much in the way of wealth, but I can spare you a few coins for your trouble.") + } + else -> { + item(reward, 400, "The High Priest gives you an ${reward.toLowerSpaceCase()}!") + npc("The beasts which dwell under this island occasionally drop gems when they die - please take this one as a sign of my gratitude.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/asgarnia/falador/Hairdresser.kt b/game/src/main/kotlin/content/area/asgarnia/falador/Hairdresser.kt index f63aa2d296..b69d2e25b0 100644 --- a/game/src/main/kotlin/content/area/asgarnia/falador/Hairdresser.kt +++ b/game/src/main/kotlin/content/area/asgarnia/falador/Hairdresser.kt @@ -56,7 +56,7 @@ class Hairdresser : Script { val type = if (beard) "beard" else "hair" val key = "look_${type}_$sex" val value = if (beard) { - EnumDefinitions.get(key).getInt(itemSlot / 2) + EnumDefinitions.get(key).int(itemSlot / 2) } else { EnumDefinitions.getStruct(key, itemSlot / 2, "body_look_id") } @@ -64,7 +64,7 @@ class Hairdresser : Script { } interfaceOption(id = "hairdressers_salon:colours") { (_, itemSlot) -> - set("makeover_colour_hair", EnumDefinitions.get("colour_hair").getInt(itemSlot / 2)) + set("makeover_colour_hair", EnumDefinitions.get("colour_hair").int(itemSlot / 2)) } interfaceClosed("hairdressers_salon") { diff --git a/game/src/main/kotlin/content/area/asgarnia/falador/MakeoverMage.kt b/game/src/main/kotlin/content/area/asgarnia/falador/MakeoverMage.kt index 375260e5f1..eb2336c5d9 100644 --- a/game/src/main/kotlin/content/area/asgarnia/falador/MakeoverMage.kt +++ b/game/src/main/kotlin/content/area/asgarnia/falador/MakeoverMage.kt @@ -69,7 +69,7 @@ class MakeoverMage : Script { } interfaceOption(id = "skin_colour:colour_*") { - set("makeover_colour_skin", EnumDefinitions.get("character_skin").getInt(it.component.removePrefix("colour_").toInt())) + set("makeover_colour_skin", EnumDefinitions.get("character_skin").int(it.component.removePrefix("colour_").toInt())) } interfaceOption("Confirm", "skin_colour:confirm") { @@ -214,6 +214,6 @@ class MakeoverMage : Script { val old = EnumDefinitions.get("look_${name}_${if (male) "female" else "male"}") val new = EnumDefinitions.get("look_${name}_${if (male) "male" else "female"}") val key = old.getKey(player.body.getLook(bodyPart)) - player.body.setLook(bodyPart, new.getInt(key)) + player.body.setLook(bodyPart, new.int(key)) } } diff --git a/game/src/main/kotlin/content/area/asgarnia/port_sarim/Betty.kt b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Betty.kt new file mode 100644 index 0000000000..ffe8c8da7a --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Betty.kt @@ -0,0 +1,26 @@ +package content.area.asgarnia.port_sarim + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Betty : Script { + init { + npcOperate("Talk-to", "betty_port_sarim") { (target) -> + npc("Hello there. Welcome to my magic emporium.") + choice { + option("Can I see your wares?") { + npc("Of course.") + openShop(target.def["shop"]) + } + option("Sorry, I'm not into magic.") { + npc("Well, if you see anyone who is, please send them my way.") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/asgarnia/port_sarim/Brian.kt b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Brian.kt new file mode 100644 index 0000000000..141042f6d2 --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Brian.kt @@ -0,0 +1,23 @@ +package content.area.asgarnia.port_sarim + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Brian : Script { + init { + npcOperate("Talk-to", "brian_port_sarim") { (target) -> + choice { + option("So, are you selling something?") { + npc("Yep, take a look at these great axes!") + openShop(target.def["shop"]) + } + option("'Ello.") { + npc("'Ello!") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/asgarnia/port_sarim/Gerrant.kt b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Gerrant.kt new file mode 100644 index 0000000000..888cc4c193 --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Gerrant.kt @@ -0,0 +1,21 @@ +package content.area.asgarnia.port_sarim + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Gerrant : Script { + init { + npcOperate("Talk-to", "gerrant*") { (target) -> + npc("Welcome! You can buy fishing equipment at my store. We'll also buy anything you catch off you.") + choice { + option("Let's see what you've got then.") { + openShop(target.def["shop"]) + } + option("Sorry, I'm not interested.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/asgarnia/port_sarim/Grum.kt b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Grum.kt new file mode 100644 index 0000000000..2085df00ca --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Grum.kt @@ -0,0 +1,24 @@ +package content.area.asgarnia.port_sarim + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Angry +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Grum : Script { + init { + npcOperate("Talk-to", "grum") { (target) -> + npc("Would you like to buy or sell some gold jewellery?") + choice { + option("Yes please.") { + openShop(target.def["shop"]) + } + option("No, I'm not that rich.") { + npc("Get out then! We don't want any riff-raff in here.") + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/asgarnia/port_sarim/Thurgo.kt b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Thurgo.kt index d930c2fa08..a8136a4c33 100644 --- a/game/src/main/kotlin/content/area/asgarnia/port_sarim/Thurgo.kt +++ b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Thurgo.kt @@ -1,8 +1,10 @@ package content.area.asgarnia.port_sarim +import content.area.misthalin.draynor_village.wise_old_man.OldMansMessage import content.entity.player.dialogue.* import content.entity.player.dialogue.type.* import content.quest.quest +import net.pearx.kasechange.toSentenceCase import world.gregs.voidps.engine.Script import world.gregs.voidps.engine.entity.character.player.Player import world.gregs.voidps.engine.entity.item.Item @@ -22,6 +24,7 @@ class Thurgo : Script { init { npcOperate("Talk-to", "thurgo") { + wiseOldManLetter() when (quest("the_knights_sword")) { "started", "find_thurgo" -> menu() "happy_thurgo" -> menuSword() @@ -40,6 +43,40 @@ class Thurgo : Script { } } + private suspend fun Player.wiseOldManLetter() { + if (get("wise_old_man_npc", "") != "thurgo" || !carriesItem("old_mans_message")) { + return + } + player("The Wise Old Man says he's got the information you wanted. Here's his message.") + npc("Ooh, thanks. I've been hoping he'd send me that information soon, although I wouldn't mind if he'd send me a pie instead!") + val reward = OldMansMessage.rewardLetter(this) ?: return + when (reward) { + "runes" -> { + items("nature_rune", "water_rune", "Thurgo gives you some runes.") // TODO check always same runes or not? + npc("Magic isn't really my thing. Here, take these.") + } + "herbs" -> { + item("grimy_tarromin", 400, "Thurgo gives you some banknotes that can be exchanged for herbs.") + npc("Some guy died down in the hole just north of my house, and he dropped these herbs. Here, take them.") + } + "seeds" -> { + item("potato_seed", 400, "Thurgo gives you some seeds.") + npc("I'm not the gardening type. Here, take these.") + } + "prayer" -> { + item(167, "Thurgo blesses you.
You gain some Prayer xp.") // TODO proper message + } + "coins" -> { + item("coins_8", 400, "Thurgo gives you some coins.") + npc("Here's some cash for your time.") + } + else -> { + item(reward, 400, "Thurgo gives you an ${reward.toSentenceCase()}!") + npc("I found this while I was mining. Hope you like it.") + } + } + } + suspend fun Player.menuReplacementSword() { choice { if (carriesItem("blurite_sword")) { diff --git a/game/src/main/kotlin/content/area/asgarnia/port_sarim/Wydin.kt b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Wydin.kt new file mode 100644 index 0000000000..138d59ce5a --- /dev/null +++ b/game/src/main/kotlin/content/area/asgarnia/port_sarim/Wydin.kt @@ -0,0 +1,36 @@ +package content.area.asgarnia.port_sarim + +import content.entity.npc.shop.openShop +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Idle +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script + +class Wydin : Script { + init { + npcOperate("Talk-to", "wydin") { (target) -> + npc("Welcome to my food store! Would you like to buy anything?") + choice { + option("Yes please.") { + openShop(target.def["shop"]) + } + option("No, thank you.") + option("What can you recommend?") { + npc("We have this really exotic fruit all the way from Karamja. It's called a banana.") + choice { + option("Hmm, I think I'll try one.") { + npc("Great. You might as well take a look at the rest of my wares as well.") + openShop(target.def["shop"]) + } + option("I don't like the sound of that.") { + npc("Well, it's your choice, but I do recommend them.") + } + } + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/fremennik_province/rellekka/Yrsa.kt b/game/src/main/kotlin/content/area/fremennik_province/rellekka/Yrsa.kt index b5d90f9e9b..6e506e8408 100644 --- a/game/src/main/kotlin/content/area/fremennik_province/rellekka/Yrsa.kt +++ b/game/src/main/kotlin/content/area/fremennik_province/rellekka/Yrsa.kt @@ -53,12 +53,12 @@ class Yrsa : Script { } interfaceOption(id = "yrsas_shoe_store:styles") { (_, itemSlot) -> - val value = EnumDefinitions.get("look_shoes_$sex").getInt(itemSlot / 2) + val value = EnumDefinitions.get("look_shoes_$sex").int(itemSlot / 2) set("makeover_shoes", value) } interfaceOption(id = "yrsas_shoe_store:colours") { (_, itemSlot) -> - set("makeover_colour_shoes", EnumDefinitions.get("colour_shoes").getInt(itemSlot / 2)) + set("makeover_colour_shoes", EnumDefinitions.get("colour_shoes").int(itemSlot / 2)) } interfaceOption("Confirm", "yrsas_shoe_store:confirm") { diff --git a/game/src/main/kotlin/content/area/kandarin/ourania/OuraniaAltar.kt b/game/src/main/kotlin/content/area/kandarin/ourania/OuraniaAltar.kt index b341e589c9..5e1acd0b2c 100644 --- a/game/src/main/kotlin/content/area/kandarin/ourania/OuraniaAltar.kt +++ b/game/src/main/kotlin/content/area/kandarin/ourania/OuraniaAltar.kt @@ -34,7 +34,7 @@ class OuraniaAltar(val drops: DropTables) : Script { } val runes = mutableListOf() for (i in 0 until essence) { - table.role(list = runes) + table.roll(list = runes) } for (drop in runes) { val item = drop.toItem() diff --git a/game/src/main/kotlin/content/area/misthalin/draynor_village/wise_old_man/OldMansMessage.kt b/game/src/main/kotlin/content/area/misthalin/draynor_village/wise_old_man/OldMansMessage.kt new file mode 100644 index 0000000000..2991e492b5 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/draynor_village/wise_old_man/OldMansMessage.kt @@ -0,0 +1,197 @@ +package content.area.misthalin.draynor_village.wise_old_man + +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.statement +import content.entity.player.inv.item.addOrDrop +import content.quest.wiseOldManScroll +import world.gregs.voidps.engine.Script +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.player.skill.level.Level.has +import world.gregs.voidps.engine.entity.item.drop.DropTables +import world.gregs.voidps.engine.entity.item.drop.ItemDrop +import world.gregs.voidps.engine.get +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.type.random + +class OldMansMessage : Script { + init { + itemOption("Read", "old_mans_message") { + when (get("wise_old_man_npc", "")) { + "father_aereck" -> wiseOldManScroll( + listOf( + "", + "To Aereck, resident priest of Lumbridge,", + "greetings:", + "", + "I am pleased to inform you that, following a", + "careful search, the staff of the seminary have", + "located your pyjamas.", + "", + "Before returning them to you, however, they", + "would be very interested to know exactly how", + "these garments came to be in the graveyard.", + "", + "Please pass on my regards to Urhney.", + "*D", + "", + "", + ), + ) + "abbot_langley" -> wiseOldManScroll( + listOf( + "", + "To Langley, Abbot of the Monastery of", + "Saradomin, greetings:", + "", + "Long has it been since our last meeting, my", + "friend, too long. Truly we are living in", + "tumultuous times, and the foul works of Zamorak", + "can be seen across the lands. Indeed, I hear a", + "whisper from the south that the power of the", + "Terrible One has been rediscovered!", + "But be of good cheer, my friend, for we are all", + "in the hands of Lord Saradomin.", + "", + "Until our next meeting, then,", + "*D", + "", + ), + ) + "high_priest_entrana" -> wiseOldManScroll( + listOf( + "To the High Priest of Entrana, greetings:", + "", + "In an effort to respond to your recent questions", + "about the effects of summoning the power of", + "Saradomin, I have spent some time searching", + "through the scrolls of Kolodion the Battle Mage.", + "He records that a bolt of lightning falls from", + "above, accompanied by a resounding crash, and", + "the victim loses up to 20 points of health.", + "However, he believed that this could be increased", + "by 50% should one be wearing the Cape of", + "Saradomin and be Charged when casting the", + "spell.", + "", + "Fare thee well, my young friend, - *D", + "", + ), + ) + "father_lawrence" -> wiseOldManScroll( + listOf( + "", + "To Lawrence, resident priest of Varrock,", + "greetings:", + "", + "Despite our recent conversation on this matter, I", + "hear that you are still often found in a less than", + "sober condition. I am forced to repeat the", + "warning I gave you at the time: if you continue", + "to indulge yourself in this manner, the Council", + "will have no choice but to transfer you to", + "Entrana where you can be supervised more", + "carefully.", + "", + "I trust you will heed this message.", + "*D", + "", + ), + ) + "thurgo" -> wiseOldManScroll( + listOf( + "", + "To Thurgo, master blacksmith, greetings:", + "", + "Following your request, I have spent some time", + "re-reading the relevant scrolls in the Library of", + "Varrock. It appears that when your forefathers", + "encountered that adventurer, he was on a quest", + "to find a mysterious shield.", + "", + "Many thanks for the recipe you sent me; I shall", + "certainly try this 'redberry pie' of which you", + "speak so highly.", + "", + "Regards,", + "*D", + "", + ), + ) + } + } + } + + companion object { + suspend fun rewardLetter(player: Player): String? { + if (player.inventory.isFull()) { + player.npc("I'd give you a reward, but you don't seem to have any space for it. Come back when you do.") + return null + } + player.inventory.remove("old_mans_message") + player.clear("wise_old_man_npc") + player.inc("wise_old_man_letters_completed") + return reward(player, true) + } + + suspend fun reward(player: Player, hard: Boolean): String { + val drops: DropTables = get() + val chance = random.nextInt(16) + if (chance < 1) { // 6.25% + val drops = drops.getValue("wise_old_man_gems").roll() + give(player, drops) + return drops.first().id + } else if (chance < 3) { // 12.5% + if (!repeat(player, "wise_old_man_runes", if (hard) 25 else 10)) { + player.statement("Unfortunately you don't have space for all the runes.") + } + return "runes" + } else if (chance < 5) { // 12.5% + if (!repeat(player, "wise_old_man_herbs", if (hard) 10 else 3)) { + player.statement("Unfortunately you don't have space for all the herbs.") + } + return "herbs" + } else if (chance < 7) { // 12.5% + if (!repeat(player, "wise_old_man_herbs", if (hard) 10 else 3)) { + player.statement("Unfortunately you don't have space for all the seeds.") + } + return "seeds" + } else if (player.has(Skill.Prayer, 3) && chance < 14) { // 43.75% + val range = if (hard) 215..430 else 185..370 + val amount = range.random(random) + player.exp(Skill.Prayer, amount.toDouble()) + return "prayer" + } else { // 12.5% or 56.25% depending on prayer level + val range = if (hard) 990..1020 else 185..215 + player.inventory.add("coins", range.random(random)) + return "coins" + } + } + + private fun repeat(player: Player, table: String, max: Int): Boolean { + val tables: DropTables = get() + val table = tables.getValue(table) + val drops = mutableListOf() + val count = random.nextInt(1, max + 1) + for (i in 0 until count) { + table.roll(list = drops) + } + return give(player, drops) + } + + private fun give(player: Player, drops: List): Boolean { + var dropped = false + for (drop in drops) { + val item = drop.toItem() + if (!player.addOrDrop(item.id, item.amount)) { + dropped = true + } + } + return !dropped + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/draynor_village/wise_old_man/ThingUnderTheBed.kt b/game/src/main/kotlin/content/area/misthalin/draynor_village/wise_old_man/ThingUnderTheBed.kt new file mode 100644 index 0000000000..0120f9b9fb --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/draynor_village/wise_old_man/ThingUnderTheBed.kt @@ -0,0 +1,44 @@ +package content.area.misthalin.draynor_village.wise_old_man + +import content.entity.combat.killer +import content.entity.effect.transform +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.type.player +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.instruction.handle.interactPlayer +import world.gregs.voidps.engine.entity.character.jingle +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.sound + +class ThingUnderTheBed : Script { + init { + npcOperate("Kick", "bed_draynor") { (target) -> + watch(target) + anim("unarmed_kick") + sound("unarmed_kick") + delay(1) + target.anim("bed_block") + clearWatch() + if (get("wise_old_man_npc", "") != "thing_under_the_bed") { + return@npcOperate + } + if (get("wise_old_man_remaining", 0) == 0) { + player("I think it's already dead. Maybe the Wise Old Man will reward me now?") + return@npcOperate + } + target.say("Gurrhh!") + jingle("wise_old_man") + delay(1) + target.transform("thing_under_the_bed") + target.interactPlayer(this, "Attack") + } + + npcDeath("bed_draynor") { + val killer = killer + if (killer is Player) { + killer.dec("wise_old_man_remaining") + killer.inc("wise_old_man_bed_kills") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/draynor_village/wise_old_man/WiseOldMan.kt b/game/src/main/kotlin/content/area/misthalin/draynor_village/wise_old_man/WiseOldMan.kt new file mode 100644 index 0000000000..e78ba3f9cd --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/draynor_village/wise_old_man/WiseOldMan.kt @@ -0,0 +1,588 @@ +package content.area.misthalin.draynor_village.wise_old_man + +import content.entity.player.bank.ownsItem +import content.entity.player.dialogue.Bored +import content.entity.player.dialogue.Confused +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Laugh +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.Shifty +import content.entity.player.dialogue.Shock +import content.entity.player.dialogue.type.ChoiceOption +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.item +import content.entity.player.dialogue.type.items +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import content.quest.questCompleted +import net.pearx.kasechange.toSentenceCase +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.data.definition.EnumDefinitions +import world.gregs.voidps.engine.entity.World +import world.gregs.voidps.engine.entity.character.npc.NPC +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.name +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.player.skill.level.Level.has +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.remove +import world.gregs.voidps.engine.inv.removeToLimit +import world.gregs.voidps.type.random + +class WiseOldMan : Script { + init { + npcOperate("Talk-to", "wise_old_man_draynor") { (target) -> + npc("Greetings, $name.") + if (get("wise_old_man_met", false)) { + checkTaskNpcs(target) + checkTaskItems() + choice("What would you like to say?") { + anyHelp(this@choice) + findJunk() + ask() + } + return@npcOperate + } + intro() + } + } + + suspend fun Player.checkTaskNpcs(npc: NPC) { + if (get("wise_old_man_npc", "") != "thing_under_the_bed") { + return + } + if (get("wise_old_man_remaining", 0) != 0) { + return + } + player("I've killed a creature that was under your bed.") + npc("Ah, thank you very much! Now I shall be able to sleep in peace.") + npc("Allow me to offer you an appropriate reward for your assistance...") + npc.anim("bind") + clear("wise_old_man_npc") + clear("wise_old_man_remaining") + exp(Skill.Constitution, (280..300).random(random).toDouble()) + } + + suspend fun Player.checkTaskItems() { + val item: String = get("wise_old_man_task") ?: return + if (!inventory.contains(item)) { + if (inventory.contains("${item}_noted")) { + player("Here, I've got the items you wanted.") + npc("Those are banknotes! I can't use those!") + } + return + } + val remaining: Int = get("wise_old_man_remaining") ?: return + if (inventory.count(item) < remaining) { + player("I've got some of the stuff you wanted.") + } else { + player("I've got all the stuff you asked me to fetch.") + } + val removed = inventory.removeToLimit(item, remaining) + if (removed != remaining) { + set("wise_old_man_remaining", remaining - removed) + npc("Ahh, you are very kind.") + player("I'll come back when I've got the rest.") + return + } + clear("wise_old_man_remaining") + clear("wise_old_man_task") + inc("wise_old_man_tasks_completed") + when (val reward = OldMansMessage.reward(this, hard.contains(item))) { + "runes" -> { + items("nature_rune", "water_rune", "The Wise Old Man gives you some runes.") + npc("Thank you, thank you! Please take these runes as a sign of my gratitude.") + } + "herbs" -> { + item("grimy_tarromin", 400, "The Wise Old Man gives you some backnotes that can be exchanged for herbs.") + npc("Thank you, thank you! Please take these herbs as a sign of my gratitude.") + } + "seeds" -> { + item("potato_seed", 400, "The Wise Old Man gives you some seeds.") + npc("Thank you, thank you! Please take these seeds as a sign of my gratitude.") + } + "prayer" -> { + item(167, "The Wise Old Man blesses you.
You gain some Prayer xp.") + npc("Thank you, thank you! In thanks, I shall bestow on you a simple blessing.") + } + "coins" -> { + item("coins_8", 400, "The Wise Old Man gives you some coins.") + npc("Thank you, thank you! Please take this money as a sign of my gratitude.") + } + else -> item( + reward, + 400, + "The Wise Old Man gives you an ${reward.toSentenceCase()}${ + when { + reward.endsWith("diamond") || reward.endsWith("ruby") || reward.endsWith("emerald") -> "!" + else -> "." + } + }", + ) + } + } + + private suspend fun Player.intro() { + player("So you're a wise old man, huh?") + npc("Less of the 'old' man, if you please!") + npc("But yes, I suppose you could say that. I prefer to think of myself as a sage.") + player("So what's a sage doing here?") + npc("I've spent most of my life studying this world in which we live. I've strode through the depths of the deadliest dungeons, roamed the murky jungles of Karamja, meditated on the glories of Saradomin on Entrana,") + npc("and read dusty tomes in the Library of Varrock.") + npc("Now I'm not as young as I used to be, I'm settling here where it's peaceful.") + if (!questCompleted("vampire_slayer")) { + npc("It's a pity about that vampyre that keeps attacking the village. At least Saradomin protects me.") + } + player("That's quite an exciting life you've had.") + npc("Exciting? Yes, I suppose so.") + npc("Now I'm here, perhaps I could offer you the benefit of my experience and wisdom?") + player("Thanks! So how can you help me?") + set("wise_old_man_met", true) + npc("Well, I imagine you've gathered up quite a lot of stuff on your travels. Things you used for quests a long time ago that you don't need now.") + npc("If you like, I can look through your bank and see if there's anything you can chuck away.") + npc("Alternatively, you can bring items here and show them to me. If I see that it's something you don't need, I'll let you know. I might even be willing to buy it.") + player("So you'll help me clear junk out of my bank?") + npc("Yes, that's right. Or I'd be happy to chat with you about the wonders of this world!") + choice("What would you like to say?") { + option("Could I have some free stuff, please?") { + npc("Deary deary me...") + if (!World.members) { + npc("I'm not giving out free money, but if you log into a members' world I'd be glad to reward you if you'd do a little job for me.") + return@option + } + npc("I'm not giving out free money, but I'd be happy to reward you if you'll do a little job for me.") + choice("What would you like to say?") { + option("Ok, what do you want me to do?") + option("Thanks, maybe some other time.") { + npc("As you wish. Farewell, $name.") + } + } + } + ask() + anyHelp(this@choice) + findJunk() + option("Thanks, maybe some other time.") + } + } + + private fun ChoiceOption.ask() { + option("I'd just like to ask you something.") { + npc("Please do!") + topic() + } + } + + private fun Player.anyHelp(option: ChoiceOption) { + if (contains("wise_old_man_task")) { + option.option("What did you ask me to do?") { + checkTask() + } + } else { + option.option("Is there anything I can do for you?") { + task() + } + } + } + + private fun ChoiceOption.findJunk() { + option("Could you check my items for junk, please?") { + choice { + option("Could you check my bank for junk, please?") { + npc("Certainly, but I should warn you that I don't know about all items.") + // TODO add junk search + npc("There doesn't seem to be any junk in your bank at all.") + } + option("Could you check my inventory for junk, please?") { + npc("Certainly, but I should warn you that I don't know about all items.") + // TODO add junk search + npc("There doesn't seem to be any junk in your inventory at all.") + } + // if (follower != null) { // TODO and has BoB + // option("Could you check my beast of burden for junk, please?") + // } + } + } + } + + private suspend fun Player.topic() { + choice("Pick a topic") { + option("Distant lands") { + choice("Pick a topic") { + option("The Wilderness") { + player("Could you tell me about the Wilderness, please?") + npc("If Entrana is a land dedicated to the glory of Saradomin, the Wilderness is surely the land of Zamorak.") + npc("It's a dangerous place, where adventurers such as yourself may attack each other, using all their combat skills in the struggle for survival.") + npc("The Wilderness has different levels. In a low level area, you can only fight adventurers whose combat level is close to yours.") + npc("But if you venture into the high level areas in the far north, you can be attacked by adventurers who are significantly stronger than you.") + npc("Of course, you'd be able to attack considerably weaker people too, so it can be worth the risk.") + npc("If you dare to go to the far north-west of the Wilderness, there's a building called the Mage Arena where you can learn to summon the power of Saradomin himself!") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + option("Misty jungles") { + player("What can you tell me about jungles?") + npc("If it's jungle you want, look no further than the southern regions of Karamja.") + npc("Once you get south of Brimhaven, the whole island is pretty much covered in exotic trees, creepers and shrubs.") + npc("There's a small settlement called Tai Bwo Wannai Village in the middle of the island. It's a funny place; the chieftain's an unfriendly chap and his sons are barking mad.") + npc("Honestly, one of them asked me to stuff a dead monkey with seaweed so he could EAT it!") + npc("Further south you'll find Shilo Village. It's been under attack by terrifying zombies in recent months, if my sources are correct.") + npc("The jungle's filled with nasty creatures. There are vicious spiders that you can hardly see before they try to bite your legs off, and great big jungle ogres.") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + option("Underground domains") { + player("Tell me about what's underground.") + npc("Oh, the dwarven realms?") + npc("Yes, there was a time, back in the Fourth Age, when we humans wouldn't have been able to venture underground. That was before we had magic; the dwarves were quite a threat.") + npc("Still, it's much more friendly now. You can visit the vast dwarven mine if you like; the entrance is on the mountain north of Falador.") + npc("If you go further west you may be able to visit the dwarven city of Keldagrim. But they were a bit cautious about letting humans in, last time I asked.") + npc("On the other hand, if you go west of Brimhaven, you'll find a huge underground labyrinth full of giants, demons, dogs and dragons to fight. It's even bigger than the caves under Taverley, although the Taverley") + npc("dungeon's pretty good for training your combat skills.") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + option("Mystical realms") { + player("What mystical realms can I visit?") + npc("The fabled Lost City of Zanaris has an entrance somewhere near here. Perhaps some day you'll go there.") + npc("Also, in my research I came across ancient references to some kind of Abyss. Demons from the Abyss have already escaped into this land; Saradomin be thanked that they are very rare!") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + } + } + option("Strange beasts") { + choice("Pick a topic") { + option("Biggest & Baddest") { + player("What's the biggest monster in the world?") + npc("There's a mighty fire-breathing dragon living underground in the deep Wilderness, known as the King Black Dragon. It's a fearsome beast, with a breath that can poison you, freeze you to the ground or") + npc("incinerate you where you stand.") + npc("But even more deadly is the Queen of the Kalphites. As if her giant mandibles of death were not enough, she also throws her spines at her foes with deadly force. She can even cast rudimentary spells.") + npc("Some dark power must be protecting her, for she can block attacks using prayer just as humans do.") + npc("Another beast that's worthy of a special mention is the Shaikahan. It dwells in the eastern reaches of Karamja, and is almost impossible to kill except with specially prepared weapons.") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + option("Poison and how to survive it") { + player("What does poison do?") + npc("Many monsters use poison against their foes. If you get poisoned, you will not feel it at the time, but later you will begin to suffer its effects, and your life will drain slowly from you.") + } + option("Wealth through slaughter") { + player("What monsters drop good items?") + npc("As a general rule, tougher monsters drop more valuable items. But even a lowly hobgoblin can drop valuable gems; it just does this extremely rarely.") + npc("If you can persuade the Slayer Masters to train you as a Slayer, you will be able to fight certain monsters that drop valuable items far more often.") + npc("You might care to invest in an enchanted dragonstone ring. These are said to make a monster drop its most valuable items a little more often.") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + option("Random events") { + player("What are these strange monsters that keep appearing out of nowhere and attacking me when I'm training?") + npc("Ah, I imagine you see a lot of those.") + npc("Creatures such as the rock golem, river troll and tree spirit dwell in places where adventurers frequently go to train their skills. While you're training you will often disturb one by accident. It will then get angry and") + npc("attack you. The safest way to deal with them is to run away immediately, but they sometimes drop valuable items if you kill them.") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + } + } + option("Days gone by") { + choice("Pick a topic") { + option("Heroic figures") { + player("Tell me about valiant heroes!") + npc("Ha ha ha... There are plenty of heroes. Always have been, always will be, until the fall of the world.") + npc("If you'd do a few more quests, you'd soon become a fairly noted adventurer yourself.") + npc("But I suppose I could tell you of a couple...") + npc("Yes, there was a man called Arrav. No-one knew where he came from, but he was a fearsome fighter, a skillful hunter and a remarkable farmer. He lived in the ancient settlement of Avarrocka, defending it from") + npc("goblins, until he went forth in search of some strange artefact long desired by the dreaded Mahjarrat.") + npc("Perhaps some day I shall be able to tell you what became of him.") + npc("But do not let your head be turned by heroics. Randas was another great man, but he let himself be beguiled into turning to serve Zamorak, and they say he is now a mindless creature deep in the Underground Pass that") + npc("leads to Isafdar.") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + option("The origin of magic") { + player("Where did humans learn to use magic?") + npc("Ah, that was quite a discovery! It revolutionised our way of life and jolted us into this Fifth Age of the world.") + npc("They say a traveller in the north discovered the key, although no records state exactly what he found. From this he was able to summon the magic of the four elements, using magic as a tool and a weapon.") + npc("He and his followers then learnt how to bind the power into runes so that others could use it.") + npc("In the land south of here they constructed an immense tower where the power could be studied, but followers of Zamorak destroyed it with fire many years ago, and much of the knowledge was lost.") + npc("Perhaps one day those lost secrets will be uncovered once more.") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + option("Settlements") { + player("I suppose you'd know about the history of today's cities?") + npc("Yes, there are fairly good records of the formation of the cities from primitive settlements.") + npc("In the early part of the Fourth Age, of course, there were no permanent settlements. Tribes wandered the lands, staying where they could until the resources were exhausted.") + npc("This changed as people learnt to grow crops and breed animals, and now there are very few of the old nomadic tribes. There's at least one tribe roaming between the Troll Stronghold and Rellekka, though.") + npc("One settlement was Avarrocka, a popular trading centre.") + npc("In the west, Ardougne gradually formed under the leadership of the Carnillean family, despite the threat of the Mahjarrat warlord Hazeel who dwelt in that area until his downfall.") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + option("The Wise Old Man of Draynor Village") { + player("Tell me about yourself, old man.") + npc("Ah, so you want to know about me, eh?") + npc("Mmm... what could I say about myself? Let's see what I've done...") + npc("I've delved into the dungeon west of Brimhaven and heard the terrifying CRASH of the steel dragons battling each other for territory.") + npc("I spent some years on Entrana, where I learnt the techniques of pure meditation.") + npc("I've wandered through the vast desert that lies south of Al Kharid and seen the great walls of Menaphos and Sophanem.") + npc("Apart from all that, I've spent many a happy hour in dusty libraries, searching through ancient scrolls and texts for the wisdom of those who have passed on.") + npc("Plus plenty of other adventures, quests, journeys... Is there anything else you'd like to know?") + anythingElse() + } + } + } + option("Gods and demons") { + choice("Pick a topic") { + option("Three gods?") + option("The wars of the gods") { + player("I wanna know about the wars of the gods!") + npc("Ah, that was a terrible time. The armies of Saradomin fought gloriously against the minions of Zamorak, but many brave warriors and noble cities were overthrown and destroyed utterly.") + player("How did it all end?") + npc("Before the Zamorakian forces could be utterly routed, Lord Saradomin took pity on them and the battle- scarred world, and allowed a truce.") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + option("The Mahjarrat") { + player("What are the Mahjarrat?") + npc("Very little is written about the tribe of the Mahjarrat. They are believed to be from the realm of Freneskae, or Frenaskrae - the spelling in this tongue is only approximate.") + npc("One of them, the foul Zamorak, has achieved godhood, although none knows how this came about.") + } + option("Wielding the power of the gods") { + player("Can I wield the power of Saradomin myself?") + npc("If you travel to the Mage Arena in the north-west reaches of the Wilderness, the battle mage Kolodion may be willing to let you learn to summon the power of Saradomin, should you be able to pass his test.") + npc("Is there anything else you'd like to ask?") + anythingElse() + } + } + } + option("Your hat!") { + player("I want to ask you about your hat.") + npc("Why, thank you! I rather like it myself.") + choice("What would you like to say?") { + option("Where did you get it?") { + npc("Oh, I saw it on the floor when I was out for my morning stroll.") + player("You found a PARTY HAT on the floor?!") + npc("Yes, that's right. Would you like to ask me about something else?") + choice("What would you like to say?") { + option("How can I get a hat like that?") { + npc("You could buy one off another player, or wait until they're next made available by the Council.") + } + option("Yes please.") + option("Thanks, maybe some other time.") + } + } + option("How can I get a hat like that?") + } + } + } + + itemOnNPCOperate("old_mans_message", "wise_old_man_draynor") { + player("Do you think I need to keep this?") + if (contains("wise_old_man_npc")) { + npc("Yes, you're meant to be delivering it for me!") + } else { + inventory.remove("old_mans_message") + npc("I asked you to deliver that for me. But I may as well take it back now.") + } + } + } + + private suspend fun Player.anythingElse() { + choice("What would you like to say?") { + option("Yes please.") + option("Thanks, maybe some other time.") + } + } + + suspend fun Player.checkTask() { + val npc: String? = get("wise_old_man_npc") + if (npc != null) { + val intro = EnumDefinitions.string("wise_old_man_npcs", npc) + npc(intro) + if (npc != "thing_under_the_bed" && !ownsItem("old_mans_message")) { + npc("You seem to have mislaid my letter, so here's another copy.") + if (!inventory.add("old_mans_message")) { + npc("Please make room in your inventory to carry the letter.") + return + } + } + hintNpc(npc) + } + val item: String = get("wise_old_man_task") ?: return + val remaining: Int = get("wise_old_man_remaining") ?: return + val intro = EnumDefinitions.string("wise_old_man_items", item) + npc("$intro I still need $remaining.") + hintItem(item) + } + + suspend fun Player.task() { + npc("I'm sure I can think of a few little jobs. This won't be a quest, mind you, just a little favour...") + if (random.nextInt(100) < 16) { + val npc = setOf( + "father_aereck", + "high_priest_entrana", + "reldo", + "thurgo", + "father_lawrence", + "abbot_langley", + "oracle", + "thing_under_the_bed", + ).random(random) + val intro = EnumDefinitions.string("wise_old_man_npcs", npc) + npc(intro) + set("wise_old_man_npc", npc) + if (npc == "thing_under_the_bed") { + set("wise_old_man_remaining", 1) + } else { + npc("Here's the letter") + if (!inventory.add("old_mans_message")) { + npc("Please make room in your inventory to carry the letter.") + return + } + } + hintNpc(npc) + return + } + val amount = random.nextInt(3, 16) + val item = tasks().random(random) + set("wise_old_man_task", item) + set("wise_old_man_remaining", amount) + val intro = EnumDefinitions.string("wise_old_man_items", item) + npc("$intro. Please bring me $amount.") + hintItem(item) + } + + private suspend fun Player.hintNpc(npc: String) { + choice("What would you like to say?") { + option("Where do I need to go?") { + npc(EnumDefinitions.string("wise_old_man_npc_hints", npc)) + player("Right, I'll see you later.") + } + option("Right, I'll see you later.") + } + } + + private suspend fun Player.hintItem(item: String) { + choice("What would you like to say?") { + option("Where can I get that?") { + npc(EnumDefinitions.string("wise_old_man_item_hints", item)) + player("Right, I'll see you later.") + } + option("Right, I'll see you later.") + } + } + + private val hard = setOf( + "ball_of_wool", + "bowstring", + "bread", + "bronze_arrowtips", + "bronze_knife", + "bronze_warhammer", + "bronze_wire", + "headless_arrow", + "swamp_paste", + "iron_arrowtips", + "iron_knife", + "iron_warhammer", + "leather_cowl", + "pot_of_flour", + "unfired_pie_dish", + "unfired_pot", + "leather_boots", + ) + + private fun Player.tasks(): MutableSet { + val tasks = mutableSetOf( + "beer_glass", + "bones", + "bronze_arrow", + "bronze_bar", + "bronze_dagger", + "bronze_hatchet", + "beer", + "cadava_berries", + "cooked_chicken", + "cooked_meat", + "copper_ore", + "cowhide", + "egg", + "feather", + "grain", + "soft_clay", + "leather_gloves", + "logs", + "molten_glass", + "raw_potato", + "raw_rat_meat", + "shrimps", + "silk", + "leather", + "tin_ore", + "ball_of_wool", + "bowstring", + "bread", + "headless_arrow", + "swamp_paste", + "pot_of_flour", + "unfired_pot", + ) + if (has(Skill.Fishing, 15)) { + tasks.add("anchovies") + } + if (has(Skill.Smithing, 2)) { + tasks.add("bronze_mace") + } + if (has(Skill.Smithing, 3)) { + tasks.add("bronze_med_helm") + } + if (has(Skill.Smithing, 4)) { + tasks.add("bronze_wire") + } + if (has(Skill.Smithing, 5)) { + tasks.add("bronze_spear") + tasks.add("bronze_sword") + tasks.add("bronze_arrowtips") + } + if (has(Skill.Smithing, 7)) { + tasks.add("bronze_knife") + } + if (has(Skill.Smithing, 9)) { + tasks.add("bronze_warhammer") + } + if (has(Skill.Smithing, 15)) { + tasks.add("iron_bar") + } + if (has(Skill.Smithing, 17)) { + tasks.add("iron_mace") + } + if (has(Skill.Smithing, 20)) { + tasks.add("iron_arrowtips") + } + if (has(Skill.Smithing, 20)) { + tasks.add("iron_knife") + } + if (has(Skill.Smithing, 24)) { + tasks.add("iron_warhammer") + } + if (has(Skill.Mining, 17)) { + tasks.add("iron_ore") + } + if (has(Skill.Crafting, 7)) { + tasks.add("unfired_pie_dish") + tasks.add("leather_boots") + } + if (has(Skill.Crafting, 9)) { + tasks.add("leather_cowl") + } + if (questCompleted("rune_mysteries")) { + tasks.add("rune_essence") + } + return tasks + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/edgeville/Oracle.kt b/game/src/main/kotlin/content/area/misthalin/edgeville/Oracle.kt new file mode 100644 index 0000000000..5dcb60bfd3 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/edgeville/Oracle.kt @@ -0,0 +1,89 @@ +package content.area.misthalin.edgeville + +import content.area.misthalin.draynor_village.wise_old_man.OldMansMessage +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.Shifty +import content.entity.player.dialogue.type.item +import content.entity.player.dialogue.type.items +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import net.pearx.kasechange.toSentenceCase +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.inv.carriesItem +import world.gregs.voidps.type.random + +class Oracle : Script { + init { + npcOperate("Talk-to", "oracle") { + wiseOldManLetter() + player("Can you impart your wise knowledge to me, O Oracle?") + when (random.nextInt(0, 30)) { + 0 -> npc("They say that ham does not mix well with other kinds of meat.") + 1 -> npc("Capes are always in fashion!") + 2 -> npc("The goblins will never make up their minds on their own.") + 3 -> npc("No. I'm not in the mood.") + 4 -> npc("An answer is unimportant; it is the question that matters.") + 5 -> npc("Nothing like a tasty fish.") + 6 -> npc("Is it time to wake up? I am not sure...") + 7 -> npc("There are no crisps at the party.") + 8 -> npc("Don't judge a book by its cover - judge it on its' grammar and, punctuation.") + 9 -> npc("Pies...they're great, aren't they?") + 10 -> npc("It's not you; it's me.") + 11 -> npc("Help wanted? Enquire within.") + 12 -> npc("Jas left a stone behind.") + 13 -> npc("Too many cooks spoil the anchovy pizza.") + 14 -> npc("Do not fear the dragons...fear their kin.") + 15 -> npc("A bird in the hand can make a tasty snack.") + 16 -> npc("Sometimes you get lucky, sometimes you don't.") + 17 -> npc("The God Wars are over...as long as the thing they were fighting over remains hidden.") + 18 -> npc("Everyone you know will one day be dead.") + 19 -> npc("If a tree falls in the forest and no one is around, then nobody gets Woodcutting xp.") + 20 -> npc("The chicken came before the egg.") + 21 -> npc("A woodchuck does not chuck wood.") + 22 -> npc("When in Asgarnia, do as the Asgarnians do.") + 23 -> npc("Many secrets are buried under this land.") + 24 -> npc("The light at the end of the tunnel is the demon-infested lava pit.") + 25 -> npc("Yes, I can. But I'm not going to.") + 26 -> npc("The great snake of Guthix guards more than she knows.") + 27 -> npc("Beware the cabbage: it is both green AND leafy.") + 28 -> npc("He who uses the power of custard mixes it with his tears.") + 29 -> npc("Who guards the guardsmen?") + } + } + } + + private suspend fun Player.wiseOldManLetter() { + if (get("wise_old_man_npc", "") != "oracle" || !carriesItem("old_mans_message")) { + return + } + player("I've got a message for you from the Wise Old Man who lives in Draynor Village.") + npc("Many do my wisdom seek; few do their own wisdom to me send!") + val reward = OldMansMessage.rewardLetter(this) ?: return + when (reward) { + "runes" -> { + items("nature_rune", "water_rune", "The Oracle gives you some runes.") // TODO proper message + } + "herbs" -> { + item("grimy_tarromin", 400, "The Oracle gives you some herbs.") // TODO proper message + } + "seeds" -> { + item("potato_seed", 400, "The Oracle gives you some seeds.") + npc("New life from these shall perchance spring!") + } + "prayer" -> { + item(167, "The Oracle blesses you.
You gain some Prayer xp.") // TODO proper message + } + "coins" -> { + item("coins_8", 400, "The Oracle gives you some coins.") // TODO proper message + } + else -> { + item(reward, 400, "The Oracle gives you an ${reward.toSentenceCase()}!") // TODO proper message + npc("I found this while I was mining. Hope you like it.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/AbbotLangley.kt b/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/AbbotLangley.kt new file mode 100644 index 0000000000..a6cea073e2 --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/AbbotLangley.kt @@ -0,0 +1,83 @@ +package content.area.misthalin.edgeville.monastery + +import content.area.misthalin.draynor_village.wise_old_man.OldMansMessage +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.type.* +import net.pearx.kasechange.toSentenceCase +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.entity.character.areaSound +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.character.player.equip.has +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.character.player.skill.level.Level.has +import world.gregs.voidps.engine.inv.carriesItem + +class AbbotLangley : Script { + init { + npcOperate("Talk-to", "abbot_langley") { + npc("Greetings traveller.") + wiseOldManLetter() + choice { + option("Can you heal me? I'm injured.") { + npc("Ok.") + message("You feel a little better.") + gfx("heal") + areaSound("heal", tile, radius = 10) + levels.restore(Skill.Constitution, -levels.getOffset(Skill.Constitution)) + statement("Abbot Langley places his hands on your head. You feel a little better.") + } + option("Isn't this place built a bit out of the way?") { + npc("We like it that way actually! We get disturbed less. We still get rather a large amount of travellers looking for sanctuary and healing here as it is!") + } + if (!get("edgeville_monastery_order_member", false)) { + option("How do I get further into the monastery?") { + npc("I'm sorry but only members of our order are allowed in the second level of the monastery.") + choice { + option("Well can I join your order?") { + canIJoin() + } + option("Oh, sorry.") + } + } + } + } + } + } + + private suspend fun Player.canIJoin() { + if (!has(Skill.Prayer, 31)) { + npc("No. I am sorry, but I feel you are not devout enough.") + message("You need a prayer level of 31 to join the order.") + return + } + npc("Ok, I see you are someone suitable for our order. You may join.") + set("edgeville_monastery_order_member", true) + } + + private suspend fun Player.wiseOldManLetter() { + if (get("wise_old_man_npc", "") != "abbot_langley" || !carriesItem("old_mans_message")) { + return + } + player("I've got a message for you from your friend in Draynor Village.") + npc("Gosh, you are very kind to bring a message to my remote monastery!") + val reward = OldMansMessage.rewardLetter(this) ?: return + when (reward) { + "runes" -> items("nature_rune", "water_rune", "Abbot Langley gives you some runes.") // TODO proper message + "herbs" -> { + item("grimy_tarromin", 400, "Abbot Langley gives you some banknotes that can be exchanged for herbs.") + npc("I grow a few herbs in my little cabbage patch; please take some as a sign of my gratitude.") + } + "seeds" -> item("potato_seed", 400, "Abbot Langley gives you some seeds.") // TODO proper message + "prayer" -> { + item(167, "Abbot Langley blesses you.
You gain some Prayer xp.") + npc("Allow me to bestow on you Saradomin's blessings...") + } + "coins" -> item("coins_8", 400, "Abbot Langley gives you some coins.") + else -> item(reward, 400, "Abbot Langley gives you an ${reward.toSentenceCase()}!") // TODO proper message + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/BrotherJared.kt b/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/BrotherJared.kt new file mode 100644 index 0000000000..feba61565b --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/BrotherJared.kt @@ -0,0 +1,106 @@ +package content.area.misthalin.edgeville.monastery + +import com.github.michaelbull.logging.InlineLogger +import content.entity.player.dialogue.* +import content.entity.player.dialogue.type.* +import world.gregs.voidps.engine.Script +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.level.Level +import world.gregs.voidps.engine.entity.character.player.skill.level.Level.hasMax +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.engine.inv.replace +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 BrotherJared : Script { + init { + npcOperate("Talk-to", "brother_jared") { (target) -> + choice { + bold() + option("Praise be to Saradomin!") { + npc("Yes! Praise he who brings life to this world.") + } + } + } + } + + private val logger = InlineLogger() + + private fun ChoiceOption.bold() { + option("What can you do to help a bold adventurer like myself?") { + val hasStar = inventory.contains("unblessed_symbol") + if (hasStar) { + npc("Well I can bless that star of Saradomin you have, or I could tell you about the Skillcape of Prayer!") + } else if (hasMax(Skill.Prayer, 99)) { + skillcape() + return@option + } else { + npc("I can tell you about holy symbols or the Skillcape of Prayer.") + } + choice { + if (hasStar) { + option("Bless star, please.") { + player("Yes please.") + inventory.replace("unblessed_symbol", "holy_symbol") + item("holy_symbol", 400, "You give Jered the symbol. Jered closes his eyes and places his hand on the symbol. He softly chants. Jered passes you the holy symbol.") + } + } else { + option("Tell me about holy symbols.") { + npc("If you have a silver star, which is the holy symbol of Saradomin, then I can bless it. Then if you are wearing it, it will help you when you are praying.") + } + } + option("Tell me about the Skillcape of Prayer.") { + npc("The Skillcape of Prayer is the hardest of all the skillcapes to get; it requires much devotion to acquire but also imbues the wearer with the ability to briefly fly!") + npc("The Cape of Prayer also increases the amount of Prayer points restored from drinking potions when it is equipped. Is there something else I can do for you?") + choice { + bold() + option("No, thank you.") { + player("No thank you.") + } + } + } + } + } + } + + private suspend fun Player.skillcape() { + npc("Well, seeing as you are so devout in praising the gods, I could sell you a Skillcape of Prayer, which increases the amount of Prayer points restored when drinking potions.") + choice { + option("Yes, please. So few people have Skillcapes of Prayer!") { + npc("One as pious as you has certainly earned the right to wear one, but the monastery requires a donation of 99000 coins for the privilege.") + choice { + option("I'm afraid I can't afford that.") { + noThanks() + } + option("I am always happy to contribute towards the monastery's upkeep.") { + inventory.transaction { + val trimmed = Skill.entries.any { it != Skill.Prayer && levels.getMax(it) >= Level.MAX_LEVEL } + remove("coins", 99_000) + add("prayer_cape${if (trimmed) "_t" else ""}") + add("prayer_hood") + } + when (inventory.transaction.error) { + is TransactionError.Deficient -> { + player("But, unfortunately, I don't have enough money with me.") + npc("Well, come back and see me when you do.") + } + is TransactionError.Full -> npc("Unfortunately all Skillcapes are only available with a free hood, it's part of a skill promotion deal; buy one get one free, you know. So you'll need to free up some inventory space before I can sell you one.") + TransactionError.None -> npc("Excellent! Wear that cape with pride my friend.") + else -> logger.debug { "Error buying prayer skillcape: ${inventory.transaction.error}." } + } + } + } + } + option("No thanks, I can't afford one of those.") { + noThanks() + } + } + } + + private suspend fun Player.noThanks() { + npc("No thanks, I can't afford one of those.") + npc("I am sorry to hear that. If you should find yourself in wealthier times come back and see me.") + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/EdgevilleMonastery.kt b/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/EdgevilleMonastery.kt new file mode 100644 index 0000000000..1dbc210b1d --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/EdgevilleMonastery.kt @@ -0,0 +1,42 @@ +package content.area.misthalin.edgeville.monastery + +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Sad +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +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.entity.character.player.Teleport +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.entity.character.player.skill.level.Level.has +import world.gregs.voidps.engine.queue.queue + +class EdgevilleMonastery : Script { + init { + objTeleportTakeOff("Climb-up", "monastery_ladder_up") { _, _ -> + if (!get("edgeville_monastery_order_member", false)) { + queue("edgeville_monastery_member_dialogue") { + npc("abbot_langley", "I'm sorry but only members of our order are allowed in the second level of the monastery.") + choice { + option("Well can I join your order?") { + if (!has(Skill.Prayer, 31)) { + npc("abbot_langley", "No. I am sorry, but I feel you are not devout enough.") + message("You need a prayer level of 31 to join the order.") + return@option + } + npc("abbot_langley", "Ok, I see you are someone suitable for our order. You may join.") + set("edgeville_monastery_order_member", true) + } + option("Oh, sorry.") + } + } + return@objTeleportTakeOff Teleport.CANCEL + } + anim("climb_up") + start("teleport_delay", 2) + return@objTeleportTakeOff Teleport.CONTINUE + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/MonasteryMonk.kt b/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/MonasteryMonk.kt new file mode 100644 index 0000000000..de47f23a9d --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/edgeville/monastery/MonasteryMonk.kt @@ -0,0 +1,35 @@ +package content.area.misthalin.edgeville.monastery + +import content.entity.player.dialogue.Neutral +import content.entity.player.dialogue.Quiz +import content.entity.player.dialogue.type.choice +import content.entity.player.dialogue.type.npc +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.entity.character.areaSound +import world.gregs.voidps.engine.entity.character.player.skill.Skill + +class MonasteryMonk : Script { + init { + npcOperate("Talk-to", "monk_edgeville") { (target) -> + npc("Greetings traveller.") + choice { + option("Can you heal me? I'm injured.") { + npc("Ok.") + message("You feel a little better.") + gfx("heal") + areaSound("heal", tile, radius = 10) + levels.restore(Skill.Constitution, levels.getOffset(Skill.Constitution)) + } + option("Isn't this place built a bit out of the way?") { + npc("We like it that way actually! We get disturbed less. We still get rather a large amount of travellers looking for sanctuary and healing here as it is!") + } + if (!get("edgeville_monastery_order_member", false)) { + option("How do I get further into the monastery?") { + npc("You'll need to talk to Abbot Langley about that. He's usually to be found walking the halls of the monastery.") + } + } + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/church/FatherAereck.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/church/FatherAereck.kt index 3bcabfcc52..a6fbdf0a80 100644 --- a/game/src/main/kotlin/content/area/misthalin/lumbridge/church/FatherAereck.kt +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/church/FatherAereck.kt @@ -1,21 +1,25 @@ package content.area.misthalin.lumbridge.church +import content.area.misthalin.draynor_village.wise_old_man.OldMansMessage import content.entity.player.dialogue.* import content.entity.player.dialogue.type.* import content.quest.quest import content.quest.refreshQuestJournal +import net.pearx.kasechange.toSentenceCase import world.gregs.voidps.engine.Script import world.gregs.voidps.engine.client.ui.open import world.gregs.voidps.engine.data.Settings import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.entity.item.drop.DropTables import world.gregs.voidps.engine.inv.carriesItem import world.gregs.voidps.engine.inv.inventory import world.gregs.voidps.engine.inv.replace -class FatherAereck : Script { +class FatherAereck(val drops: DropTables) : Script { init { npcOperate("Talk-to", "father_aereck") { + wiseOldManLetter() when (quest("the_restless_ghost")) { "unstarted" -> { npc("Welcome to the church of holy Saradomin.") @@ -52,6 +56,35 @@ class FatherAereck : Script { } } + private suspend fun Player.wiseOldManLetter() { + if (get("wise_old_man_npc", "") != "father_aereck" || !carriesItem("old_mans_message")) { + return + } + player("The Wise Old Man of Draynor Village said you might reward me if I brought you this.") + npc("Oh, did he?") + val reward = OldMansMessage.rewardLetter(this) ?: return + when (reward) { + "runes" -> { + items("nature_rune", "water_rune", "Faether Aereck gives you some runes.") + npc("Well, maybe you'll have a use for these?") + } + "herbs" -> item("grimy_tarromin", 400, "Faether Aereck gives you some herbs.") // TODO proper message + "seeds" -> { + item("potato_seed", 400, "Faether Aereck gives you some seeds.") + npc("Well, maybe you'll find a use for these seeds?") + } + "prayer" -> { + item(167, "Father Aereck blesses you.
You gain some Prayer xp.") + npc("Well, it's still nice of you to bring the message here. Here, I shall bless you...") + } + "coins" -> item("coins_8", 400, "Faether Aereck gives you some coins.") + else -> { + item(reward, 400, "Father Aereck gives you an ${reward.toSentenceCase()}.") + npc("I suppose gems are always acceptable rewards!") + } + } + } + suspend fun Player.started() { npc("Have you got rid of the ghost yet?") player("I can't find Father Urhney at the moment.") diff --git a/game/src/main/kotlin/content/area/misthalin/lumbridge/church/GravestoneShop.kt b/game/src/main/kotlin/content/area/misthalin/lumbridge/church/GravestoneShop.kt index 107467c706..617eaf62f4 100644 --- a/game/src/main/kotlin/content/area/misthalin/lumbridge/church/GravestoneShop.kt +++ b/game/src/main/kotlin/content/area/misthalin/lumbridge/church/GravestoneShop.kt @@ -41,12 +41,12 @@ class GravestoneShop : Script { } interfaceOption(id = "gravestone_shop:button") { (_, itemSlot) -> - val name = EnumDefinitions.get("gravestone_names").getString(itemSlot) + val name = EnumDefinitions.get("gravestone_names").string(itemSlot) val id = name.replace(" ", "_").lowercase() if (get("gravestone_current", "memorial_plaque") == id) { return@interfaceOption } - val cost = EnumDefinitions.get("gravestone_price").getInt(itemSlot) + val cost = EnumDefinitions.get("gravestone_price").int(itemSlot) if (cost > 0 && !inventory.remove("coins", cost)) { notEnough("coins") return@interfaceOption diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/FatherLawrence.kt b/game/src/main/kotlin/content/area/misthalin/varrock/FatherLawrence.kt new file mode 100644 index 0000000000..6ce4db4e0a --- /dev/null +++ b/game/src/main/kotlin/content/area/misthalin/varrock/FatherLawrence.kt @@ -0,0 +1,61 @@ +package content.area.misthalin.varrock + +import content.area.misthalin.draynor_village.wise_old_man.OldMansMessage +import content.entity.player.dialogue.Drunk +import content.entity.player.dialogue.Happy +import content.entity.player.dialogue.Shifty +import content.entity.player.dialogue.type.item +import content.entity.player.dialogue.type.items +import content.entity.player.dialogue.type.npc +import content.entity.player.dialogue.type.player +import net.pearx.kasechange.toSentenceCase +import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.client.message +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.inv.carriesItem + +class FatherLawrence : Script { + init { + npcOperate("Talk-to", "father_lawrence") { + wiseOldManLetter() + npc("Oh, to be a father in the times of whiskey! I sing and I drink and I wake up in gutters.") + player("Good morning.") + npc("Top of the morning to you.") + } + } + + private suspend fun Player.wiseOldManLetter() { + if (get("wise_old_man_npc", "") != "father_lawrence" || !carriesItem("old_mans_message")) { + return + } + player("The Wise Old Man of Draynor Village sent you this message about your drinking habits!") + npc("mssge? wha messsge?") + npc("oh, msesesge for me.") + message("Oh dear, he doesn't look like he's going to be able to read the message!") + val reward = OldMansMessage.rewardLetter(this) ?: return + when (reward) { + "runes" -> { + items("nature_rune", "water_rune", "Father Lawrence gives you some runes.") + npc("Whee! Mmmgic power, kazamm...") + } + "herbs" -> { + item("grimy_tarromin", 400, "Father Lawrence gives you some herbs.") // TODO proper message + } + "seeds" -> { + item("potato_seed", 400, "Father Lawrence gives you some seeds.") // TODO proper message + } + "prayer" -> { + item(167, "Father lawrence blesses you.
You gain some Prayer xp.") + npc("in nomine saradomini, blah blah blah...") + } + "coins" -> { + item("coins_8", 400, "Father Lawrence gives you some coins.") + npc("here, hve som munny.") + } + else -> { + item(reward, 400, "Father Lawrence gives you an ${reward.toSentenceCase()}!") // TODO proper message + npc("I found this while I was mining. Hope you like it.") + } + } + } +} diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/Thessalia.kt b/game/src/main/kotlin/content/area/misthalin/varrock/Thessalia.kt index ca9d1bc591..a5cff5e8b7 100644 --- a/game/src/main/kotlin/content/area/misthalin/varrock/Thessalia.kt +++ b/game/src/main/kotlin/content/area/misthalin/varrock/Thessalia.kt @@ -78,7 +78,7 @@ class Thessalia : Script { if ((part == "arms" || part == "wrists") && previous) { return@interfaceOption } - val value = EnumDefinitions.get("look_${part}_$sex").getInt(itemSlot / 2) + val value = EnumDefinitions.get("look_${part}_$sex").int(itemSlot / 2) if (part == "top") { val current = fullBodyChest(value, male) if (previous && !current) { @@ -100,7 +100,7 @@ class Thessalia : Script { "legs" -> "makeover_colour_legs" else -> return@interfaceOption } - set(colour, EnumDefinitions.get("colour_$part").getInt(itemSlot / 2)) + set(colour, EnumDefinitions.get("colour_$part").int(itemSlot / 2)) } interfaceOption("Confirm", "thessalias_makeovers:confirm") { diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/grand_exchange/CommonItemCosts.kt b/game/src/main/kotlin/content/area/misthalin/varrock/grand_exchange/CommonItemCosts.kt index d830f10135..562c20f072 100644 --- a/game/src/main/kotlin/content/area/misthalin/varrock/grand_exchange/CommonItemCosts.kt +++ b/game/src/main/kotlin/content/area/misthalin/varrock/grand_exchange/CommonItemCosts.kt @@ -18,7 +18,7 @@ class CommonItemCosts( val enum = EnumDefinitions.get("exchange_items_$type") var index = 1 for (i in 0 until enum.length) { - val item = enum.getInt(i) + val item = enum.int(i) val definition = ItemDefinitions.get(item) val price = exchange.history.marketPrice(definition.stringId) sendScript("send_common_item_price", index, i, "${price.toDigitGroupString()} gp") diff --git a/game/src/main/kotlin/content/area/misthalin/varrock/palace/Reldo.kt b/game/src/main/kotlin/content/area/misthalin/varrock/palace/Reldo.kt index 864e6b76a6..e3190142f7 100644 --- a/game/src/main/kotlin/content/area/misthalin/varrock/palace/Reldo.kt +++ b/game/src/main/kotlin/content/area/misthalin/varrock/palace/Reldo.kt @@ -1,12 +1,17 @@ package content.area.misthalin.varrock.palace +import content.area.misthalin.draynor_village.wise_old_man.OldMansMessage +import content.entity.player.dialogue.Happy import content.entity.player.dialogue.Idle import content.entity.player.dialogue.Laugh import content.entity.player.dialogue.Quiz import content.entity.player.dialogue.Shifty import content.entity.player.dialogue.type.* import content.quest.quest +import net.pearx.kasechange.toSentenceCase import world.gregs.voidps.engine.Script +import world.gregs.voidps.engine.entity.character.player.Player +import world.gregs.voidps.engine.inv.carriesItem class Reldo : Script { @@ -14,6 +19,11 @@ class Reldo : Script { npcOperate("Talk-to", "reldo*") { npc("Hello stranger.") choice { + if (get("wise_old_man_npc", "") == "reldo" && carriesItem("old_mans_message")) { + option("The Wise Old Man of Draynor Village sent you this message.") { + wiseOldManLetter() + } + } anythingToTrade() whatDoYouDo() val stage = quest("the_knights_sword") @@ -24,6 +34,34 @@ class Reldo : Script { } } + private suspend fun Player.wiseOldManLetter() { + npc("Ah, I am always delighted to hear from him. You would not imagine the depths of his wisdom!") + val reward = OldMansMessage.rewardLetter(this) ?: return + when (reward) { + "runes" -> { + items("nature_rune", "water_rune", "Reldo gives you some runes.") + npc("My old friend Aubury sent me some runes in return for some books he wanted. Perhaps you'd like them?") + } + "herbs" -> { + item("grimy_tarromin", 400, "Reldo gives you some herbs.") // TODO proper message + } + "seeds" -> { + item("potato_seed", 400, "Reldo gives you some seeds.") + npc("These little things seem to be everywhere these days! Perhaps you'd like them?") + } + "prayer" -> { + item(167, "Reldo blesses you.
You gain some Prayer xp.") // TODO proper message + } + "coins" -> { + item("coins_8", 400, "Reldo gives you some coins.") + npc("King Roald has been very generous with my salary, so I can spare you some coins for your trouble.") + } + else -> { + item(reward, 400, "Reldo gives you an ${reward.toSentenceCase()}!") // TODO proper message + } + } + } + fun ChoiceOption.anythingToTrade() = option("Do you have anything to trade?") { npc("Only knowledge.") player("How much do you want for that then?") diff --git a/game/src/main/kotlin/content/area/wilderness/WildernessLevers.kt b/game/src/main/kotlin/content/area/wilderness/WildernessLevers.kt index 7f50ccde62..a13b5bbbfb 100644 --- a/game/src/main/kotlin/content/area/wilderness/WildernessLevers.kt +++ b/game/src/main/kotlin/content/area/wilderness/WildernessLevers.kt @@ -13,14 +13,13 @@ import world.gregs.voidps.engine.entity.character.player.chat.ChatType import world.gregs.voidps.engine.entity.character.sound import world.gregs.voidps.engine.entity.obj.GameObject import world.gregs.voidps.engine.queue.queue -import world.gregs.voidps.engine.queue.strongQueue class WildernessLevers(val teleports: ObjectTeleports) : Script { init { objTeleportTakeOff("Pull", "lever_*") { target, option -> if (target.def(this).stringId == "lever_ardougne_edgeville" && get("wilderness_lever_warning", true)) { - strongQueue("wilderness_lever_warning") { + queue("wilderness_lever_warning") { statement("Warning! Pulling the lever will teleport you deep into the Wilderness.") choice("Are you sure you wish to pull it?") { option("Yes I'm brave.") { diff --git a/game/src/main/kotlin/content/entity/death/NPCDeath.kt b/game/src/main/kotlin/content/entity/death/NPCDeath.kt index db08a814f0..eef288de08 100644 --- a/game/src/main/kotlin/content/entity/death/NPCDeath.kt +++ b/game/src/main/kotlin/content/entity/death/NPCDeath.kt @@ -102,7 +102,7 @@ class NPCDeath( is NPC -> killer.def.combat else -> -1 } - val drops = table.role(maximumRoll = if (combatLevel > 0) combatLevel * 10 else -1, player = killer as? Player) + val drops = table.roll(maximumRoll = if (combatLevel > 0) combatLevel * 10 else -1, player = killer as? Player) .filterNot { it.id == "nothing" } .reversed() .map { it.toItem() } diff --git a/game/src/main/kotlin/content/entity/npc/shop/stock/ItemInfo.kt b/game/src/main/kotlin/content/entity/npc/shop/stock/ItemInfo.kt index f377dea49a..f5b7c12f6e 100644 --- a/game/src/main/kotlin/content/entity/npc/shop/stock/ItemInfo.kt +++ b/game/src/main/kotlin/content/entity/npc/shop/stock/ItemInfo.kt @@ -38,7 +38,7 @@ object ItemInfo { private fun setRequirements(player: Player, def: ItemDefinition) { val quest = def["quest_info", -1] if (def.contains("equip_req") || def.contains("skillcape_skill") || quest != -1) { - player["item_info_requirement_title"] = EnumDefinitions.get("item_info_requirement_titles").getString(def.slot.index) + player["item_info_requirement_title"] = EnumDefinitions.get("item_info_requirement_titles").string(def.slot.index) val builder = StringBuilder() val requirements = def.getOrNull>("equip_req") ?: emptyMap() for ((skill, level) in requirements) { @@ -57,7 +57,7 @@ object ItemInfo { } player["item_info_requirement"] = builder.toString() } else { - player["item_info_requirement_title"] = EnumDefinitions.get("item_info_titles").getString(def.slot.index) + player["item_info_requirement_title"] = EnumDefinitions.get("item_info_titles").string(def.slot.index) player["item_info_requirement"] = "" } } diff --git a/game/src/main/kotlin/content/entity/npc/shop/stock/Price.kt b/game/src/main/kotlin/content/entity/npc/shop/stock/Price.kt index b93c7254ad..18a969093d 100644 --- a/game/src/main/kotlin/content/entity/npc/shop/stock/Price.kt +++ b/game/src/main/kotlin/content/entity/npc/shop/stock/Price.kt @@ -18,11 +18,11 @@ object Price { fun getPrice(player: Player, item: String, index: Int, amount: Int): Int { val itemId = getRealItem(item) - var price = EnumDefinitions.get("price_runes").getInt(itemId) + var price = EnumDefinitions.get("price_runes").int(itemId) if (player["shop_currency", "coins"] == "tokkul" && price != -1 && price > 0) { return price } - price = EnumDefinitions.get("price_garden").getInt(itemId) + price = EnumDefinitions.get("price_garden").int(itemId) if (price != -1 && price > 0) { return price } @@ -57,11 +57,11 @@ object Price { val itemId = getRealItem(item) val koin = KoinPlatformTools.defaultContext().getOrNull() if (koin != null) { - var price = EnumDefinitions.get("price_runes").getInt(itemId) + var price = EnumDefinitions.get("price_runes").int(itemId) if (currency == "tokkul" && price != -1 && price > 0) { return price } - price = EnumDefinitions.get("price_garden").getInt(itemId) + price = EnumDefinitions.get("price_garden").int(itemId) if (price != -1 && price > 0) { return price } diff --git a/game/src/main/kotlin/content/entity/player/command/DropCommands.kt b/game/src/main/kotlin/content/entity/player/command/DropCommands.kt index 5bee38ae3f..9b3d8b5380 100644 --- a/game/src/main/kotlin/content/entity/player/command/DropCommands.kt +++ b/game/src/main/kotlin/content/entity/player/command/DropCommands.kt @@ -79,7 +79,7 @@ class DropCommands(val tables: DropTables) : Script { val temp = Inventory.debug(capacity = 100) val list = InventoryDelegate(temp) for (i in numbers) { - table.role(list = list, player = player) + table.roll(list = list, player = player) } temp } diff --git a/game/src/main/kotlin/content/entity/player/dialogue/type/ItemBox.kt b/game/src/main/kotlin/content/entity/player/dialogue/type/ItemBox.kt index 7c159d07d1..a6fb43f2a8 100644 --- a/game/src/main/kotlin/content/entity/player/dialogue/type/ItemBox.kt +++ b/game/src/main/kotlin/content/entity/player/dialogue/type/ItemBox.kt @@ -24,6 +24,15 @@ suspend fun Player.item(item: String, zoom: Int, text: String, sprite: Int? = nu close(ITEM_INTERFACE_ID) } +suspend fun Player.item(sprite: Int, text: String) { + check(open(ITEM_INTERFACE_ID)) { "Unable to open item dialogue for $this" } + interfaces.sendSprite(ITEM_INTERFACE_ID, "sprite", sprite) + val lines = if (text.contains("\n")) text.trimIndent().replace("\n", "
") else get().get("q8_full").splitLines(text, 380).joinToString("
") + interfaces.sendText(ITEM_INTERFACE_ID, "line1", lines) + ContinueSuspension.get(this) + close(ITEM_INTERFACE_ID) +} + suspend fun Player.items(item1: String, item2: String, text: String) { check(open(DOUBLE_ITEM_INTERFACE_ID)) { "Unable to open item dialogue for $this" } interfaces.sendItem(DOUBLE_ITEM_INTERFACE_ID, "model1", ItemDefinitions.get(item1).id) diff --git a/game/src/main/kotlin/content/entity/player/inv/item/ItemExtensions.kt b/game/src/main/kotlin/content/entity/player/inv/item/ItemExtensions.kt index f66b499fb9..e6aa62a0b1 100644 --- a/game/src/main/kotlin/content/entity/player/inv/item/ItemExtensions.kt +++ b/game/src/main/kotlin/content/entity/player/inv/item/ItemExtensions.kt @@ -10,8 +10,10 @@ import world.gregs.voidps.engine.inv.inventory val Item.tradeable: Boolean get() = def["tradeable", true] -fun Player.addOrDrop(id: String, amount: Int = 1, inventory: Inventory = this.inventory, revealTicks: Int = 100, disappearTicks: Int = 200) { +fun Player.addOrDrop(id: String, amount: Int = 1, inventory: Inventory = this.inventory, revealTicks: Int = 100, disappearTicks: Int = 200): Boolean { if (!inventory.add(id, amount)) { FloorItems.add(tile, id, amount, revealTicks = revealTicks, disappearTicks = disappearTicks, owner = this) + return false } + return true } diff --git a/game/src/main/kotlin/content/entity/player/modal/CharacterCreation.kt b/game/src/main/kotlin/content/entity/player/modal/CharacterCreation.kt index 0097d0aa56..83797be689 100644 --- a/game/src/main/kotlin/content/entity/player/modal/CharacterCreation.kt +++ b/game/src/main/kotlin/content/entity/player/modal/CharacterCreation.kt @@ -44,7 +44,7 @@ class CharacterCreation : Script { } interfaceOption(id = "character_creation:skin_colour") { (_, itemSlot) -> - set("makeover_colour_skin", EnumDefinitions.get("character_skin").getInt(itemSlot)) + set("makeover_colour_skin", EnumDefinitions.get("character_skin").int(itemSlot)) } interfaceOption(id = "character_creation:style_*") { @@ -68,7 +68,7 @@ class CharacterCreation : Script { if (part == "beard") { part = "hair" } - set("makeover_colour_$part", EnumDefinitions.get("character_$part").getInt(itemSlot)) + set("makeover_colour_$part", EnumDefinitions.get("character_$part").int(itemSlot)) } interfaceOption("Choose My Colour", "character_creation:choose_colour") { @@ -83,7 +83,7 @@ class CharacterCreation : Script { val value = if (part == "hair") { EnumDefinitions.getStruct("character_${part}_styles_$sex", itemSlot, "body_look_id") } else { - EnumDefinitions.get("character_${part}_styles_$sex").getInt(itemSlot) + EnumDefinitions.get("character_${part}_styles_$sex").int(itemSlot) } if (part == "top") { onStyle(value) { @@ -182,7 +182,7 @@ class CharacterCreation : Script { player["character_creation_female"] = female val hairStyle = player["character_creation_hair_style", 0] val hair: Int = EnumDefinitions.getStruct("character_hair_styles_${if (female) "female" else "male"}", hairStyle, "body_look_id") - val beard: Int = if (female) -1 else EnumDefinitions.get("character_beard_styles_male").getInt(hairStyle / 2) + val beard: Int = if (female) -1 else EnumDefinitions.get("character_beard_styles_male").int(hairStyle / 2) player["makeover_hair"] = hair player["makeover_beard"] = beard player["character_creation_sub_style"] = 1 diff --git a/game/src/main/kotlin/content/entity/world/music/Music.kt b/game/src/main/kotlin/content/entity/world/music/Music.kt index a0c574e64c..b97148a681 100644 --- a/game/src/main/kotlin/content/entity/world/music/Music.kt +++ b/game/src/main/kotlin/content/entity/world/music/Music.kt @@ -215,14 +215,14 @@ class Music(val tracks: MusicTracks) : Script { } fun Player.hasUnlocked(musicIndex: Int): Boolean { - val name = EnumDefinitions.get("music_track_names").getString(musicIndex) + val name = EnumDefinitions.get("music_track_names").string(musicIndex) return containsVarbit("unlocked_music_${musicIndex / 32}", toIdentifier(name)) } fun autoPlay(player: Player, track: MusicTracks.Track) { val index = track.index if (player.addVarbit("unlocked_music_${index / 32}", track.name)) { - player.message("You have unlocked a new music track: ${EnumDefinitions.get("music_track_names").getString(index)}.") + player.message("You have unlocked a new music track: ${EnumDefinitions.get("music_track_names").string(index)}.") } if (!player["playing_song", false]) { player.playTrack(index) diff --git a/game/src/main/kotlin/content/minigame/sorceress_garden/SorceressGarden.kt b/game/src/main/kotlin/content/minigame/sorceress_garden/SorceressGarden.kt index a884e37dcc..20bc91155a 100644 --- a/game/src/main/kotlin/content/minigame/sorceress_garden/SorceressGarden.kt +++ b/game/src/main/kotlin/content/minigame/sorceress_garden/SorceressGarden.kt @@ -106,8 +106,8 @@ class SorceressGarden(val dropTables: DropTables) : Script { val table = dropTables.get("${type}_herbs_drop_table") if (table != null) { val drops = mutableListOf() - table.role(list = drops) - table.role(list = drops) + table.roll(list = drops) + table.roll(list = drops) inventory.transaction { for (drop in drops) { add(drop.id, 1) diff --git a/game/src/main/kotlin/content/quest/Quest.kt b/game/src/main/kotlin/content/quest/Quest.kt index 7a1c8c6278..2b957e95e0 100644 --- a/game/src/main/kotlin/content/quest/Quest.kt +++ b/game/src/main/kotlin/content/quest/Quest.kt @@ -77,13 +77,12 @@ fun Player.letterScroll(name: String, lines: List) { } } -fun Player.wiseOldManScroll(name: String, lines: List) { +fun Player.wiseOldManScroll(lines: List) { if (!interfaces.open("wise_old_man_scroll")) { return } - interfaces.sendText("wise_old_man_scroll", "title", name) for (i in 0..16) { - interfaces.sendText("wise_old_man_scroll", "line${i + 1}", lines.getOrNull(i) ?: "") + interfaces.sendText("wise_old_man_scroll", "line$i", lines.getOrNull(i) ?: "") } } diff --git a/game/src/main/kotlin/content/skill/farming/Farmer.kt b/game/src/main/kotlin/content/skill/farming/Farmer.kt index 0fd5ed8b0b..d54656e44d 100644 --- a/game/src/main/kotlin/content/skill/farming/Farmer.kt +++ b/game/src/main/kotlin/content/skill/farming/Farmer.kt @@ -169,7 +169,7 @@ class Farmer : Script { } val def = ObjectDefinitions.get("${value.substringBeforeLast("_")}_fullygrown") val item: String = def.getOrNull("harvest") ?: return - val harvest = EnumDefinitions.get("farming_protection").getString(ItemDefinitions.get(item).id).substringAfter(":") + val harvest = EnumDefinitions.get("farming_protection").string(ItemDefinitions.get(item).id).substringAfter(":") npc("If you like, but I want $harvest for that.") val (required, noted) = requiredItems(item) if (!inventory.remove(required) && (noted.isEmpty() || !inventory.remove(noted))) { diff --git a/game/src/main/kotlin/content/skill/summoning/ShardSwapping.kt b/game/src/main/kotlin/content/skill/summoning/ShardSwapping.kt index 137f8e0e9d..52a3d89759 100644 --- a/game/src/main/kotlin/content/skill/summoning/ShardSwapping.kt +++ b/game/src/main/kotlin/content/skill/summoning/ShardSwapping.kt @@ -39,7 +39,7 @@ class ShardSwapping : Script { val itemType = id.substringAfter(":").removeSuffix("_trade_in") if (item.id.endsWith("_u")) { - val actualItemId = EnumDefinitions.get("summoning_${itemType}_ids_1").getInt(enumIndex) + val actualItemId = EnumDefinitions.get("summoning_${itemType}_ids_1").int(enumIndex) actualItem = Item(ItemDefinitions.get(actualItemId).stringId) } @@ -52,7 +52,7 @@ class ShardSwapping : Script { val itemType = id.substringAfter(":").removeSuffix("_trade_in") if (item.id.endsWith("_u")) { - val actualItemId = EnumDefinitions.get("summoning_${itemType}_ids_1").getInt(enumIndex) + val actualItemId = EnumDefinitions.get("summoning_${itemType}_ids_1").int(enumIndex) actualItem = Item(ItemDefinitions.get(actualItemId).stringId) sendValueMessage(this, actualItem, itemType) return@interfaceOption diff --git a/game/src/main/kotlin/content/skill/summoning/Summoning.kt b/game/src/main/kotlin/content/skill/summoning/Summoning.kt index 53b89180d9..1344a44c04 100644 --- a/game/src/main/kotlin/content/skill/summoning/Summoning.kt +++ b/game/src/main/kotlin/content/skill/summoning/Summoning.kt @@ -173,8 +173,8 @@ class Summoning : Script { init { itemOption("Summon", "*_pouch") { option -> - val familiarLevel = EnumDefinitions.get("summoning_pouch_levels").getInt(option.item.def.id) - val familiarId = EnumDefinitions.get("summoning_familiar_ids").getInt(option.item.def.id) + val familiarLevel = EnumDefinitions.get("summoning_pouch_levels").int(option.item.def.id) + val familiarId = EnumDefinitions.get("summoning_familiar_ids").int(option.item.def.id) val summoningXp = option.item.def["summon_experience", 0.0] val familiar = NPCDefinitions.get(familiarId) if (!has(Skill.Summoning, familiarLevel)) { diff --git a/game/src/main/kotlin/content/skill/summoning/SummoningCrafting.kt b/game/src/main/kotlin/content/skill/summoning/SummoningCrafting.kt index dac0f72ad1..f30898f04d 100644 --- a/game/src/main/kotlin/content/skill/summoning/SummoningCrafting.kt +++ b/game/src/main/kotlin/content/skill/summoning/SummoningCrafting.kt @@ -103,7 +103,7 @@ class SummoningCrafting : Script { * @param amount: The amount of pouches the player is attempting to craft */ fun infusePouches(player: Player, enumIndex: Int, amount: Int) { - val pouchItemId = EnumDefinitions.get("summoning_pouch_ids_1").getInt(enumIndex) + val pouchItemId = EnumDefinitions.get("summoning_pouch_ids_1").int(enumIndex) val pouchItem = Item(ItemDefinitions.get(pouchItemId).stringId) val shards = getShards(pouchItem) @@ -138,7 +138,7 @@ class SummoningCrafting : Script { * @param amount: The amount of pouches the player is attempting to turn into scrolls */ fun transformScrolls(player: Player, enumIndex: Int, amount: Int) { - val scrollItemId = EnumDefinitions.get("summoning_scroll_ids_1").getInt(enumIndex) + val scrollItemId = EnumDefinitions.get("summoning_scroll_ids_1").int(enumIndex) val scrollItem = Item(ItemDefinitions.get(scrollItemId).stringId) val pouchId = EnumDefinitions.get("summoning_scroll_ids_2").getKey(scrollItemId) @@ -262,8 +262,8 @@ class SummoningCrafting : Script { * @param enumIndex: The index of the clicked pouch in the "summoning_pouch_ids_1" enum. */ fun sendIngredientMessage(player: Player, enumIndex: Int) { - val realPouchId = EnumDefinitions.get("summoning_pouch_ids_1").getInt(enumIndex) - val ingredientString = EnumDefinitions.get("summoning_pouch_crafting_ingredient_strings").getString(realPouchId) + val realPouchId = EnumDefinitions.get("summoning_pouch_ids_1").int(enumIndex) + val ingredientString = EnumDefinitions.get("summoning_pouch_crafting_ingredient_strings").string(realPouchId) player.message(ingredientString) } diff --git a/game/src/main/kotlin/content/skill/thieving/Pickpocketing.kt b/game/src/main/kotlin/content/skill/thieving/Pickpocketing.kt index d32876748e..60bd35e197 100644 --- a/game/src/main/kotlin/content/skill/thieving/Pickpocketing.kt +++ b/game/src/main/kotlin/content/skill/thieving/Pickpocketing.kt @@ -87,16 +87,16 @@ class Pickpocketing(val combatDefinitions: CombatDefinitions, val dropTables: Dr fun getLoot(target: NPC, table: String?): List? { var id = dropTables.get("${table}_pickpocket") if (id != null) { - return id.role() + return id.roll() } id = dropTables.get("${target.id}_pickpocket") if (id != null) { - return id.role() + return id.roll() } for (category in target.categories) { id = dropTables.get("${category}_pickpocket") if (id != null) { - return id.role() + return id.roll() } } return null diff --git a/game/src/main/kotlin/content/skill/thieving/Stalls.kt b/game/src/main/kotlin/content/skill/thieving/Stalls.kt index 982a4ad729..a74d15befb 100644 --- a/game/src/main/kotlin/content/skill/thieving/Stalls.kt +++ b/game/src/main/kotlin/content/skill/thieving/Stalls.kt @@ -60,7 +60,7 @@ class Stalls(val drops: DropTables) : Script { } val table = drops.get("${target.id}_drop_table") if (table != null) { - val drops = table.role().map { it.toItem() } + val drops = table.roll().map { it.toItem() } inventory.transaction { for (item in drops) { add(item.id, item.amount) diff --git a/game/src/main/kotlin/content/skill/thieving/TrapChests.kt b/game/src/main/kotlin/content/skill/thieving/TrapChests.kt index 2bc1f99f3a..7c4cb10247 100644 --- a/game/src/main/kotlin/content/skill/thieving/TrapChests.kt +++ b/game/src/main/kotlin/content/skill/thieving/TrapChests.kt @@ -70,7 +70,7 @@ class TrapChests(val tables: DropTables) : Script { delay(1) val table = tables.get("${target.id}_drop_table") if (table != null) { - val drops = table.role().map { it.toItem() } + val drops = table.roll().map { it.toItem() } inventory.transaction { for (item in drops) { add(item.id, item.amount) diff --git a/game/src/main/kotlin/content/skill/woodcutting/BirdsNest.kt b/game/src/main/kotlin/content/skill/woodcutting/BirdsNest.kt index b97bce78b6..04d4354677 100644 --- a/game/src/main/kotlin/content/skill/woodcutting/BirdsNest.kt +++ b/game/src/main/kotlin/content/skill/woodcutting/BirdsNest.kt @@ -26,7 +26,7 @@ class BirdsNest(val drops: DropTables) : Script { val table = drops.get(tableId) ?: return@itemOption val items = mutableListOf() - table.role(list = items) + table.roll(list = items) val drop = items.firstOrNull() ?: return@itemOption val itemId = drop.id diff --git a/game/src/main/kotlin/content/skill/woodcutting/Woodcutting.kt b/game/src/main/kotlin/content/skill/woodcutting/Woodcutting.kt index 06bdde308a..860e4bdf71 100644 --- a/game/src/main/kotlin/content/skill/woodcutting/Woodcutting.kt +++ b/game/src/main/kotlin/content/skill/woodcutting/Woodcutting.kt @@ -105,7 +105,7 @@ class Woodcutting(val drops: DropTables) : Script { val hasRabbitFoot = player.equipment.contains("strung_rabbit_foot") val totalWeight = if (hasRabbitFoot) 95 else 100 - val drop = table.role(totalWeight).firstOrNull() ?: return + val drop = table.roll(totalWeight).firstOrNull() ?: return val source = if (ivy) "ivy" else "tree" player.message("A bird's nest falls out of the $source!") diff --git a/game/src/main/kotlin/content/social/trade/exchange/GrandExchangeItemSets.kt b/game/src/main/kotlin/content/social/trade/exchange/GrandExchangeItemSets.kt index 70a9a31b40..dbfc0acf2e 100644 --- a/game/src/main/kotlin/content/social/trade/exchange/GrandExchangeItemSets.kt +++ b/game/src/main/kotlin/content/social/trade/exchange/GrandExchangeItemSets.kt @@ -35,7 +35,7 @@ class GrandExchangeItemSets : Script { interfaceOption("Components", "exchange_item_sets:sets") { (item) -> val descriptions = EnumDefinitions.get("exchange_set_descriptions") - message(descriptions.getString(item.def.id)) + message(descriptions.string(item.def.id)) } interfaceOption("Exchange", "exchange_item_sets:sets") { (item) -> @@ -73,7 +73,7 @@ class GrandExchangeItemSets : Script { interfaceOption("Components", "exchange_sets_side:items") { (item) -> val descriptions = EnumDefinitions.get("exchange_set_descriptions") - val text = descriptions.getString(item.def.id) + val text = descriptions.string(item.def.id) if (text != "shop_dummy") { message(text) } else { diff --git a/game/src/test/kotlin/WorldTest.kt b/game/src/test/kotlin/WorldTest.kt index c40b576641..49688b3373 100644 --- a/game/src/test/kotlin/WorldTest.kt +++ b/game/src/test/kotlin/WorldTest.kt @@ -354,10 +354,17 @@ abstract class WorldTest : KoinTest { private val weaponStyleDefinitions: WeaponStyleDefinitions by lazy { WeaponStyleDefinitions().load(configFiles.find(Settings["definitions.weapons.styles"])) } private val weaponAnimationDefinitions: WeaponAnimationDefinitions by lazy { WeaponAnimationDefinitions().load(configFiles.find(Settings["definitions.weapons.animations"])) } private val enumDefinitions: Array by lazy { - EnumDecoder().load(cache) + itemIds + interfaceIds + inventoryIds + npcIds + structIds + EnumDefinitions.init(EnumDecoder().load(cache)).load(configFiles.list(Settings["definitions.enums"])) + EnumDefinitions.definitions } + private val enumIds: Map by lazy { - EnumDefinitions.init(EnumDecoder().load(cache)).load(configFiles.find(Settings["definitions.enums"])) + enumDefinitions EnumDefinitions.ids } private val objectCollisionAdd: GameObjectCollisionAdd by lazy { GameObjectCollisionAdd() } diff --git a/game/src/test/kotlin/content/area/misthalin/draynor_village/wise_old_man/WiseOldManTest.kt b/game/src/test/kotlin/content/area/misthalin/draynor_village/wise_old_man/WiseOldManTest.kt new file mode 100644 index 0000000000..1fd9627b43 --- /dev/null +++ b/game/src/test/kotlin/content/area/misthalin/draynor_village/wise_old_man/WiseOldManTest.kt @@ -0,0 +1,73 @@ +package content.area.misthalin.draynor_village.wise_old_man + +import FakeRandom +import WorldTest +import dialogueContinue +import dialogueOption +import npcOption +import org.junit.jupiter.api.Test +import world.gregs.voidps.engine.client.ui.closeDialogue +import world.gregs.voidps.engine.entity.character.player.skill.Skill +import world.gregs.voidps.engine.inv.add +import world.gregs.voidps.engine.inv.inventory +import world.gregs.voidps.type.Tile +import world.gregs.voidps.type.setRandom +import kotlin.test.assertEquals + +class WiseOldManTest : WorldTest() { + + @Test + fun `Complete letter task`() { + setRandom(object : FakeRandom() { + override fun nextInt(until: Int) = if (until == 16) 12 else 0 + override fun nextInt(from: Int, until: Int) = from + }) + val player = createPlayer(Tile(3088, 3254)) + player.levels.set(Skill.Prayer, 3) + player["wise_old_man_met"] = true + val wom = createNPC("wise_old_man_draynor", Tile(3088, 3255)) + player.npcOption(wom, "Talk-to") + tick() + player.dialogueContinue() + player.dialogueOption("line1") + player.dialogueContinue(4) + assertEquals("father_aereck", player["wise_old_man_npc", ""]) + assertEquals(1, player.inventory.count("old_mans_message")) + val father = createNPC("father_aereck", Tile(3088, 3255)) + player.npcOption(father, "Talk-to") + tick() + player.dialogueContinue(4) + assertEquals("", player["wise_old_man_npc", ""]) + assertEquals(1, player["wise_old_man_letters_completed", 0]) + assertEquals(0, player.inventory.count("old_mans_message")) + assertEquals(215.0, player.experience.get(Skill.Prayer)) + } + + @Test + fun `Complete basic task`() { + setRandom(object : FakeRandom() { + override fun nextInt(until: Int) = if (until == 100) 20 else 0 + override fun nextInt(from: Int, until: Int) = from + }) + val player = createPlayer(Tile(3088, 3254)) + player["wise_old_man_met"] = true + val wom = createNPC("wise_old_man_draynor", Tile(3088, 3255)) + player.npcOption(wom, "Talk-to") + tick() + player.dialogueContinue() + player.dialogueOption("line1") + player.dialogueContinue(3) + player.closeDialogue() + assertEquals("beer_glass", player["wise_old_man_task", ""]) + assertEquals(3, player["wise_old_man_remaining", 0]) + player.inventory.add("beer_glass", 3) + player.npcOption(wom, "Talk-to") + tick() + player.dialogueContinue(3) + assertEquals(0, player.inventory.count("beer_glass")) + assertEquals(1, player.inventory.count("uncut_red_topaz")) + assertEquals("", player["wise_old_man_task", ""]) + assertEquals(0, player["wise_old_man_remaining", 0]) + assertEquals(1, player["wise_old_man_tasks_completed", 0]) + } +} diff --git a/tools/src/main/kotlin/world/gregs/voidps/tools/EnumDefinitions.kt b/tools/src/main/kotlin/world/gregs/voidps/tools/EnumDefinitions.kt index 1810dd7bf8..1a2fe95320 100644 --- a/tools/src/main/kotlin/world/gregs/voidps/tools/EnumDefinitions.kt +++ b/tools/src/main/kotlin/world/gregs/voidps/tools/EnumDefinitions.kt @@ -2,8 +2,20 @@ package world.gregs.voidps.tools import world.gregs.voidps.cache.Cache import world.gregs.voidps.cache.CacheDelegate +import world.gregs.voidps.cache.config.decoder.InventoryDecoder +import world.gregs.voidps.cache.config.decoder.StructDecoder import world.gregs.voidps.cache.definition.decoder.EnumDecoder +import world.gregs.voidps.cache.definition.decoder.InterfaceDecoder +import world.gregs.voidps.cache.definition.decoder.ItemDecoder +import world.gregs.voidps.cache.definition.decoder.NPCDecoder import world.gregs.voidps.engine.data.Settings +import world.gregs.voidps.engine.data.configFiles +import world.gregs.voidps.engine.data.definition.EnumDefinitions +import world.gregs.voidps.engine.data.definition.InterfaceDefinitions +import world.gregs.voidps.engine.data.definition.InventoryDefinitions +import world.gregs.voidps.engine.data.definition.ItemDefinitions +import world.gregs.voidps.engine.data.definition.NPCDefinitions +import world.gregs.voidps.engine.data.definition.StructDefinitions object EnumDefinitions { @@ -11,9 +23,15 @@ object EnumDefinitions { fun main(args: Array) { Settings.load() val cache: Cache = CacheDelegate(Settings["storage.cache.path"]) - val decoder = EnumDecoder().load(cache) - for (i in decoder.indices) { - val def = decoder.getOrNull(i) ?: continue + val files = configFiles() + ItemDefinitions.init(ItemDecoder().load(cache)).load(files.list(Settings["definitions.items"])) + InterfaceDefinitions.init(InterfaceDecoder().load(cache)).load(files.list(Settings["definitions.interfaces"]), files.find(Settings["definitions.interfaces.types"])) + InventoryDefinitions.init(InventoryDecoder().load(cache)).load(files.list(Settings["definitions.inventories"]), files.list(Settings["definitions.shops"])) + NPCDefinitions.init(NPCDecoder().load(cache)).load(files.list(Settings["definitions.npcs"])) + StructDefinitions.init(StructDecoder().load(cache)).load(files.find(Settings["definitions.structs"])) + val definitions = EnumDefinitions.init(EnumDecoder().load(cache)).load(files.list(Settings["definitions.enums"])) + for (i in definitions.definitions.indices) { + val def = definitions.getOrNull(i) ?: continue println("$i $def") } }