From d069269884f5db32a410ec32445f6ff68abc0d27 Mon Sep 17 00:00:00 2001 From: Bishop Date: Sun, 21 Jun 2026 20:07:04 -0500 Subject: [PATCH] Implemented easels --- .../global/skill/construction/CrestType.kt | 1 + .../decoration/workshop/EaselListener.kt | 139 ++++++++++++++++++ .../global/skill/crafting/HeraldicProduct.kt | 31 ++++ .../falador/dialogue/SirReniteeDialogue.kt | 92 ++++++------ 4 files changed, 217 insertions(+), 46 deletions(-) create mode 100644 Server/src/main/content/global/skill/construction/decoration/workshop/EaselListener.kt create mode 100644 Server/src/main/content/global/skill/crafting/HeraldicProduct.kt diff --git a/Server/src/main/content/global/skill/construction/CrestType.kt b/Server/src/main/content/global/skill/construction/CrestType.kt index 9780c893b..d296bb8b3 100644 --- a/Server/src/main/content/global/skill/construction/CrestType.kt +++ b/Server/src/main/content/global/skill/construction/CrestType.kt @@ -8,6 +8,7 @@ import org.rs09.consts.Items /** * Enumerates the various crests the player can purchase for the Construction skill, as well as their requirements. + * ORDINAL BOUND, see HeraldicProduct.kt * @author Bishop */ diff --git a/Server/src/main/content/global/skill/construction/decoration/workshop/EaselListener.kt b/Server/src/main/content/global/skill/construction/decoration/workshop/EaselListener.kt new file mode 100644 index 000000000..3c442a659 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/workshop/EaselListener.kt @@ -0,0 +1,139 @@ +package content.global.skill.construction.decoration.workshop + +import content.global.skill.crafting.HeraldicProduct +import core.api.* +import core.game.interaction.InteractionListener +import core.game.node.entity.skill.Skills +import org.rs09.consts.Scenery + +/** + * Listens for use of the easel furniture pieces in a POH, with which players may make heraldic items. + * @author Bishop + */ + +class EaselListener : InteractionListener { + + private val easels = intArrayOf(Scenery.HELMET_PLUMING_STAND_13716, Scenery.PAINTING_STAND_13717, Scenery.BANNER_MAKING_STAND_13718) + private val heraldryAnimation = 0 // PLACEHOLDER + + override fun defineListeners() { + on(easels, SCENERY, "use") { player, node -> + val crest = player.houseManager.crest + val options = when (node.id) { + Scenery.HELMET_PLUMING_STAND_13716 -> + arrayOf("Plumed steel helmet", "Plumed rune helmet") + Scenery.PAINTING_STAND_13717 -> + arrayOf("Plumed steel helmet", "Plumed rune helmet", "Steel heraldic shield", "Runite heraldic shield") + Scenery.BANNER_MAKING_STAND_13718 -> + arrayOf("Plumed steel helmet", "Plumed rune helmet", "Steel heraldic shield", "Runite heraldic shield", "Heraldic banner") + else -> + return@on false + } + sendDialogueOptions(player, "What do you want to make?", *options) + addDialogueAction(player) { player, button -> + when (button) { + 2,3 -> { + when { + (!hasLevelDyn(player, Skills.CONSTRUCTION, HeraldicProduct.STEEL_HELM.constructionReq)) -> { + sendDialogue(player, "You need a Construction level of ${HeraldicProduct.STEEL_HELM.constructionReq} to make this.") + return@addDialogueAction + } + (!hasLevelDyn(player, Skills.CRAFTING, HeraldicProduct.STEEL_HELM.craftingReq)) -> { + sendDialogue(player, "You need a Crafting level of ${HeraldicProduct.STEEL_HELM.craftingReq} to make this.") + return@addDialogueAction + } + (button == 2) -> { + if (removeItem(player, HeraldicProduct.STEEL_HELM.primaryMaterialId)) { + addItemOrDrop(player, HeraldicProduct.STEEL_HELM.productForCrest(crest)) + rewardXP(player, Skills.CRAFTING, HeraldicProduct.STEEL_HELM.craftingXp) + animate(player, heraldryAnimation) + sendMessage(player, "You make a helmet in your colours.") + return@addDialogueAction + } else { + sendDialogue(player, "PLACEHOLDER") + return@addDialogueAction + } + } + else -> { + if (removeItem(player, HeraldicProduct.RUNE_HELM.primaryMaterialId)) { + addItemOrDrop(player, HeraldicProduct.RUNE_HELM.productForCrest(crest)) + rewardXP(player, Skills.CRAFTING, HeraldicProduct.RUNE_HELM.craftingXp) + animate(player, heraldryAnimation) + sendMessage(player, "You make a helmet in your colours.") + return@addDialogueAction + } else { + sendDialogue(player, "PLACEHOLDER") + return@addDialogueAction + } + } + } + } + 4,5 -> { + when { + (!hasLevelDyn(player, Skills.CONSTRUCTION, HeraldicProduct.STEEL_SHIELD.constructionReq)) -> { + sendDialogue(player, "You need a Construction level of ${HeraldicProduct.STEEL_SHIELD.constructionReq} to make this.") + return@addDialogueAction + } + (!hasLevelDyn(player, Skills.CRAFTING, HeraldicProduct.STEEL_SHIELD.craftingReq)) -> { + sendDialogue(player, "You need a Crafting level of ${HeraldicProduct.STEEL_SHIELD.craftingReq} to make this.") + return@addDialogueAction + } + (button == 4) -> { + if (removeItem(player, HeraldicProduct.STEEL_SHIELD.primaryMaterialId)) { + addItemOrDrop(player, HeraldicProduct.STEEL_SHIELD.productForCrest(crest)) + rewardXP(player, Skills.CRAFTING, HeraldicProduct.STEEL_SHIELD.craftingXp) + animate(player, heraldryAnimation) + sendMessage(player, "You make a shield with your symbol on.") + return@addDialogueAction + } else { + sendDialogue(player, "PLACEHOLDER") + return@addDialogueAction + } + } + else -> { + if (removeItem(player, HeraldicProduct.RUNE_SHIELD.primaryMaterialId)) { + addItemOrDrop(player, HeraldicProduct.RUNE_SHIELD.productForCrest(crest)) + rewardXP(player, Skills.CRAFTING, HeraldicProduct.RUNE_SHIELD.craftingXp) + animate(player, heraldryAnimation) + sendMessage(player, "You make a shield with your symbol on.") + return@addDialogueAction + } else { + sendDialogue(player, "PLACEHOLDER") + return@addDialogueAction + } + } + } + } + 6 -> { + when { + (!hasLevelDyn(player, Skills.CONSTRUCTION, HeraldicProduct.BANNER.constructionReq)) -> { + sendDialogue(player, "You need a Construction level of ${HeraldicProduct.BANNER.constructionReq} to make this.") + return@addDialogueAction + } + (!hasLevelDyn(player, Skills.CRAFTING, HeraldicProduct.BANNER.craftingReq)) -> { + sendDialogue(player, "You need a Crafting level of ${HeraldicProduct.BANNER.craftingReq} to make this.") + return@addDialogueAction + } + (!inInventory(player, HeraldicProduct.BANNER.primaryMaterialId) || !inInventory(player, HeraldicProduct.BANNER.secondaryMaterialId!!)) -> { + sendDialogue(player, "PLACEHOLDER") + } + else -> { + if (removeItem(player, HeraldicProduct.BANNER.primaryMaterialId) && removeItem(player, HeraldicProduct.BANNER.secondaryMaterialId)) { + addItemOrDrop(player, HeraldicProduct.BANNER.productForCrest(crest)) + rewardXP(player, Skills.CRAFTING, HeraldicProduct.BANNER.craftingXp) + animate(player, heraldryAnimation) + sendMessage(player, "PLACEHOLDER") + return@addDialogueAction + } else { + sendDialogue(player, "PLACEHOLDER") + return@addDialogueAction + } + } + } + } + } + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/HeraldicProduct.kt b/Server/src/main/content/global/skill/crafting/HeraldicProduct.kt new file mode 100644 index 000000000..ddb58dec5 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/HeraldicProduct.kt @@ -0,0 +1,31 @@ +package content.global.skill.crafting + +import content.global.skill.construction.CrestType +import org.rs09.consts.Items + +/** + * Enumerates the heraldic items players can craft with easels in their POHs. + * @author Bishop + */ + +enum class HeraldicProduct( + val primaryMaterialId: Int, + val secondaryMaterialId: Int?, + val baseProductId: Int, + val craftingReq: Int, + val craftingXp: Double, + val constructionReq: Int = 16) { + + STEEL_HELM(Items.STEEL_FULL_HELM_1157, null, + Items.STEEL_HERALDIC_HELM_8682, 38, 37.0), + RUNE_HELM(Items.RUNE_FULL_HELM_1163, null, + Items.RUNE_HERALDIC_HELM_8464, STEEL_HELM.craftingReq, STEEL_HELM.craftingXp), + STEEL_SHIELD(Items.STEEL_KITESHIELD_1193, null, + Items.STEEL_KITESHIELD_8746, 43, 40.0), + RUNE_SHIELD(Items.RUNE_KITESHIELD_1201, null, + Items.RUNE_KITESHIELD_8714, STEEL_SHIELD.craftingReq, STEEL_SHIELD.craftingXp), + BANNER(Items.BOLT_OF_CLOTH_8790, Items.PLANK_960, + Items.BANNER_8650, 48, 42.5); + + fun productForCrest(crest: CrestType): Int = baseProductId + (crest.ordinal * 2) +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.kt index 3347dcca6..f0ec194a9 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.kt @@ -62,33 +62,33 @@ class SirReniteeDialogueFile : DialogueLabeller() { * GENERIC DIALOGUE */ - npc(ChatAnim.HALF_GUILTY, "Hmm? What's that, young ${if (player!!.isMale) "man" else "woman"}? What can I do for", "you?") + npc(ChatAnim.NEUTRAL, "Hmm? What's that, young ${if (player!!.isMale) "man" else "woman"}? What can I do for", "you?") options( - DialogueOption("intro", "I don't know, what can you do for me?", expression = ChatAnim.HALF_GUILTY), - DialogueOption("nothing", "Nothing, thanks", spokenText = "Nothing, thanks.", expression = ChatAnim.HALF_GUILTY), + DialogueOption("intro", "I don't know, what can you do for me?", expression = ChatAnim.ASKING), + DialogueOption("nothing", "Nothing, thanks", spokenText = "Nothing, thanks.", expression = ChatAnim.NEUTRAL), ) label("nothing") - npc(ChatAnim.HALF_GUILTY, "Mmm, well, see you some other time maybe.") + npc(ChatAnim.NEUTRAL, "Mmm, well, see you some other time maybe.") /** * CREST DIALOGUE */ label("intro") - npc(ChatAnim.HALF_GUILTY, "Hmm, well, mmm, do you have a family crest? I keep", + npc(ChatAnim.NEUTRAL, "Hmm, well, mmm, do you have a family crest? I keep", "track of every ${ServerConstants.SERVER_NAME} family, you know, so I might", "be able to find yours.") - npc(ChatAnim.HALF_GUILTY, "I'm also something of an, mmm, a painter. If you've", + npc(ChatAnim.NEUTRAL, "I'm also something of an, mmm, a painter. If you've", "met any important persons or visited any nice places I", "could paint them for you.") options( - DialogueOption("family_crest", "Can you see if I have a family crest?", expression = ChatAnim.HALF_GUILTY), - DialogueOption("painting", "Can I buy a painting?", expression = ChatAnim.HALF_GUILTY), + DialogueOption("family_crest", "Can you see if I have a family crest?", expression = ChatAnim.ASKING), + DialogueOption("painting", "Can I buy a painting?", expression = ChatAnim.NEUTRAL), ) label("family_crest") - npc(ChatAnim.HALF_GUILTY, "What is your name?") - player(ChatAnim.HALF_GUILTY, "${player!!.username}.") - npc(ChatAnim.HALF_GUILTY, "Mmm, ${player!!.username}, let me see...") + npc(ChatAnim.NEUTRAL, "What is your name?") + player(ChatAnim.NEUTRAL, "${player!!.username}.") + npc(ChatAnim.NEUTRAL, "Mmm, ${player!!.username}, let me see...") exec { player, _ -> if (hasLevelStat(player, Skills.CONSTRUCTION, 16)) { loadLabel(player, "has_construction_level") @@ -98,9 +98,9 @@ class SirReniteeDialogueFile : DialogueLabeller() { } label("no_construction_level") - npc(ChatAnim.HALF_GUILTY, "First thing's first, young ${if (player!!.isMale) "man" else "woman"}! There is not much point", + npc(ChatAnim.NEUTRAL, "First thing's first, young ${if (player!!.isMale) "man" else "woman"}! There is not much point", "in having a family crest if you cannot display it.") - npc(ChatAnim.HALF_GUILTY, "You should train construction until you can build a wall", + npc(ChatAnim.NEUTRAL, "You should train construction until you can build a wall", "decoration in your dining room.") label("has_construction_level") @@ -119,27 +119,27 @@ class SirReniteeDialogueFile : DialogueLabeller() { } label("show_existing_crest") - npc(ChatAnim.HALF_GUILTY, "According to my records, your crest is", + npc(ChatAnim.NEUTRAL, "According to my records, your crest is", "${player!!.houseManager.crest.crestName}.") goto("crest_options") label("show_new_crest") - npc(ChatAnim.HALF_GUILTY, "Well, I don't think you have any noble blood,", + npc(ChatAnim.NEUTRAL, "Well, I don't think you have any noble blood,", "but I see that your ancestors came from ${Util.enumToString(player!!.houseManager.crest.name)},", "so ${if (player!!.houseManager.crest == CrestType.VARROCK) "you can use that city's" else "that can be your"} crest.") goto("crest_options") label("crest_options") options( - DialogueOption("change_crest", "I don't like that crest. Can I have a different one?", expression = ChatAnim.HALF_GUILTY), - DialogueOption("thanks", "Thanks!", expression = ChatAnim.HALF_GUILTY), + DialogueOption("change_crest", "I don't like that crest. Can I have a different one?", expression = ChatAnim.ASKING), + DialogueOption("thanks", "Thanks!", expression = ChatAnim.NEUTRAL), ) label("thanks") - npc(ChatAnim.HALF_GUILTY, "You're welcome, my ${if (player!!.isMale) "boy" else "girl"}.") + npc(ChatAnim.NEUTRAL, "You're welcome, my ${if (player!!.isMale) "boy" else "girl"}.") label("change_crest") - npc(ChatAnim.HALF_GUILTY, "Mmm, very well. Changing your crest will cost", "5,000 coins.") + npc(ChatAnim.NEUTRAL, "Mmm, very well. Changing your crest will cost", "5,000 coins.") exec { player, _ -> if (amountInInventory(player, Items.COINS_995) < CrestType.MISTHALIN.cost) { loadLabel(player, "no_money") @@ -149,10 +149,10 @@ class SirReniteeDialogueFile : DialogueLabeller() { } label("no_money") - player(ChatAnim.HALF_GUILTY, "I'll have to go and get some money then.") + player(ChatAnim.NEUTRAL, "I'll have to go and get some money then.") label("choose_symbol") - npc(ChatAnim.HALF_GUILTY, "There are sixteen different symbols;", "which one would you like?") + npc(ChatAnim.NEUTRAL, "There are sixteen different symbols;", "which one would you like?") goto("crest_page1") label("crest_page1") @@ -272,7 +272,7 @@ class SirReniteeDialogueFile : DialogueLabeller() { else -> arrayOf("This shouldn't be happening. Please report this.") } - interpreter!!.sendDialogues(npc, ChatAnim.HALF_GUILTY, *messages) + interpreter!!.sendDialogues(npc, ChatAnim.NEUTRAL, *messages) } label("crest_buy_fail") @@ -308,17 +308,17 @@ class SirReniteeDialogueFile : DialogueLabeller() { else -> arrayOf("This shouldn't be happening. Please report this.") } - interpreter!!.sendDialogues(npc, ChatAnim.HALF_GUILTY, *messages) + interpreter!!.sendDialogues(npc, ChatAnim.NEUTRAL, *messages) } goto("crest_page1") // Money crest has a unique flow since Sir Renitee needs to check the player's cash stack a second time label("crest_money") - npc(ChatAnim.HALF_GUILTY, "You wish to represent yourself by a moneybag?", + npc(ChatAnim.NEUTRAL, "You wish to represent yourself by a moneybag?", "I think to make that meaningful I should increase", "the price to 500,000 coins. Do you agree?") options( - DialogueOption("money_accept", "All right.", expression = ChatAnim.HALF_GUILTY), - DialogueOption("money_refuse", "No way!", expression = ChatAnim.HALF_GUILTY), + DialogueOption("money_accept", "All right.", expression = ChatAnim.NEUTRAL), + DialogueOption("money_refuse", "No way!", expression = ChatAnim.NEUTRAL), ) label("money_accept") @@ -332,10 +332,10 @@ class SirReniteeDialogueFile : DialogueLabeller() { } label("money_ok") - npc(ChatAnim.HALF_GUILTY, "Thank you very much! You may now use a money-bag", "as your symbol.") + npc(ChatAnim.NEUTRAL, "Thank you very much! You may now use a money-bag", "as your symbol.") label("money_refuse") - npc(ChatAnim.HALF_GUILTY, "Well we can't have just any pauper using a money-bag", + npc(ChatAnim.NEUTRAL, "Well we can't have just any pauper using a money-bag", "as a symbol, can we? You'll have to choose a", "different symbol.") goto("crest_page1") @@ -344,14 +344,14 @@ class SirReniteeDialogueFile : DialogueLabeller() { */ label("painting") - npc(ChatAnim.HALF_GUILTY, "Would you like a portrait or an, mmm, a landscape? Or", "a map, maybe?") + npc(ChatAnim.NEUTRAL, "Would you like a portrait or an, mmm, a landscape? Or", "a map, maybe?") goto("painting_options") label("painting_options") options( - DialogueOption("portrait", "A portrait", "A portrait please.", expression = ChatAnim.HALF_GUILTY), - DialogueOption("landscape", "A landscape", "A landscape please.", expression = ChatAnim.HALF_GUILTY), - DialogueOption("map", "A map", "A map please.", expression = ChatAnim.HALF_GUILTY), + DialogueOption("portrait", "A portrait", "A portrait please.", expression = ChatAnim.NEUTRAL), + DialogueOption("landscape", "A landscape", "A landscape please.", expression = ChatAnim.NEUTRAL), + DialogueOption("map", "A map", "A map please.", expression = ChatAnim.NEUTRAL), ) /** @@ -359,7 +359,7 @@ class SirReniteeDialogueFile : DialogueLabeller() { */ label("portrait") - npc(ChatAnim.HALF_GUILTY, "Mmm, well, there are a few portraits I can paint. I", + npc(ChatAnim.NEUTRAL, "Mmm, well, there are a few portraits I can paint. I", "can only let you have one if you've got some connection", "with that person though. Who would you like?") options( DialogueOption("portrait_arthur", "King Arthur", skipPlayer = true), @@ -369,7 +369,7 @@ class SirReniteeDialogueFile : DialogueLabeller() { ) label("landscape") - npc(ChatAnim.HALF_GUILTY, "Mmm, well, I can paint a few places. Where have you", "had your adventures?") + npc(ChatAnim.NEUTRAL, "Mmm, well, I can paint a few places. Where have you", "had your adventures?") options( DialogueOption("landscape_lumbridge", "The River Lum", skipPlayer = true), DialogueOption("landscape_desert", "The Kharid desert", skipPlayer = true), @@ -379,7 +379,7 @@ class SirReniteeDialogueFile : DialogueLabeller() { ) label("map") - npc(ChatAnim.HALF_GUILTY, "Mmm, yes, ah, I have painted maps of the known world", + npc(ChatAnim.NEUTRAL, "Mmm, yes, ah, I have painted maps of the known world", "on several different sizes of parchment. Which size", "would you like?") options( DialogueOption("map_small", "Small", skipPlayer = true), @@ -417,7 +417,7 @@ class SirReniteeDialogueFile : DialogueLabeller() { label("painting_show_cost") manual { player, _ -> val painting = getAttribute(player, attributeTempPainting, PaintingType.MAP_SMALL) - interpreter!!.sendDialogues(npc, ChatAnim.HALF_GUILTY, "That will be, mmm, ${painting.cost} coins please.") + interpreter!!.sendDialogues(npc, ChatAnim.NEUTRAL, "That will be, mmm, ${painting.cost} coins please.") } exec { player, _ -> val painting = getAttribute(player, attributeTempPainting, PaintingType.MAP_SMALL) @@ -430,8 +430,8 @@ class SirReniteeDialogueFile : DialogueLabeller() { label("painting_buy_confirm") options( - DialogueOption("painting_accept", "All right.", expression = ChatAnim.HALF_GUILTY), - DialogueOption("painting_refuse", "No thanks.", expression = ChatAnim.HALF_GUILTY), + DialogueOption("painting_accept", "All right.", expression = ChatAnim.NEUTRAL), + DialogueOption("painting_refuse", "No thanks.", expression = ChatAnim.NEUTRAL), ) label("painting_buy_fail") @@ -471,7 +471,7 @@ class SirReniteeDialogueFile : DialogueLabeller() { arrayOf("Mmm, I'm not sure you've had enough adventures in the", "world to deserve that map.") } - interpreter!!.sendDialogues(npc, ChatAnim.HALF_GUILTY, *messages) + interpreter!!.sendDialogues(npc, ChatAnim.NEUTRAL, *messages) } manual { player, _ -> val painting = getAttribute(player, attributeTempPainting, PaintingType.MAP_SMALL) @@ -523,20 +523,20 @@ class SirReniteeDialogueFile : DialogueLabeller() { addItemOrDrop(player, painting.paintingId) } } - npc(ChatAnim.HALF_GUILTY, "There you go. Would you like another painting?") + npc(ChatAnim.NEUTRAL, "There you go. Would you like another painting?") goto("painting_options") label("painting_refuse") - npc(ChatAnim.HALF_GUILTY, "Well, mmm, maybe a different painting, mmm?") + npc(ChatAnim.NEUTRAL, "Well, mmm, maybe a different painting, mmm?") goto("painting_options") label("painting_reset") - npc(ChatAnim.HALF_GUILTY, "Would you like a different painting?") + npc(ChatAnim.NEUTRAL, "Would you like a different painting?") options( - DialogueOption("portrait", "Yes - a portrait", "A portrait please.", expression = ChatAnim.HALF_GUILTY), - DialogueOption("landscape", "Yes - a landscape", "A landscape please.", expression = ChatAnim.HALF_GUILTY), - DialogueOption("map", "Yes - a map", "A map please.", expression = ChatAnim.HALF_GUILTY), - DialogueOption("nothing", "No, thanks.", expression = ChatAnim.HALF_GUILTY), + DialogueOption("portrait", "Yes - a portrait", "A portrait please.", expression = ChatAnim.NEUTRAL), + DialogueOption("landscape", "Yes - a landscape", "A landscape please.", expression = ChatAnim.NEUTRAL), + DialogueOption("map", "Yes - a map", "A map please.", expression = ChatAnim.NEUTRAL), + DialogueOption("nothing", "No, thanks.", expression = ChatAnim.NEUTRAL), ) } } \ No newline at end of file