Elemental Workshop I Bug Fixes

Corrected Elemental rock npc out of combat ai
Players can no longer attempt to re-complete the quest
Fixed listeners from user comments
Added the elemental smithing animation and sound
Added more special interactions
Added author tags
This commit is contained in:
Woahscam
2022-09-17 05:02:21 +00:00
committed by Ryan
parent 7c4fd7ea36
commit 338bf3ab70
7 changed files with 141 additions and 26 deletions
@@ -8,6 +8,11 @@ import core.game.node.entity.player.Player
import core.plugin.Initializable import core.plugin.Initializable
import org.rs09.consts.Items import org.rs09.consts.Items
/**
* Battered book handler for the Elemental Workshop I quest
*
* @author Woah, with love
*/
@Initializable @Initializable
class BatteredBookHandler : Book { class BatteredBookHandler : Book {
@@ -20,8 +25,7 @@ class BatteredBookHandler : Book {
} }
override fun finish() { override fun finish() {
val stage = player.questRepository.getStage("Elemental Workshop I") if (EWUtils.currentStage(player) == 0) {
if (stage == 0) {
setQuestStage(player, "Elemental Workshop I", 1) setQuestStage(player, "Elemental Workshop I", 1)
} }
} }
@@ -19,6 +19,11 @@ import rs09.game.content.quest.members.elementalworkshop.EWUtils.currentStage
import rs09.game.interaction.InteractionListener import rs09.game.interaction.InteractionListener
import rs09.game.system.SystemLogger import rs09.game.system.SystemLogger
/**
* Listeners for the Elemental Workshop I quest
*
* @author Woah, with love
*/
class EWListeners : InteractionListener { class EWListeners : InteractionListener {
/* Items */ /* Items */
@@ -58,6 +63,7 @@ class EWListeners : InteractionListener {
private val fillStoneBowlAnimation = Animation(Animations.HUMAN_FILL_STONE_BOWL_4862) private val fillStoneBowlAnimation = Animation(Animations.HUMAN_FILL_STONE_BOWL_4862)
private val fixBellowsAnimation = Animation(Animations.HUMAN_SEW_LARGE_SCENERY_4862) private val fixBellowsAnimation = Animation(Animations.HUMAN_SEW_LARGE_SCENERY_4862)
private val smeltElementalBar = Animation(Animations.HUMAN_FURNACE_SMELTING_3243) private val smeltElementalBar = Animation(Animations.HUMAN_FURNACE_SMELTING_3243)
private val smithElementalShield = Animation(Animations.HUMAN_COOKING_RANGE_896)
/* Sound effects */ /* Sound effects */
private val fillStoneBowlSFX = Audio(Sounds.FILL_STONE_BOWL_1537, 1) private val fillStoneBowlSFX = Audio(Sounds.FILL_STONE_BOWL_1537, 1)
@@ -67,11 +73,15 @@ class EWListeners : InteractionListener {
private val pullLeverEnabledSFX = Audio(Sounds.PULL_LEVER_ENABLED_1547) private val pullLeverEnabledSFX = Audio(Sounds.PULL_LEVER_ENABLED_1547)
private val pullLeverDisabledSFX = Audio(Sounds.PULL_LEVER_DISABLED_1548) private val pullLeverDisabledSFX = Audio(Sounds.PULL_LEVER_DISABLED_1548)
private val bellowLeverSFX = Audio(Sounds.PULL_LEVER_GENERAL_2400) private val bellowLeverSFX = Audio(Sounds.PULL_LEVER_GENERAL_2400)
private val smithElementalShieldSFX = Audio(2721)
/* In-game locations */ /* In-game locations */
private val leftWaterControlsLocation = Location.create(2713, 9907, 0) private val leftWaterControlsLocation = Location.create(2713, 9907, 0)
private val rightWaterControlsLocation = Location.create(2726, 9907, 0) private val rightWaterControlsLocation = Location.create(2726, 9907, 0)
/* Special interactions */
private val furnaceIDS = intArrayOf(4304, 6189, 11010, 11666, 12100, 12809, 14921, 18497, 26814, 30021, 30510, 36956, 37651)
private val DISABLED = 0 private val DISABLED = 0
private val ENABLED = 1 private val ENABLED = 1
@@ -81,7 +91,7 @@ class EWListeners : InteractionListener {
* * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * */
// Bookcase (quest start) // Bookcase (quest start)
on(Scenery.BOOKCASE_26113, SCENERY, "search") { player, _ -> on(Scenery.BOOKCASE_26113, SCENERY, "search") { player, _ ->
val stage = player.questRepository.getStage("Elemental Workshop I") val stage = currentStage(player)
if (stage < 3) { if (stage < 3) {
// Player already has battered book in inventory // Player already has battered book in inventory
@@ -104,13 +114,17 @@ class EWListeners : InteractionListener {
"There is a book here titled 'The Elemental Shield'. " + "There is a book here titled 'The Elemental Shield'. " +
"It can stay here, as you have a copy in your backpack." "It can stay here, as you have a copy in your backpack."
) )
player.inventory.addIfDoesntHave(batteredKey) if (player.inventory.addIfDoesntHave(batteredKey)) {
sendItemDialogue(player, Item(Items.BATTERED_KEY_2887),"You also find a key.")
}
return@on true return@on true
} }
sendItemDialogue(player, Item(Items.SLASHED_BOOK_9715), "You find a book titled 'The Elemental Shield'.") sendItemDialogue(player, Item(Items.SLASHED_BOOK_9715), "You find a book titled 'The Elemental Shield'.")
addItem(player, slashedBook.id) addItem(player, slashedBook.id)
player.inventory.addIfDoesntHave(batteredKey) if (player.inventory.addIfDoesntHave(batteredKey)) {
sendItemDialogue(player, Item(Items.BATTERED_KEY_2887),"You also find a key.")
}
return@on true return@on true
} }
@@ -149,6 +163,11 @@ class EWListeners : InteractionListener {
* * * * * * * * * * * * * * * * * */ * * * * * * * * * * * * * * * * * */
// Odd looking wall handler // Odd looking wall handler
on(intArrayOf(Scenery.ODD_LOOKING_WALL_26114, Scenery.ODD_LOOKING_WALL_26115), SCENERY, "open") { player, wall -> on(intArrayOf(Scenery.ODD_LOOKING_WALL_26114, Scenery.ODD_LOOKING_WALL_26115), SCENERY, "open") { player, wall ->
// Nothing interesting happens if quest wasn't started.
if (currentStage(player) < 1) {
sendMessage(player, "Nothing interesting happens.")
return@on true
}
// Player is allowed to exit without key // Player is allowed to exit without key
if (player.location == Location.create(2710, 3496, 0) || player.location == Location.create(2709, 3496, 0)) { if (player.location == Location.create(2710, 3496, 0) || player.location == Location.create(2709, 3496, 0)) {
DoorActionHandler.handleAutowalkDoor(player, wall.asScenery()) DoorActionHandler.handleAutowalkDoor(player, wall.asScenery())
@@ -170,9 +189,16 @@ class EWListeners : InteractionListener {
} }
on(Scenery.STAIRCASE_3415, SCENERY, "climb-down") { player, _ -> on(Scenery.STAIRCASE_3415, SCENERY, "climb-down") { player, _ ->
val stage = currentStage(player)
// Prevent player from somehow skipping beginning quest stages
if (stage < 1) {
sendMessage(player, "Nothing interesting happens.")
return@on true
}
// Move player to downstairs location
teleport(player, Location.create(2716, 9888, 0)) teleport(player, Location.create(2716, 9888, 0))
// If it is the players first time in the workshop // If it is the players first time in the workshop
if (currentStage(player) < 6) { if (stage < 6) {
sendPlayerDialogue(player, sendPlayerDialogue(player,
"Now to explore this area thoroughly, to find what " + "Now to explore this area thoroughly, to find what " +
"forgotten secrets it contains.", FacialExpression.NEUTRAL) "forgotten secrets it contains.", FacialExpression.NEUTRAL)
@@ -233,7 +259,7 @@ class EWListeners : InteractionListener {
} }
// Workbenches/Anvil // Workbenches/Anvil
onUseWith(SCENERY, Items.ELEMENTAL_METAL_2893, Scenery.WORKBENCH_3402) { player, used, _ -> onUseWith(SCENERY, Items.ELEMENTAL_METAL_2893, Scenery.WORKBENCH_3402) { player, used, workbench ->
// Warn player their smithing level is too low to make the shield // Warn player their smithing level is too low to make the shield
if (player.skills.getLevel(Skills.SMITHING) < 20) { if (player.skills.getLevel(Skills.SMITHING) < 20) {
sendMessage(player, "You need a smithing level of 20 to create an elemental shield.") sendMessage(player, "You need a smithing level of 20 to create an elemental shield.")
@@ -241,12 +267,12 @@ class EWListeners : InteractionListener {
} }
// Warn player they don't have the required book in their inventory // Warn player they don't have the required book in their inventory
if (!player.inventory.containsAtLeastOneItem(intArrayOf(Items.SLASHED_BOOK_9715, Items.BATTERED_BOOK_2886))) { if (!player.inventory.containsAtLeastOneItem(intArrayOf(Items.SLASHED_BOOK_9715, Items.BATTERED_BOOK_2886))) {
sendMessage(player, "You are unsure what to do with the bar. Perhaps there is a book to help guide you.") sendDialogue(player, "This workbench is too complicated. You need instructions to follow.")
return@onUseWith true return@onUseWith true
} }
// Warn player they don't have a hammer in their inventory // Warn player they don't have a hammer in their inventory
if (!inInventory(player, Items.HAMMER_2347)) { if (!inInventory(player, Items.HAMMER_2347)) {
sendMessage(player, "You don't have a tool available to shape the metal.") sendDialogue(player, "You don't have a tool available to shape the metal.")
return@onUseWith true return@onUseWith true
} }
// Sanity error check (Should never get thrown) // Sanity error check (Should never get thrown)
@@ -255,9 +281,19 @@ class EWListeners : InteractionListener {
return@onUseWith false return@onUseWith false
} }
// Successfully smith the elemental shield // Successfully smith the elemental shield
lock(player, (animationDuration(smithElementalShield) + 2))
runTask(player) {
face(player, workbench)
animate(player, smithElementalShield)
playAudio(player, smithElementalShieldSFX)
replaceSlot(player, used.asItem().slot, elementalShield) replaceSlot(player, used.asItem().slot, elementalShield)
rewardXP(player, Skills.SMITHING, 20.0)
sendMessage(player, "Following the instructions in the book you make an elemental shield.") sendMessage(player, "Following the instructions in the book you make an elemental shield.")
}
// Check to see if the quest is completed, if not, complete the quest
if (!player.questRepository.getQuest("Elemental Workshop I").isCompleted(player)) {
player.questRepository.getQuest("Elemental Workshop I").finish(player) player.questRepository.getQuest("Elemental Workshop I").finish(player)
}
return@onUseWith true return@onUseWith true
} }
@@ -340,8 +376,8 @@ class EWListeners : InteractionListener {
return@onUseWith true return@onUseWith true
} }
val duration = animationDuration(smeltElementalBar) val barSmeltingDuration = animationDuration(smeltElementalBar)
lock(player, (duration + 1)) lock(player, barSmeltingDuration + 2)
// Run the "smelting" pulse // Run the "smelting" pulse
submitIndividualPulse(player, object : Pulse() { submitIndividualPulse(player, object : Pulse() {
var count = 0 var count = 0
@@ -353,7 +389,7 @@ class EWListeners : InteractionListener {
animate(player, Animations.HUMAN_FURNACE_SMELTING_3243) animate(player, Animations.HUMAN_FURNACE_SMELTING_3243)
sendMessage(player, "You place the elemental ore and four heaps of coal into the furnace.") sendMessage(player, "You place the elemental ore and four heaps of coal into the furnace.")
} }
duration -> { barSmeltingDuration -> {
addItem(player, Items.ELEMENTAL_METAL_2893) addItem(player, Items.ELEMENTAL_METAL_2893)
sendMessage(player, "You retrieve a bar of elemental metal.") sendMessage(player, "You retrieve a bar of elemental metal.")
rewardXP(player, Skills.SMITHING, 7.0) rewardXP(player, Skills.SMITHING, 7.0)
@@ -367,6 +403,20 @@ class EWListeners : InteractionListener {
return@onUseWith true return@onUseWith true
} }
/* Special Interactions:
* onUseWith FURNACE scenery (this handler)
* onUseWith BLAST FURNACE BELT (this handler)
* onCast SUPERHEAT spell (ModernListeners.kt)
* SKIPPED DUE TO SPAGHETTI: on BLAST FURNACE BELT "put-ore-on" (BlastfurnaceListeners.kt)
*/
onUseWith(SCENERY, Items.ELEMENTAL_ORE_2892, *furnaceIDS) { player, _, _ ->
sendMessage(player, "This furnace is not hot enough to smelt elemental ore.")
return@onUseWith true
}
onUseWith(SCENERY, Items.ELEMENTAL_ORE_2892, Scenery.CONVEYOR_BELT_9100) { player, _, _ ->
sendMessage(player, "There would be no point in smelting that.")
return@onUseWith true
}
/* * * * * * * * * * * * * * /* * * * * * * * * * * * * *
* Water room listeners * * Water room listeners *
@@ -479,10 +529,18 @@ class EWListeners : InteractionListener {
return@on true return@on true
} }
// Warn player they don't have the required items // Warn player they don't have the required items
if (!player.inventory.containsAll(*bellowFixReqItems)) { bellowFixReqItems.forEach { reqItem ->
sendMessage(player, "You don't have the required item(s) to fix the bellows.") if (!player.inventory.contains(reqItem, 1)) {
val missingItemMsg = when (reqItem) {
Items.NEEDLE_1733 -> "a needle"
Items.THREAD_1734 -> "some thread"
Items.LEATHER_1741 -> "a piece of leather"
else -> "NULL" // Should never be called; no sense in making an enum
}
sendMessage(player, "You need $missingItemMsg to fix the bellows.")
return@on true return@on true
} }
}
// If the player has all the requirements to fix the bellows // If the player has all the requirements to fix the bellows
lock(player, (animationDuration(fixBellowsAnimation) + 1)) lock(player, (animationDuration(fixBellowsAnimation) + 1))
runTask(player) { runTask(player) {
@@ -532,5 +590,19 @@ class EWListeners : InteractionListener {
setDest(SCENERY, intArrayOf(Scenery.BELLOWS_18516), "fix") { _, _ -> setDest(SCENERY, intArrayOf(Scenery.BELLOWS_18516), "fix") { _, _ ->
return@setDest Location.create(2733, 9884, 0) return@setDest Location.create(2733, 9884, 0)
} }
setDest(SCENERY, intArrayOf(Scenery.ODD_LOOKING_WALL_26115), "open") { player, _ ->
if (inBorders(player, 2709, 3496, 2711, 3498)) {
return@setDest Location.create(2709, 3496, 0)
} else {
return@setDest Location.create(2709, 3495, 0)
}
}
setDest(SCENERY, intArrayOf(Scenery.ODD_LOOKING_WALL_26114), "open") { player, _ ->
if (inBorders(player, 2709, 3496, 2711, 3498)) {
return@setDest Location.create(2710, 3496, 0)
} else {
return@setDest Location.create(2710, 3495, 0)
}
}
} }
} }
@@ -7,6 +7,12 @@ import core.game.content.dialogue.book.PageSet
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import org.rs09.consts.Vars import org.rs09.consts.Vars
/**
* Utils for the Elemental Workshop I quest
* Book/Varp/Method Utils
*
* @author Woah, with love
*/
object EWUtils { object EWUtils {
// Helpers for the battered/slashed book // Helpers for the battered/slashed book
@@ -29,6 +29,8 @@ class ElementalRockNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id
// Sanity transform back to elemental rock // Sanity transform back to elemental rock
override fun init() { override fun init() {
isWalks = false
isNeverWalks = true
transform(NPCs.ELEMENTAL_ROCK_4911) transform(NPCs.ELEMENTAL_ROCK_4911)
super.init() super.init()
} }
@@ -41,19 +43,21 @@ class ElementalRockNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id
return intArrayOf(NPCs.EARTH_ELEMENTAL_4910, NPCs.ELEMENTAL_ROCK_4911) return intArrayOf(NPCs.EARTH_ELEMENTAL_4910, NPCs.ELEMENTAL_ROCK_4911)
} }
// The NPC stays in their upright form when the player
// stops attacking and can walk around
override fun defineListeners() { override fun defineListeners() {
on(ids, NPC, "Mine") { player, node -> on(ids, NPC, "Mine") { player, node ->
val tool: SkillingTool? = getTool(player, true) val tool: SkillingTool? = getTool(player, true)
// Warn player they don't have a high enough mining level
if (player.skills.getStaticLevel(Skills.MINING) < 20) {
sendDialogue(player, "You need a mining level of at least 20 to mine elemental ore.")
return@on true
}
// Check to see if the player can proc the NPC // Check to see if the player can proc the NPC
if (tool == null) { if (tool == null) {
sendDialogue(player, "You do not have a pickaxe which you have the Mining level to use.") sendDialogue(player, "You do not have a pickaxe which you have the Mining level to use.")
return@on true return@on true
} }
// Warn player they don't have a high enough mining level
if (player.skills.getStaticLevel(Skills.MINING) < 20) {
sendDialogue(player, "You need a mining level of at least 20 to mine elemental ore.")
return@on true
}
// Transform the current NPC to 4910 (monster form) // Transform the current NPC to 4910 (monster form)
pulseManager.run(object : Pulse() { pulseManager.run(object : Pulse() {
var count = 0 var count = 0
@@ -69,6 +73,8 @@ class ElementalRockNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id
// Start attacking the player // Start attacking the player
3 -> { 3 -> {
node.asNpc().attack(player) node.asNpc().attack(player)
node.asNpc().isNeverWalks = false
node.asNpc().isWalks = true
return true return true
} }
} }
@@ -82,6 +88,8 @@ class ElementalRockNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id
// Reset back to rock NPC // Reset back to rock NPC
override fun finalizeDeath(killer: Entity?) { override fun finalizeDeath(killer: Entity?) {
isWalks = false
isNeverWalks = true
transform(NPCs.ELEMENTAL_ROCK_4911) transform(NPCs.ELEMENTAL_ROCK_4911)
super.finalizeDeath(killer) super.finalizeDeath(killer)
} }
@@ -3,6 +3,7 @@ package rs09.game.content.quest.members.elementalworkshop
import api.Commands import api.Commands
import api.addItem import api.addItem
import api.setAttribute import api.setAttribute
import api.setVarbit
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.player.link.quest.Quest
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
@@ -131,5 +132,17 @@ class ElementalWorkshopQuest : Quest("Elemental Workshop I", 52, 51, 1), Command
addItem(player, Items.HAMMER_2347) addItem(player, Items.HAMMER_2347)
addItem(player, Items.COAL_453, 4) addItem(player, Items.COAL_453, 4)
} }
define("readyew", Privilege.ADMIN) { player, _ ->
val enabled = 1
setAttribute(player, "/save:ew1:got_needle", true)
setAttribute(player, "/save:ew1:got_leather", true)
setAttribute(player, "/save:ew1:bellows_fixed", true)
player.questRepository.setStageNonmonotonic(player.questRepository.forIndex(52), 95)
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, EWUtils.BELLOWS_STATE, enabled, true)
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, EWUtils.FURNACE_STATE, enabled, true)
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, EWUtils.WATER_WHEEL_STATE, enabled, true)
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, EWUtils.RIGHT_WATER_CONTROL_STATE, enabled, true)
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, EWUtils.LEFT_WATER_CONTROL_STATE, enabled, true)
}
} }
} }
@@ -7,6 +7,11 @@ import core.game.node.entity.player.Player
import core.plugin.Initializable import core.plugin.Initializable
import org.rs09.consts.Items import org.rs09.consts.Items
/**
* Slashed book handler for the Elemental Workshop I quest
*
* @author Woah, with love
*/
@Initializable @Initializable
class SlashedBookHandler : Book { class SlashedBookHandler : Book {
constructor(player: Player?) : super(player, "Book of the elemental shield", Items.SLASHED_BOOK_9715, *EWUtils.PAGES) {} constructor(player: Player?) : super(player, "Book of the elemental shield", Items.SLASHED_BOOK_9715, *EWUtils.PAGES) {}
@@ -3,6 +3,7 @@ package rs09.game.node.entity.skill.magic
import api.events.ItemAlchemizationEvent import api.events.ItemAlchemizationEvent
import api.events.TeleportEvent import api.events.TeleportEvent
import api.getAttribute import api.getAttribute
import api.sendMessage
import core.game.content.activity.mta.impl.GraveyardZone import core.game.content.activity.mta.impl.GraveyardZone
import core.game.content.global.Bones import core.game.content.global.Bones
import core.game.interaction.MovementPulse import core.game.interaction.MovementPulse
@@ -10,8 +11,8 @@ import core.game.node.entity.Entity
import core.game.node.entity.impl.Animator.Priority import core.game.node.entity.impl.Animator.Priority
import core.game.node.entity.impl.Projectile import core.game.node.entity.impl.Projectile
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.entity.player.link.audio.Audio
import core.game.node.entity.player.link.TeleportManager import core.game.node.entity.player.link.TeleportManager
import core.game.node.entity.player.link.audio.Audio
import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.player.link.diary.DiaryType
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import core.game.node.entity.skill.smithing.smelting.Bar import core.game.node.entity.skill.smithing.smelting.Bar
@@ -188,6 +189,12 @@ class ModernListeners : SpellListener("modern"){
return return
} }
// Elemental Workshop I special interaction
if(item.id == Items.ELEMENTAL_ORE_2892) {
sendMessage(player, "Even this spell is not hot enough to heat this item.")
return
}
var bar = Bar.forOre(item.id) ?: return var bar = Bar.forOre(item.id) ?: return
if(bar == Bar.IRON && player.inventory.getAmount(Items.COAL_453) >= 2 && player.skills.getLevel(Skills.SMITHING) >= Bar.STEEL.level && player.inventory.contains(Items.IRON_ORE_441,1)) bar = Bar.STEEL if(bar == Bar.IRON && player.inventory.getAmount(Items.COAL_453) >= 2 && player.skills.getLevel(Skills.SMITHING) >= Bar.STEEL.level && player.inventory.contains(Items.IRON_ORE_441,1)) bar = Bar.STEEL