From d879416a85f553255a9b407a2ada2c59432f4c85 Mon Sep 17 00:00:00 2001 From: aidan Date: Tue, 7 Jul 2026 20:57:54 -0500 Subject: [PATCH] fuck these dynamic regions though --- .../quest/elementalworkshop/EWListeners.kt | 61 ++-- .../elementalworkshop2/EW2ActivityPlugin.kt | 78 +++++ .../quest/elementalworkshop2/EW2Listeners.kt | 306 ++++++++---------- .../seers/quest/elementalworkshop2/EW2Zone.kt | 63 +--- .../elementalworkshop2/ElementalWorkshop2.kt | 15 +- .../quest/elementalworkshop2/JigCartNPC.kt | 26 +- 6 files changed, 271 insertions(+), 278 deletions(-) create mode 100644 Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2ActivityPlugin.kt diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt index 40eacb72d..8cc3616e5 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt @@ -20,6 +20,7 @@ import content.data.Quests import content.global.handlers.iface.BookInterface import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.EW2StageVarbit import core.game.dialogue.DialogueLabeller +import core.game.interaction.QueueStrength /** * Listeners for the Elemental Workshop I quest @@ -308,13 +309,14 @@ class EWListeners : InteractionListener { } // Successfully smith the elemental shield lock(player, (animationDuration(smithElementalShield) + 2)) - runTask(player) { + queueScript(player, 0, QueueStrength.SOFT) { _ -> face(player, workbench) animate(player, smithElementalShield) playAudio(player, smithElementalShieldSFX) 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.") + return@queueScript stopExecuting(player) } } 3 -> { // ele claw @@ -335,13 +337,14 @@ class EWListeners : InteractionListener { } // Successfully smith the elemental shield lock(player, (animationDuration(smithElementalShield) + 2)) - runTask(player) { + queueScript(player, 0, QueueStrength.SOFT) { _ -> face(player, workbench) animate(player, smithElementalShield) playAudio(player, smithElementalShieldSFX) replaceSlot(player, used.asItem().slot, Item(Items.CRANE_CLAW_9720)) rewardXP(player, Skills.SMITHING, 20.0) sendMessage(player, "Following the instructions from the schematic you make an elemental claw.") + return@queueScript stopExecuting(player) } } 4 -> { // ele helm @@ -362,13 +365,14 @@ class EWListeners : InteractionListener { } // Successfully smith the elemental helm lock(player, (animationDuration(smithElementalShield) + 2)) - runTask(player) { + queueScript(player, 0, QueueStrength.SOFT) { _ -> face(player, workbench) animate(player, smithElementalShield) playAudio(player, smithElementalShieldSFX) replaceSlot(player, used.asItem().slot, Item(Items.ELEMENTAL_HELMET_9729)) rewardXP(player, Skills.SMITHING, 20.0) sendMessage(player, "Following the instructions in the book you make an elemental helm.") + return@queueScript stopExecuting(player) } } } @@ -398,13 +402,14 @@ class EWListeners : InteractionListener { } // Successfully smith the elemental shield lock(player, (animationDuration(smithElementalShield) + 2)) - runTask(player) { + queueScript(player, 0, QueueStrength.SOFT) { _ -> face(player, workbench) animate(player, smithElementalShield) playAudio(player, smithElementalShieldSFX) 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.") + return@queueScript stopExecuting(player) } // Check to see if the quest is completed, if not, complete the quest if (!player.questRepository.getQuest(Quests.ELEMENTAL_WORKSHOP_I).isCompleted(player)) { @@ -433,24 +438,36 @@ class EWListeners : InteractionListener { } // Successfully smith the elemental helm and complete quest lock(player, (animationDuration(smithElementalShield) + 2)) - runTask(player) { - face(player, workbench) - animate(player, smithElementalShield) - playAudio(player, smithElementalShieldSFX) - replaceSlot(player, used.asItem().slot, Item(Items.MIND_HELMET_9733)) - rewardXP(player, Skills.SMITHING, 30.0) - sendMessage(player, "Following the instructions in the book you make an elemental mind helm.") + queueScript(player, 0, QueueStrength.SOFT) { count -> + when(count) { + // make the helm + 0 -> { + face(player, workbench) + animate(player, smithElementalShield) + playAudio(player, smithElementalShieldSFX) + replaceSlot(player, used.asItem().slot, Item(Items.MIND_HELMET_9733)) + rewardXP(player, Skills.SMITHING, 30.0) + sendMessage(player, "Following the instructions in the book you make an elemental mind helm.") + return@queueScript delayScript(player, smithElementalShield.duration) + } - if(getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) <= 10) { - setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II, 11) - setVarbit(player, EW2StageVarbit, 11, true) - } - // complete quest if it's not done. - if (!isQuestComplete(player, Quests.ELEMENTAL_WORKSHOP_II)) { - finishQuest(player, Quests.ELEMENTAL_WORKSHOP_II) + // complete quest + 1 -> { + if(getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) <= 10) { + setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II, 11) + setVarbit(player, EW2StageVarbit, 11, true) + } + // complete quest if it's not done. + if (!isQuestComplete(player, Quests.ELEMENTAL_WORKSHOP_II)) { + finishQuest(player, Quests.ELEMENTAL_WORKSHOP_II) + } + return@queueScript delayScript(player, 1) + } } + return@queueScript stopExecuting(player) } - // choice to make helmet or shield + + // after quest, choice to make helmet or shield } else { sendDialogueOptions(player, "Would you like to make...", "An elemental mind shield.", "An elemental mind helm.") addDialogueAction(player) { _, buttonId -> @@ -473,13 +490,14 @@ class EWListeners : InteractionListener { } // Successfully smith the elemental mind shield lock(player, (animationDuration(smithElementalShield) + 2)) - runTask(player) { + queueScript(player, 0, QueueStrength.SOFT) { _ -> face(player, workbench) animate(player, smithElementalShield) playAudio(player, smithElementalShieldSFX) replaceSlot(player, used.asItem().slot, Item(Items.MIND_SHIELD_9731)) rewardXP(player, Skills.SMITHING, 30.0) sendMessage(player, "Following the instructions in the book you make an elemental mind shield.") + return@queueScript stopExecuting(player) } } 3 -> { // ele mind helm @@ -500,13 +518,14 @@ class EWListeners : InteractionListener { } // Successfully smith the elemental helm lock(player, (animationDuration(smithElementalShield) + 2)) - runTask(player) { + queueScript(player, 0, QueueStrength.SOFT) { _ -> face(player, workbench) animate(player, smithElementalShield) playAudio(player, smithElementalShieldSFX) replaceSlot(player, used.asItem().slot, Item(Items.MIND_HELMET_9733)) rewardXP(player, Skills.SMITHING, 30.0) sendMessage(player, "Following the instructions in the book you make an elemental mind helm.") + return@queueScript stopExecuting(player) } } } diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2ActivityPlugin.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2ActivityPlugin.kt new file mode 100644 index 000000000..c20595485 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2ActivityPlugin.kt @@ -0,0 +1,78 @@ +package content.region.kandarin.seers.quest.elementalworkshop2 + +import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncCart +import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncCrane +import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncFan +import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncSteamPress +import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncTank +import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncTankDoor +import core.api.* +import core.game.activity.ActivityPlugin +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.build.DynamicRegion +import core.plugin.Initializable + +/** + * Handles an instanced Elemental Workshop II session. + */ + +@Initializable +class EW2ActivityPlugin : ActivityPlugin { + + // blank constructor because ... + constructor() : super("EW2 Workshop", true, false, false) + + // real constructor + constructor(player: Player) : super("EW2 Workshop", true, false, false) { + this.player = player + } + + // more empty shit + override fun configure() { + } + + // starts the dynamic region + override fun start(player: Player, login: Boolean, vararg args: Any): Boolean { + player.properties.teleportLocation = base.transform(35, 35, 2) + return super.start(player, login, *args) + } + + // the exit location back in EW1 workshop + override fun getSpawnLocation(): Location = Location.create(2719, 9892, 0) + + // creates the region + override fun newInstance(p: Player): ActivityPlugin { + val plugin = EW2ActivityPlugin(p) + plugin.region = DynamicRegion.create(7760) + plugin.setRegionBase() + plugin.register(plugin.region.borders) + return plugin + } + + // sync the workshop state + override fun enter(e: Entity?): Boolean { + if (e is Player) { + + // need a slight delay or all the stuff doesn't load right + queueScript(player, 1) { + syncCrane(player) + syncCart(player) + syncSteamPress(player) + syncTank(player) + syncTankDoor(player) + syncFan(player) + return@queueScript stopExecuting(player) + } + } + + return super.enter(e) + } + + // this should shut down the dynamic region, I hope + override fun leave(e: Entity, logout: Boolean): Boolean { + unregisterRegion(region.id) + return super.leave(e, logout) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Listeners.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Listeners.kt index 6dd04a408..be2c29859 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Listeners.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Listeners.kt @@ -25,24 +25,15 @@ import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2 import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.attrJigged import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.attrWatered import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.attrWinded -import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.badHat -import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.eHelmEquip -import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.eShieldEquip -import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.goodHat -import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.mHelmEquip -import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.mShieldEquip -import core.game.world.update.flag.context.Graphics import core.api.* -import core.game.container.impl.EquipmentContainer +import core.game.activity.ActivityPlugin import core.game.interaction.IntType import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills -import core.game.node.item.Item -import core.game.node.scenery.SceneryBuilder import core.game.world.map.Location -import core.game.world.map.RegionManager import org.rs09.consts.* class EW2Listeners : InteractionListener { @@ -52,8 +43,6 @@ class EW2Listeners : InteractionListener { //val ew2_ambient_loop = 3149 //val ew2_machine_loop = 3150 - // gfx 809 mind shield equip, 810 mind helmet equip - /** * Quest structure and testing notes: * @@ -81,6 +70,18 @@ class EW2Listeners : InteractionListener { companion object { + // graphics + const val goodHatGfx = Graphics.EXTRACTOR_HAT_CHAIR_807 + const val badHatGfx = Graphics.IMPROPERLY_EXTRACTOR_HAT_CHAIR_808 + + // animations + const val goodHatAnim = Animations.HUMAN_USE_EXTRACTOR_HAT_CHAIR_4884 + const val badHatAnim = Animations.HUMAN_IMPROPERLY_USE_EXTRACTOR_HAT_CHAIR_4885 + const val CRANE_LOWER = 4887 + const val CRANE_RAISE = 4888 + const val CRANE_PIVOT_TO_LAVA = 4889 + const val CRANE_PIVOT_TO_CART = 4890 + /** * This companion object stores helper functions to save and recall workshop state. * All the machinery states are stored in varbits and are updated in various listeners, @@ -157,32 +158,37 @@ class EW2Listeners : InteractionListener { fun syncCrane(player: Player) { val mat = getVarbit(player, EW2CraneStateVarbit) val pos = getVarbit(player, EW2CranePosVarbit) - val index = (mat * 4) + pos val newId = craneStates[index] - val craneLoc = Location.create(1952, 5143, 2) - val currentObject = getScenery(craneLoc) + val craneLoc = toInstance(player, Location.create(1952, 5143, 2)) + val currentObject = getScenery(craneLoc) ?: return - // check an object actually exists here - if (currentObject != null) { + if (!craneStates.contains(currentObject.id)) return + if (currentObject.id == newId) return - // check that the object is a crane - if (craneStates.contains(currentObject.id)) { + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + animateScenery(player, currentObject, 4890) // replace with confirmed anim ID + return@queueScript delayScript(player, 4) // adjust to match anim duration + } - // replace crane if current state doesn't match what is called for - // todo it would be nice to animate this crane - if (currentObject.id != newId) { + 1 -> { replaceScenery(currentObject, newId, -1) playAudio(player, Sounds.EW2_CRANE_TURN_3170) - //playAudio(player, Sounds.EW2_CRANE_LOWER_3169) - - // if bar is hot set attr for quest log - if(newId == 18634) { - if(!getAttribute(player, attrDipped, false) && getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) <= 6) { + if (newId == Scenery.OLD_CRANE_18634) { + if (!getAttribute(player, attrDipped, false) && getQuestStage( + player, + Quests.ELEMENTAL_WORKSHOP_II + ) <= 6 + ) { setAttribute(player, attrDipped, true) } } + return@queueScript stopExecuting(player) } + + else -> return@queueScript stopExecuting(player) } } } @@ -192,12 +198,12 @@ class EW2Listeners : InteractionListener { val cartState = getVarbit(player, EW2JigCartVarbit) val cartPosIndex = getVarbit(player, EW2CartLeverVarbit) // cart moving lever. 0 = start (south), 1 = west, 2 = north, 3 = east val targetId = jigCartStates[cartState] - val targetLocation = jigCartLocations[cartPosIndex] + val targetLocation = toInstance(player, jigCartLocations[cartPosIndex]) val tankState = getVarbit(player, EW2TankDoorStateVarbit) // if tank state is 3, 4, 6, 7 the cart is inside the tank and should be removed. // find cart - val trackCenter = Location.create(1953, 5155, 2) - val existingCart = RegionManager.getLocalNPCs(trackCenter, 15).find { it.id in jigCartStates } + val trackCenter = toInstance(player, Location.create(1953, 5155, 2)) + val existingCart = findLocalNPCs(trackCenter, 15).find { it.id in jigCartStates } if (existingCart != null) { // update appearance if (existingCart.id != targetId) { @@ -214,8 +220,10 @@ class EW2Listeners : InteractionListener { // if there is no cart, spawn a new one (like if player is just logging in) } else { - val newCart = NPC(targetId, targetLocation) + // create the cart + val newCart = NPC.create(targetId, targetLocation) newCart.init() + // if cart is inside water tank, clear it if (tankState == 3 || tankState == 4 || tankState == 6 || tankState == 7) { newCart.clear() @@ -230,19 +238,17 @@ class EW2Listeners : InteractionListener { val largeCogs = intArrayOf(Scenery.COG_18675, Scenery.COG_18669, Scenery.COG_18672) // pin 1, pin 2, pin 3 /* - // updates the cog states (unused right now) + // updates the cog states (unused right now, could maybe set the anims here) fun syncCogs(player: Player) { } */ - /* - // visualizes the steam press. right now all it does is spawn the hammer part. no anims or fancy stuff. - // if you just do a simple scenerybuilder.add it'll overwrite the track, so i've disabled it for now. + // visualizes the steam press. right now all it does is spawn the hammer part. fun syncSteamPress(player: Player) { - SceneryBuilder.add(core.game.node.scenery.Scenery(steamPressStates[2], Location.create(1946, 5155, 2), 0)) + val pressLoc = toInstance(player, Location.create(1946, 5155, 2)) + addScenery(core.game.node.scenery.Scenery(steamPressStates[2], pressLoc, 0)) } - */ // syncs the water tank state. this is dependent on the valve states and the door state. also changes cart state (cooling bar) fun syncTank(player: Player) { @@ -253,12 +259,12 @@ class EW2Listeners : InteractionListener { if(waterIn == 0 && waterOut == 0) { // both valves closed, nothing happens return } else if(waterIn == 0 && waterOut == 1) { // drain is open, remove all added scenery - SceneryBuilder.remove(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18660, location(1951, 5164, 2))) // full tank - SceneryBuilder.remove(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18661, location(1951, 5164, 2))) // full tank + removeScenery(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18660, toInstance(player, location(1951, 5164, 2)))) // full tank + removeScenery(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18661, toInstance(player, location(1951, 5164, 2)))) // full tank playAudio(player, Sounds.EW2_WATER_RACK_3184) // drain? return } else if(waterIn == 1 && waterOut == 0) { // inlet is open, fill tank - SceneryBuilder.add(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18661, location(1951, 5164, 2))) // full tank + addScenery(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18661, toInstance(player, location(1951, 5164, 2)))) // full tank playAudio(player, Sounds.EW2_WATER_FLOW_3183) // fill? if(cart == 3) { // if cart state is hot bar, transform to cool bar. setVarbit(player, EW2TankDoorStateVarbit, 6, true) @@ -271,7 +277,7 @@ class EW2Listeners : InteractionListener { } return } else if(waterIn == 1 && waterOut == 1) { // inlet and drain are open, tank is damp - SceneryBuilder.add(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18660, location(1951, 5164, 2))) // damp tank + addScenery(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18660, toInstance(player, location(1951, 5164, 2)))) // damp tank playAudio(player, Sounds.EW2_WATER_FLOW_3183) // fill? return } @@ -280,15 +286,15 @@ class EW2Listeners : InteractionListener { // syncs the tank door state. this is dependent on the corkscrew lever and cart state fun syncTankDoor(player: Player) { val state = getVarbit(player, EW2TankDoorStateVarbit) - val doorLoc = Location.create(1953, 5162, 2) + val doorLoc = toInstance(player, Location.create(1953, 5162, 2)) val currentObject = getScenery(doorLoc) val targetId = tankDoorStates[state] // remove old scenery then add the new one - // todo right now this also deletes a piece of track that shares a tile with the door, but I don't really care. if(currentObject != null && currentObject.id != targetId) { - SceneryBuilder.remove(core.game.node.scenery.Scenery(currentObject.id, doorLoc, 1)) - SceneryBuilder.add(core.game.node.scenery.Scenery(targetId, doorLoc, 1)) + // I know what you're thinking, replaceScenery doesn't work in this situation. Why? idfk + removeScenery(core.game.node.scenery.Scenery(currentObject.id, doorLoc, 1)) + addScenery(core.game.node.scenery.Scenery(targetId, doorLoc, 1)) } } @@ -301,16 +307,26 @@ class EW2Listeners : InteractionListener { setVarbit(player, EW2FanStateVarbit, 0) } } + + // helper to convert static location coordinates to the dynamic instance location coordinates + fun toInstance(player: Player, staticLoc: Location): Location { + val activity = player.getAttribute("activity") + if (activity == null || activity.base == null) return staticLoc + + val offsetX = staticLoc.x - 1920 + val offsetY = staticLoc.y - 5120 + + return activity.base.transform(offsetX, offsetY, staticLoc.z) + } } override fun defineListeners() { - /**------------------------ - * SECTION 0 - START - ** ------------------------ - */ + /** ------------------------ * + * SECTION 0 - START + * ------------------------- */ - // Bookcase at digsite where you start quest. + // bookcase at digsite where you start quest on(Scenery.BOOKCASE_17382, IntType.SCENERY, "search") { player, _ -> if (isQuestComplete(player, Quests.ELEMENTAL_WORKSHOP_I) && !inInventory(player, Items.BEATEN_BOOK_9717) && @@ -351,7 +367,6 @@ class EW2Listeners : InteractionListener { } if(getVarbit(player, EW2MachineryVarbit) == 0) setVarbit(player, EW2MachineryVarbit, 1, true) - // todo the wiki notes that you must actually walk the path for the varbit to flip. Here i just flip it as soon as you read the scroll. return@on true } @@ -408,14 +423,14 @@ class EW2Listeners : InteractionListener { // stairs to lower level on(Scenery.STAIRWELL_18597, IntType.SCENERY, "climb-down") { player, _ -> - teleport(player, location(1948, 5158, 0)) + teleport(player, toInstance(player,location(1948, 5158, 0))) sendMessage(player, "You climb down the stairs.") return@on true } // stairs back up to EW2 on(Scenery.STAIRS_18599, IntType.SCENERY, "climb-up") { player, _ -> - teleport(player, location(1948, 5157, 2)) + teleport(player, toInstance(player,location(1948, 5157, 2))) sendMessage(player, "You climb up the stairs.") return@on true } @@ -459,9 +474,9 @@ class EW2Listeners : InteractionListener { val fanState = getVarbit(player, EW2FanLeverVarbit) // 1 if the fan is on - // find the cart - val trackCenter = Location.create(1953, 5155, 2) - val existingCart = RegionManager.getLocalNPCs(trackCenter, 15).find { it.id in jigCartStates } + // find the player's cart + val existingCart = findLocalNPCs(player, jigCartStates, 32).firstOrNull() + if (existingCart == null) { sendMessage(player, "CART MISSING. LOG IN AND OUT TO RESET IT.") return@on true @@ -508,7 +523,7 @@ class EW2Listeners : InteractionListener { // else nothing else -> arrayOf() - } + }.map { toInstance(player, it) }.toTypedArray() if (path.isNotEmpty()) { animate(player, Animations.HUMAN_PULL_LEVER_4909) @@ -543,26 +558,27 @@ class EW2Listeners : InteractionListener { } when(node.id) { - 4914 -> { + NPCs.JIG_CART_4914 -> { addItemOrDrop(player, Items.ELEMENTAL_METAL_2893) sendItemDialogue(player, Items.ELEMENTAL_METAL_2893, "You take the elemental metal.") animate(player, 537) setVarbit(player, EW2JigCartVarbit, 0, true) } - 4915 -> { + NPCs.JIG_CART_4915 -> { sendMessage(player, "The bar is too hot to touch.") } - 4916 -> { + NPCs.JIG_CART_4916 -> { sendMessage(player, "The bar is too hot to touch.") } - 4917 -> { + NPCs.JIG_CART_4917 -> { sendMessage(player, "You should finish the process before taking the bar.") } - 4918 -> { + NPCs.JIG_CART_4918 -> { addItemOrDrop(player, Items.PRIMED_BAR_9727) sendItemDialogue(player, Items.PRIMED_BAR_9727, "You take the primed bar.") - animate(player, 537) setVarbit(player, EW2JigCartVarbit, 0, true) + + // advance quest stage if(getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) <= 6) { setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II, 7) setVarbit(player, EW2StageVarbit, 7, true) @@ -591,7 +607,6 @@ class EW2Listeners : InteractionListener { } setVarbit(player, EW2JigCartVarbit, 1, true) - animate(player, 537) playAudio(player, Sounds.EW2_PLACE_BAR_3177) syncCart(player) removeItem(player, used) @@ -601,10 +616,10 @@ class EW2Listeners : InteractionListener { return@onUseWith true } - /**------------------------ - * SECTION 1 - THE CRANE - ** ------------------------ - */ + /** ------------------------ * + * SECTION 1 - THE CRANE + * ------------------------- */ + // requires the elemental claw to be smithed and attached to the crane // crane schematic @@ -648,13 +663,11 @@ class EW2Listeners : InteractionListener { // pick up setVarbit(player, EW2CraneStateVarbit, cartState + 1) setVarbit(player, EW2JigCartVarbit, 0) - sendMessage(player, "The crane claw picks up the bar from the cart.") syncCart(player) } else if ((craneMat == 2 || craneMat == 3) && cartState == 0 && cartPos == 0) { // drop off setVarbit(player, EW2JigCartVarbit, craneMat - 1) setVarbit(player, EW2CraneStateVarbit, 1) - sendMessage(player, "You lower the bar onto the jig cart.") syncCart(player) } } @@ -691,10 +704,10 @@ class EW2Listeners : InteractionListener { return@on true } - /**------------------------ - * SECTION 2 - THE ANVIL - ** ------------------------ - */ + /** ------------------------ * + * SECTION 2 - THE ANVIL + * ------------------------- */ + // requires the junction box interface to be fixed // opens the junction box @@ -714,7 +727,7 @@ class EW2Listeners : InteractionListener { if (cartPos == 1) { when (cartState) { 0 -> { // empty. nothing happens. - sendDialogue(player, "You pull the lever and the press operates, but there is nothing on the cart to flatten.") + sendDialogue(player, "You pull the lever and the press responds. The hardy comes down, stopping inches from the jig, then returns to its original position.") playAudio(player, Sounds.EW2_PRESS_BAR_3180) animate(player, Animations.HUMAN_PULL_LEVER_4909) } @@ -748,17 +761,17 @@ class EW2Listeners : InteractionListener { } } else { // The junction box is not configured correctly - sendDialogue(player, "You pull the lever but nothing happens.") + sendDialogue(player, "You pull the lever but nothing happens. Maybe the pipes are connected the wrong way round.") animate(player, Animations.HUMAN_PULL_LEVER_4909) } return@on true } - /**------------------------ - * SECTION 3 - WATER - ** ------------------------ - */ + /** ------------------------ * + * SECTION 3 - WATER + * ------------------------- */ + // requires the pipe to be replaced // pipe crate @@ -943,7 +956,7 @@ class EW2Listeners : InteractionListener { setVarbit(player, EW2ValveEastVarbit, 1, true) runTask(player, 3) { syncTank(player) - sendDialogue(player, "you open the valve") + sendDialogue(player, "You open the valve.") playAudio(player, Sounds.EW2_WATER_VALVE_3185) } // if open, close the valve @@ -951,19 +964,25 @@ class EW2Listeners : InteractionListener { setVarbit(player, EW2ValveEastVarbit, 0, true) runTask(player, 3) { syncTank(player) - sendDialogue(player, "you close the valve") + sendDialogue(player, "You close the valve.") playAudio(player, Sounds.EW2_WATER_VALVE_3185) } } return@on true } - /**------------------------ - * SECTION 4 - FAN - ** ------------------------ - */ + /** ------------------------ * + * SECTION 4 - FAN + * ------------------------- */ + // requires the cogs to be placed + // this crate authentically has its own special search message! + on(intArrayOf(Scenery.CRATE_18507, Scenery.CRATES_18508), IntType.SCENERY, "search") { player, _ -> + sendMessage(player, "You don't find anything interesting.") + return@on true + } + // small cog crate on(Scenery.CRATE_18618, IntType.SCENERY, "search") { player, _ -> if (!inInventory(player, Items.SMALL_COG_9726)) { @@ -1013,9 +1032,9 @@ class EW2Listeners : InteractionListener { // Pin 2 uses: 18667, 18668, 18669 // Pin 3 uses: 18670, 18671, 18672 when (node.id) { - 18673, 18674, 18675 -> setVarbit(player, EW2CogVarbit1, 0, true) - 18667, 18668, 18669 -> setVarbit(player, EW2CogVarbit2, 0, true) - 18670, 18671, 18672 -> setVarbit(player, EW2CogVarbit3, 0, true) + Scenery.COG_18673, Scenery.COG_18674, Scenery.COG_18675 -> setVarbit(player, EW2CogVarbit1, 0, true) + Scenery.COG_18667, Scenery.COG_18668, Scenery.COG_18669 -> setVarbit(player, EW2CogVarbit2, 0, true) + Scenery.COG_18670, Scenery.COG_18671, Scenery.COG_18672 -> setVarbit(player, EW2CogVarbit3, 0, true) } addItemOrDrop(player, cogToGive) @@ -1038,14 +1057,14 @@ class EW2Listeners : InteractionListener { } Items.MEDIUM_COG_9725 -> { setVarbit(player, EW2CogVarbit1, 2, true) - sendItemDialogue(player, item, "You put the medium cog onto the shaft.") + sendItemDialogue(player, item, "You put the medium-sized cog onto the shaft.") animate(player, 537) playAudio(player, Sounds.EW2_PLACE_COG_3178) removeItem(player, item) } Items.LARGE_COG_9724 -> { setVarbit(player, EW2CogVarbit1, 3, true) - sendItemDialogue(player, item, "You put the large cog onto the shaft.") + sendItemDialogue(player, item, "You put the big cog onto the shaft.") animate(player, 537) playAudio(player, Sounds.EW2_PLACE_COG_3178) removeItem(player, item) @@ -1106,7 +1125,7 @@ class EW2Listeners : InteractionListener { return@onUseWith true } - // fan lever. Authentically this check should allow the machine to start and the fan to either not start, start backwards, or start correctly, but I'm simplifying it for my sanity. + // fan lever. todo Authentically this check should allow the machine to start and the fan to either not start, start backwards, or start correctly, but I'm simplifying it for my sanity. on(Scenery.AN_OLD_LEVER_18663, IntType.SCENERY, "pull") { player, _ -> animate(player, Animations.HUMAN_PULL_LEVER_4909) @@ -1147,17 +1166,15 @@ class EW2Listeners : InteractionListener { return@on true } - /**------------------------ - * SECTION 5 - MIND IMBUE - ** ------------------------ - */ + /** ------------------------ * + * SECTION 5 - MIND IMBUE + * ------------------------- */ // use bar with extractor gun onUseWith(IntType.SCENERY, intArrayOf(Items.PRIMED_BAR_9727), Scenery.EXTRACTOR_GUN_18693) { player, item, _ -> if(removeItem(player, item)) { setVarbit(player, EW2ExtractorGun, 1, true) - animate(player, 537) - sendDialogue(player, "You place the primed bar under the device.") + sendItemDialogue(player, Items.PRIMED_BAR_9727, "You place the primed bar under the device.") if(getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) <= 8) { setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II, 9) setVarbit(player, EW2StageVarbit, 9, true) @@ -1169,18 +1186,16 @@ class EW2Listeners : InteractionListener { // take primed bar on(intArrayOf(Scenery.EXTRACTOR_GUN_18694), IntType.SCENERY, "take-from") { player, _ -> addItemOrDrop(player, Items.PRIMED_BAR_9727) - animate(player, 537) setVarbit(player, EW2ExtractorGun, 0, true) - sendDialogue(player, "You take the primed bar.") + sendItemDialogue(player, Items.PRIMED_BAR_9727, "You take the primed bar.") return@on true } // take mind bar on(intArrayOf(Scenery.EXTRACTOR_GUN_18695), IntType.SCENERY, "take-from") { player, _ -> addItemOrDrop(player, Items.ELEMENTAL_MIND_BAR_9728) - animate(player, 537) setVarbit(player, EW2ExtractorGun, 0, true) - sendDialogue(player, "You take the elemental mind bar.") + sendItemDialogue(player, Items.ELEMENTAL_MIND_BAR_9728, "You take the elemental mind bar.") return@on true } @@ -1198,12 +1213,12 @@ class EW2Listeners : InteractionListener { runTask(player) { forceMove( player, - Location.create(1961, 5150, 0), - Location.create(1962, 5150, 0), + toInstance(player, Location.create(1961, 5150, 0)), + toInstance(player, Location.create(1962, 5150, 0)), 0, 0 ) - visualize(player, Animations.HUMAN_USE_EXTRACTOR_HAT_CHAIR_4884, goodHat) + visualize(player, goodHatAnim, goodHatGfx) playAudio(player, Sounds.EW2_EXTRACTOR_OPERATION_3166) sendGraphics(809, Location.create(1962, 5148, 0)) setVarbit(player, EW2ExtractorGun, 2, true) @@ -1216,8 +1231,8 @@ class EW2Listeners : InteractionListener { } forceMove( player, - Location.create(1962, 5150, 0), - Location.create(1961, 5150, 0), + toInstance(player,Location.create(1962, 5150, 0)), + toInstance(player,Location.create(1961, 5150, 0)), 200, 2 ) @@ -1226,89 +1241,44 @@ class EW2Listeners : InteractionListener { runTask(player) { // 50 client cycles = 1 second forceMove( player, - Location.create(1961, 5150, 0), - Location.create(1962, 5150, 0), + toInstance(player,Location.create(1961, 5150, 0)), + toInstance(player,Location.create(1962, 5150, 0)), 0, 0 ) - visualize(player, Animations.HUMAN_IMPROPERLY_USE_EXTRACTOR_HAT_CHAIR_4885, badHat) + visualize(player, badHatAnim, badHatGfx) playAudio(player, Sounds.EW2_EXTRACTOR_ZAP_3173) impact(player, 4) sendMessage(player, "The extractor hat shocks you!.") } forceMove( player, - Location.create(1962, 5150, 0), - Location.create(1961, 5150, 0), + toInstance(player,Location.create(1962, 5150, 0)), + toInstance(player,Location.create(1961, 5150, 0)), 350, 2 ) } return@on true } - - /**------------------------ - * EXTRA - ** ------------------------ - */ - - // equip graphics for helmets and shields. the shields should not technically show until the graphic finishes, but I can't find an easy way to do that. - onEquip(Items.MIND_HELMET_9733) { player, _ -> - visualize(player, -1, mHelmEquip) - return@onEquip true - } - - onEquip(Items.MIND_SHIELD_9731) { player, node -> - val currentShield = player.equipment[EquipmentContainer.SLOT_SHIELD] - if (currentShield != null) { - player.equipment.remove(currentShield) - } - visualize(player, -1, Graphics(mShieldEquip,95)) - runTask(player, 2) { - val item = node.asItem() - player.equipment.add(item, item.slot, true, true) - player.inventory.add(currentShield) - } - return@onEquip false - } - - onEquip(Items.ELEMENTAL_HELMET_9729) { player, _ -> - visualize(player, -1, eHelmEquip) - return@onEquip true - } - - onEquip(Items.ELEMENTAL_SHIELD_2890) { player, node -> - val currentShield = player.equipment[EquipmentContainer.SLOT_SHIELD] - if (currentShield != null) { - player.equipment.remove(currentShield) - } - visualize(player, -1, Graphics(eShieldEquip,95)) - runTask(player, 2) { - val item = node.asItem() - player.equipment.add(item, item.slot, true, true) - player.inventory.add(currentShield) - } - return@onEquip false - } - } // fixes to get right interact locations override fun defineDestinationOverrides() { - setDest(IntType.SCENERY, intArrayOf(Scenery.OLD_CRANE_18636), "use") { _, _ -> - return@setDest Location.create(1954, 5148, 2) + setDest(IntType.SCENERY, intArrayOf(Scenery.OLD_CRANE_18636), "use") { entity, _ -> + return@setDest toInstance(entity as Player,Location.create(1954, 5148, 2)) } - setDest(IntType.NPC, intArrayOf(NPCs.JIG_CART_4913), "use") { _, _ -> - return@setDest Location.create(1954, 5148, 2) + setDest(IntType.NPC, intArrayOf(NPCs.JIG_CART_4913), "use") { entity, _ -> + return@setDest toInstance(entity as Player,Location.create(1954, 5148, 2)) } - setDest(IntType.NPC, intArrayOf(NPCs.JIG_CART_4914, NPCs.JIG_CART_4915, NPCs.JIG_CART_4916, NPCs.JIG_CART_4917, NPCs.JIG_CART_4918), "take-from") { _, _ -> - return@setDest Location.create(1954, 5148, 2) + setDest(IntType.NPC, intArrayOf(NPCs.JIG_CART_4914, NPCs.JIG_CART_4915, NPCs.JIG_CART_4916, NPCs.JIG_CART_4917, NPCs.JIG_CART_4918), "take-from") { entity, _ -> + return@setDest toInstance(entity as Player,Location.create(1954, 5148, 2)) } - setDest(IntType.SCENERY, intArrayOf(Scenery.PIPING_18650), "use") { _, _ -> - return@setDest Location.create(1953, 5167, 3) + setDest(IntType.SCENERY, intArrayOf(Scenery.PIPING_18650), "use") { entity, _ -> + return@setDest toInstance(entity as Player,Location.create(1953, 5167, 3)) } // the double stacked pins and the cogs on them. @@ -1316,8 +1286,8 @@ class EW2Listeners : InteractionListener { Scenery.PIN_18664, Scenery.PIN_18665, Scenery.COG_18673, Scenery.COG_18674, Scenery.COG_18675, Scenery.COG_18667, Scenery.COG_18668, Scenery.COG_18669 - ), "use","take") { _, _ -> - return@setDest Location.create(1958, 5157, 2) + ), "use","take") { entity, _ -> + return@setDest toInstance(entity as Player,Location.create(1958, 5157, 2)) } } } \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Zone.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Zone.kt index fd3dae6cf..349bd3b43 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Zone.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Zone.kt @@ -1,82 +1,33 @@ package content.region.kandarin.seers.quest.elementalworkshop2 import content.data.Quests -import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncCart -import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncCrane -import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncFan -import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncSteamPress -import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncTank -import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.syncTankDoor -import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.toInstance import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.EW2StageVarbit import core.api.* -import core.game.interaction.QueueStrength +import core.game.activity.ActivityManager import core.game.node.entity.Entity import core.game.node.entity.player.Player -import core.game.world.map.Location -import core.game.world.map.build.DynamicRegion import core.game.world.map.zone.ZoneBorders -import org.rs09.consts.Scenery /** - * This updates the Elemental Workshop II state whenever the player enters, - * keeping the equipment state synced to the saved varbits. The workshop - * area is a dynamic region, per OSRS wiki. + * Handles an instanced Elemental Workshop II session. */ class EW2Zone : MapArea { - override fun defineAreaBorders(): Array { return arrayOf(ZoneBorders(1920,5120,1983,5183)) } override fun areaEnter(entity: Entity) { - if(entity is Player) { + if (entity is Player) { - val baseRegion = 7760 + // start the instanced workshop region + ActivityManager.start(entity, "EW2 Workshop", false) - // create the dynamic region - val dynamicRegion = DynamicRegion.create(baseRegion) - - // save the exit location back to the real world surface - setAttribute(entity, "ew2_exit_location", Location.create(2713, 3484, 0)) - - // find the player's offset from the base coordinates - val offsetX = entity.location.x - ((baseRegion shr 8) shl 6) - val offsetY = entity.location.y - ((baseRegion and 0xFF) shl 6) - - // apply offset to the dynamic region base - val destX = dynamicRegion.borders.southWestX + offsetX - val destY = dynamicRegion.borders.southWestY + offsetY - - // teleport the player into the instance - teleport(entity, Location.create(destX, destY, entity.location.z)) - - // save the dynamic region coords to the player to reference later - setAttribute(entity, ElementalWorkshop2.coordsX, dynamicRegion.borders.southWestX) - setAttribute(entity, ElementalWorkshop2.coordsY, dynamicRegion.borders.southWestY) - - // advance quest - if(getQuestStage(entity, Quests.ELEMENTAL_WORKSHOP_II) <= 5) { + // advance quest stage + if (getQuestStage(entity, Quests.ELEMENTAL_WORKSHOP_II) <= 5) { setQuestStage(entity, Quests.ELEMENTAL_WORKSHOP_II, 6) setVarbit(entity, EW2StageVarbit, 6, true) } - - // sync the workshop state - queueScript(entity, 2, QueueStrength.SOFT) { _ -> - - // add this door because I can't interact without parlour chairs todo test removing this on test - // toInstance(entity, Location.create(1953, 5162, 2)) - addScenery(core.game.node.scenery.Scenery(Scenery.DOOR_18651, location(33,42,2), 1)) - - syncCrane(entity) - syncCart(entity) - syncSteamPress(entity) - syncTank(entity) - syncTankDoor(entity) - syncFan(entity) - return@queueScript stopExecuting(entity) - } } } } \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/ElementalWorkshop2.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/ElementalWorkshop2.kt index f5dc32f0a..35c0fc0cb 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/ElementalWorkshop2.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/ElementalWorkshop2.kt @@ -6,7 +6,6 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.Quest import core.game.node.entity.skill.Skills import core.plugin.Initializable -import org.rs09.consts.Graphics import org.rs09.consts.Items /** @@ -57,14 +56,6 @@ class ElementalWorkshop2 : Quest(Quests.ELEMENTAL_WORKSHOP_II, 53, 52, 1, EW2Sta const val attrFlattened = "/save:quest:ew2-flattened" //true after you flatten the hot bar const val attrWatered = "/save:quest:ew2-watered" //true after you cool the bar in the tank const val attrWinded = "/save:quest:ew2-winded" //true after you cool the bar in the wind tunnel - - // defining these graphics here because the graphics consts conflicts with the graphics object in the listeners - const val goodHat = Graphics.EXTRACTOR_HAT_CHAIR_807 - const val badHat = Graphics.IMPROPERLY_EXTRACTOR_HAT_CHAIR_808 - const val mShieldEquip = 809 - const val mHelmEquip = 810 - const val eShieldEquip = 244 - const val eHelmEquip = 811 } /** @@ -273,12 +264,12 @@ class ElementalWorkshop2 : Quest(Quests.ELEMENTAL_WORKSHOP_II, 53, 52, 1, EW2Sta override fun finish(player: Player) { var ln = 10 super.finish(player) - player.packetDispatch.sendString("You have completed the Elemental Workshop II quest!", 277, 4) + player.packetDispatch.sendString("You have completed the Elemental Workshop II Quest!", 277, 4) player.packetDispatch.sendItemZoomOnInterface(Items.MIND_HELMET_9733, 230, 277, 5) drawReward(player, "1 Quest Point", ln++) - drawReward(player, "7,500 Smithing XP,", ln++) - drawReward(player, "7,500 Crafting XP,", ln++) + drawReward(player, "7500 Smithing XP,", ln++) + drawReward(player, "7500 Crafting XP,", ln++) drawReward(player, "Ability to make and equip", ln++) drawReward(player, "elemental mind equipment", ln) diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/JigCartNPC.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/JigCartNPC.kt index d83a97a3b..22c3bd1cc 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/JigCartNPC.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/JigCartNPC.kt @@ -1,43 +1,27 @@ package content.region.kandarin.seers.quest.elementalworkshop2 -import core.game.node.entity.npc.AbstractNPC +import content.region.kandarin.seers.quest.elementalworkshop2.EW2Listeners.Companion.jigCartStates import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPCBehavior -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.game.world.map.build.ChunkFlags import core.game.world.map.path.ClipMaskSupplier import org.rs09.consts.NPCs /** * Jig cart in Elemental Workshop II - * The only reason I have this here is to set a custom clipping mask to allow the cart to walk over the track scenery. - * Right now the walking part is broken, so the cart just teleports. */ -class JigCartNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { - - override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { - return JigCartNPC(id, location) - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.JIG_CART_4913, NPCs.JIG_CART_4914, NPCs.JIG_CART_4915, NPCs.JIG_CART_4916, NPCs.JIG_CART_4917, NPCs.JIG_CART_4918,) - } -} - -class JigCartNPCBehavior : NPCBehavior(NPCs.JIG_CART_4913, NPCs.JIG_CART_4914, NPCs.JIG_CART_4915, NPCs.JIG_CART_4916, NPCs.JIG_CART_4917, NPCs.JIG_CART_4918,) { +// the only reason I have this here is to set a custom clipping mask to allow the cart to walk over the track scenery. +// right now the walking part is broken, so the cart just teleports. +class JigCartNPCBehavior : NPCBehavior(*jigCartStates) { override fun getClippingSupplier (self: NPC) : ClipMaskSupplier { return CartClipper } } - // this clip mask lets the jig cart walk through scenery object CartClipper : ClipMaskSupplier { override fun getClippingFlag (z: Int, x: Int, y: Int) : Int { - val flag = RegionManager.getClippingFlag(z, x, y) - return flag and (ChunkFlags.SOLID_TILE.inv()) and (ChunkFlags.TILE_OBJECT.inv()) and (ChunkFlags.OBJ_10.inv()) + return 0 } } \ No newline at end of file