From e2baea400d0654f72a2d6b410b7df98475e133c3 Mon Sep 17 00:00:00 2001 From: aidan Date: Thu, 9 Jul 2026 14:14:40 -0500 Subject: [PATCH] these animations are breaking my mind --- .../quest/elementalworkshop2/EW2Listeners.kt | 398 ++++++++++++++---- .../quest/elementalworkshop2/EW2Session.kt | 2 + .../JunctionBoxInterface.kt | 22 +- .../system/command/sets/SpawnCommandSet.kt | 52 +++ 4 files changed, 375 insertions(+), 99 deletions(-) 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 be2c29859..7a832974c 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 @@ -26,7 +26,6 @@ import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2 import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.attrWatered import content.region.kandarin.seers.quest.elementalworkshop2.ElementalWorkshop2.Companion.attrWinded import core.api.* -import core.game.activity.ActivityPlugin import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.interaction.QueueStrength @@ -34,14 +33,14 @@ import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import core.game.world.map.Location +import core.game.world.update.flag.context.Animation import org.rs09.consts.* class EW2Listeners : InteractionListener { - // values from 530_synth_debug_names.txt that are still unused - //val ew2_dial_spin = 2326 - //val ew2_ambient_loop = 3149 - //val ew2_machine_loop = 3150 + // Sounds I still haven't used but should be part of quest somewhere + // Sounds.EW2_MACHINE_LOOP_3150 + // Sounds.EW2_AMBIENT_LOOP_3149 /** * Quest structure and testing notes: @@ -71,22 +70,46 @@ class EW2Listeners : InteractionListener { companion object { // graphics - const val goodHatGfx = Graphics.EXTRACTOR_HAT_CHAIR_807 - const val badHatGfx = Graphics.IMPROPERLY_EXTRACTOR_HAT_CHAIR_808 + const val EXTRACTOR_GOOD_GFX = Graphics.EXTRACTOR_HAT_CHAIR_807 + const val EXTRACTOR_BAD_GFX = 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 + const val DOOR_OPEN = 4871 // 4875 is another open + const val DOOR_CLOSE = 4872 // 4876 is another open + const val DOOR_EXTEND = 4873 // 4877 is another extend + const val DOOR_RETRACT = 4874 // 4878 is another retract + // 4879 + // 4880 + // 4881 + // 4882 + // 4883 + const val EXTRACTOR_GOOD = Animations.HUMAN_USE_EXTRACTOR_HAT_CHAIR_4884 + const val EXTRACTOR_BAD = Animations.HUMAN_IMPROPERLY_USE_EXTRACTOR_HAT_CHAIR_4885 + // 4886 + const val CRANE_LOWER_CART = 4887 + const val CRANE_RAISE_CART = 4888 + const val CRANE_LOWER_LAVA = 4889 + const val CRANE_RAISE_LAVA = 4890 + // 4891 (another pivot anim, unused) + const val CRANE_PIVOT = 4892 + // 4893 + // 4894 + // 4895 + const val COG_REV = 4896 + // 4897 + const val FAN_SPIN = 4898 + // 4899 + // 4900 + // 4901 + const val TANK_FILL = 4902 + const val TANK_DRAIN = 4903 + const val COG_FWD = 4906 /** * 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, * then synced in the various sync() functions. The whole workshop is synced using the - * EW2Zone enter trigger, so when the player comes back, the workshop is where they left it. + * EW2Session start trigger, so when the player comes back, the workshop is where they left it. */ // various states the crane can be in. @@ -157,31 +180,79 @@ class EW2Listeners : InteractionListener { // position the crane based on varbit states fun syncCrane(player: Player) { val mat = getVarbit(player, EW2CraneStateVarbit) - val pos = getVarbit(player, EW2CranePosVarbit) - val index = (mat * 4) + pos - val newId = craneStates[index] + val pos = getVarbit(player, EW2CranePosVarbit) // 0: South Down, 1: South Up, 2: North Down, 3: North Up + val targetIndex = (mat * 4) + pos + val newId = craneStates[targetIndex] + val craneLoc = toInstance(player, Location.create(1952, 5143, 2)) val currentObject = getScenery(craneLoc) ?: return if (!craneStates.contains(currentObject.id)) return if (currentObject.id == newId) return + // get the crane's current position + val currentIndex = craneStates.indexOf(currentObject.id) + val currentPos = if (currentIndex != -1) currentIndex % 4 else pos + + // select the anim and sound + var animId = -1 + var soundId = -1 + if (currentPos != pos) { + when { + // lowering into lava + currentPos == 1 && pos == 0 -> { + animId = CRANE_LOWER_LAVA + soundId = Sounds.EW2_CRANE_LOWER_3169 + } + // raising from lava + currentPos == 0 && pos == 1 -> { + animId = CRANE_RAISE_LAVA + soundId = Sounds.EW2_CRANE_LOWER_3169 // I can't find raise sound + } + // lowering to cart + currentPos == 3 && pos == 2 -> { + animId = CRANE_LOWER_CART + soundId = Sounds.EW2_CRANE_LOWER_3169 + } + // raising from cart + currentPos == 2 && pos == 3 -> { + animId = CRANE_RAISE_CART + soundId = Sounds.EW2_CRANE_LOWER_3169 + } + // pivot to cart + currentPos == 1 && pos == 3 -> { + animId = CRANE_PIVOT + soundId = Sounds.EW2_CRANE_TURN_3170 + } + // pivot to lava + currentPos == 3 && pos == 1 -> { + animId = CRANE_PIVOT + soundId = Sounds.EW2_CRANE_TURN_3170 + } + } + } + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> when (stage) { + // animate the scenery 0 -> { - animateScenery(player, currentObject, 4890) // replace with confirmed anim ID - return@queueScript delayScript(player, 4) // adjust to match anim duration + if (animId != -1) { + val anim = Animation(animId) + animateScenery(player, currentObject, animId) + playAudio(player, soundId) + return@queueScript delayScript(player, anim.duration) + } else return@queueScript delayScript(player, 1) } + // update the scenery 1 -> { replaceScenery(currentObject, newId, -1) - playAudio(player, Sounds.EW2_CRANE_TURN_3170) + syncCart(player) + + // advance quest stage if (newId == Scenery.OLD_CRANE_18634) { - if (!getAttribute(player, attrDipped, false) && getQuestStage( - player, - Quests.ELEMENTAL_WORKSHOP_II - ) <= 6 - ) { + if (!getAttribute(player, attrDipped, false) && + getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) <= 6) { setAttribute(player, attrDipped, true) } } @@ -191,6 +262,7 @@ class EW2Listeners : InteractionListener { else -> return@queueScript stopExecuting(player) } } + syncCart(player) } // update the jig cart based on varbit status @@ -211,7 +283,7 @@ class EW2Listeners : InteractionListener { } // failsafe to reset the cart where it's supposed to be. right now cart just teleports so this shouldn't trigger if (!existingCart.walkingQueue.isMoving && existingCart.location != targetLocation) { - existingCart.teleport(targetLocation) + teleport(existingCart, targetLocation) } // if cart is inside water tank, clear it if (tankState == 3 || tankState == 4 || tankState == 6 || tankState == 7) { @@ -237,12 +309,24 @@ class EW2Listeners : InteractionListener { val medCogs = intArrayOf(Scenery.COG_18674, Scenery.COG_18668, Scenery.COG_18671) // pin 1, pin 2, pin 3 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, could maybe set the anims here) + // spins the cogs fun syncCogs(player: Player) { + val lever = getVarbit(player, EW2FanLeverVarbit) + val cogs1 = getScenery(toInstance(player,Location.create(1959, 5157, 2))) + val cogs2 = getScenery(toInstance(player,Location.create(1959, 5156, 2))) + if (cogs1 != null && cogs2 != null) { + if (lever == 1) { + // spin cogs + animateScenery(cogs1, COG_FWD) + animateScenery(cogs2, COG_FWD) + } else { + // stop cogs + animateScenery(cogs1, -1) + animateScenery(cogs2, -1) + } + } } - */ // visualizes the steam press. right now all it does is spawn the hammer part. fun syncSteamPress(player: Player) { @@ -255,31 +339,102 @@ class EW2Listeners : InteractionListener { val waterIn = getVarbit(player, EW2ValveWestVarbit) // 0 if valve closed, 1 if open val waterOut = getVarbit(player, EW2ValveEastVarbit) // 0 if valve closed, 1 if open val cart = getVarbit(player, EW2TankDoorStateVarbit) // whether the cart is in the tank or not + val tankLoc = toInstance(player, location(1951, 5164, 2)) + val currentObject = getScenery(tankLoc) - if(waterIn == 0 && waterOut == 0) { // both valves closed, nothing happens + if (waterIn == 0 && waterOut == 0) { + // both valves closed, nothing happens return - } else if(waterIn == 0 && waterOut == 1) { // drain is open, remove all added scenery - 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 - 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) - setVarbit(player, EW2JigCartVarbit, 4, true) - syncTankDoor(player) - playAudio(player, Sounds.EW2_COOL_BAR_3168) - if(!getAttribute(player, attrWatered, false) && getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) <= 6) { - setAttribute(player, attrWatered, true) + + } else if (waterIn == 0 && waterOut == 1) { + // drain is open, empty the tank + if (currentObject != null && currentObject.id == Scenery.WATER_TANK_18661) { + // tank is full: play drain anim, then remove + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + animateScenery(player, currentObject, TANK_DRAIN) + playAudio(player, Sounds.EW2_WATER_RACK_3184) + return@queueScript delayScript(player, Animation(TANK_DRAIN).duration) + } + 1 -> { + removeScenery(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18661, tankLoc)) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } else if (currentObject != null && currentObject.id == Scenery.WATER_TANK_18660) { + // tank is damp: just remove the scenery + removeScenery(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18660, tankLoc)) + playAudio(player, Sounds.EW2_WATER_RACK_3184) + } + + } else if (waterIn == 1 && waterOut == 0) { + // inlet is open, fill tank + if (currentObject?.id == Scenery.WATER_TANK_18661) return // already full + + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + // add damp tank so we have something to animate + var animTarget = currentObject + if (animTarget == null || animTarget.id != Scenery.WATER_TANK_18660) { + animTarget = core.game.node.scenery.Scenery(Scenery.WATER_TANK_18660, tankLoc) + addScenery(animTarget) + } + + animateScenery(player, animTarget, TANK_FILL) + playAudio(player, Sounds.EW2_WATER_FLOW_3183) + return@queueScript delayScript(player, Animation(TANK_FILL).duration) + } + 1 -> { + // swap damp for full tank + val existing = getScenery(tankLoc) + if (existing != null) removeScenery(core.game.node.scenery.Scenery(existing.id, tankLoc)) + addScenery(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18661, tankLoc)) + + // cooling the hot bar + if (cart == 3) { + setVarbit(player, EW2TankDoorStateVarbit, 6, true) + setVarbit(player, EW2JigCartVarbit, 4, true) + syncTankDoor(player) + playAudio(player, Sounds.EW2_COOL_BAR_3168) + + if (!getAttribute(player, attrWatered, false) && getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) <= 6) { + setAttribute(player, attrWatered, true) + } + } + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) } } - return - } else if(waterIn == 1 && waterOut == 1) { // inlet and drain are open, tank is damp - 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 + + } else if (waterIn == 1 && waterOut == 1) { + // inlet and drain are open, tank is damp + if (currentObject?.id == Scenery.WATER_TANK_18661) { + // tank is full: play drain anim, then swap to damp + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + animateScenery(player, currentObject, TANK_DRAIN) + playAudio(player, Sounds.EW2_WATER_FLOW_3183) + return@queueScript delayScript(player, Animation(TANK_DRAIN).duration) + } + 1 -> { + removeScenery(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18661, tankLoc)) + addScenery(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18660, tankLoc)) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } else if (currentObject?.id != Scenery.WATER_TANK_18660) { + // tank is empty: add the damp scenery + addScenery(core.game.node.scenery.Scenery(Scenery.WATER_TANK_18660, tankLoc)) + playAudio(player, Sounds.EW2_WATER_FLOW_3183) + } } } @@ -287,14 +442,64 @@ class EW2Listeners : InteractionListener { fun syncTankDoor(player: Player) { val state = getVarbit(player, EW2TankDoorStateVarbit) val doorLoc = toInstance(player, Location.create(1953, 5162, 2)) - val currentObject = getScenery(doorLoc) + val currentObject = getScenery(doorLoc) ?: return val targetId = tankDoorStates[state] - // remove old scenery then add the new one - if(currentObject != null && currentObject.id != targetId) { - // 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)) + if (currentObject.id == targetId) return + + var animId = -1 + var soundId = -1 + + when { + // door opening/closing + (currentObject.id == Scenery.DOOR_18651 && targetId == Scenery.DOOR_18652) || + (currentObject.id == Scenery.DOOR_18654 && targetId == Scenery.DOOR_18655) || + (currentObject.id == Scenery.DOOR_18657 && targetId == Scenery.DOOR_18658) -> { + animId = DOOR_OPEN + soundId = Sounds.EW2_DIAL_SPIN_2326 + } + (currentObject.id == Scenery.DOOR_18652 && targetId == Scenery.DOOR_18651) || + (currentObject.id == Scenery.DOOR_18655 && targetId == Scenery.DOOR_18654) || + (currentObject.id == Scenery.DOOR_18658 && targetId == Scenery.DOOR_18657) -> { + animId = DOOR_CLOSE + soundId = Sounds.EW2_DIAL_SPIN_2326 + } + // rack extending/retracting + (currentObject.id == Scenery.DOOR_18652 && targetId == Scenery.DOOR_18653) || + (currentObject.id == Scenery.DOOR_18655 && targetId == Scenery.DOOR_18656) || + (currentObject.id == Scenery.DOOR_18658 && targetId == Scenery.DOOR_18659) -> { + animId = DOOR_EXTEND + soundId = Sounds.EW2_SCREW_TURN_3182 + } + (currentObject.id == Scenery.DOOR_18653 && targetId == Scenery.DOOR_18652) || + (currentObject.id == Scenery.DOOR_18656 && targetId == Scenery.DOOR_18655) || + (currentObject.id == Scenery.DOOR_18659 && targetId == Scenery.DOOR_18658) -> { + animId = DOOR_RETRACT + soundId = Sounds.EW2_SCREW_TURN_3182 + } + } + + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + if (animId != -1) { + val anim = Animation(animId) + animateScenery(player, currentObject, animId) + if (soundId != -1) playAudio(player, soundId) + return@queueScript delayScript(player, anim.duration) + } + return@queueScript delayScript(player, 1) + } + 1 -> { + // replaceScenery doesn't work. why? idfk + removeScenery(core.game.node.scenery.Scenery(currentObject.id, doorLoc, 1)) + addScenery(core.game.node.scenery.Scenery(targetId, doorLoc, 1)) + syncCart(player) + + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } } } @@ -302,21 +507,19 @@ class EW2Listeners : InteractionListener { fun syncFan(player: Player) { val fanMachine = getVarbit(player, EW2FanLeverVarbit) if (fanMachine == 1) { - setVarbit(player, EW2FanStateVarbit, 1) + // spins the fan + setVarbit(player, EW2FanStateVarbit, 1, true) } else { - setVarbit(player, EW2FanStateVarbit, 0) + setVarbit(player, EW2FanStateVarbit, 0, true) } } // 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 session = player.getAttribute("ew2-session", null) ?: return staticLoc val offsetX = staticLoc.x - 1920 val offsetY = staticLoc.y - 5120 - - return activity.base.transform(offsetX, offsetY, staticLoc.z) + return session.base.transform(offsetX, offsetY, staticLoc.z) } } @@ -405,7 +608,12 @@ class EW2Listeners : InteractionListener { // unlocked center hatch down to EW2 on(Scenery.HATCH_18596, IntType.SCENERY, "climb-down") { player, _ -> sendMessage(player, "You climb down the stairs.") - teleport(player, location(1955, 5155, 2)) + + // this is the non-instanced area + //teleport(player, location(1955, 5155, 2)) + + // start the instanced workshop session + EW2Session(player).start() if(getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) == 4) { setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II, 5) @@ -610,6 +818,13 @@ class EW2Listeners : InteractionListener { playAudio(player, Sounds.EW2_PLACE_BAR_3177) syncCart(player) removeItem(player, used) + + // advance quest stage + if (getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) <= 5) { + setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II, 6) + setVarbit(player, EW2StageVarbit, 6, true) + } + if(!getAttribute(player, attrJigged, false) && getQuestStage(player, Quests.ELEMENTAL_WORKSHOP_II) <= 6) { setAttribute(player, attrJigged, true) } @@ -661,14 +876,12 @@ class EW2Listeners : InteractionListener { if (currentPos == 3 && nextPos == 2) { if (craneMat == 1 && (cartState == 1 || cartState == 2) && cartPos == 0) { // pick up - setVarbit(player, EW2CraneStateVarbit, cartState + 1) - setVarbit(player, EW2JigCartVarbit, 0) - syncCart(player) + setVarbit(player, EW2CraneStateVarbit, cartState + 1, true) + setVarbit(player, EW2JigCartVarbit, 0, true) } else if ((craneMat == 2 || craneMat == 3) && cartState == 0 && cartPos == 0) { // drop off - setVarbit(player, EW2JigCartVarbit, craneMat - 1) - setVarbit(player, EW2CraneStateVarbit, 1) - syncCart(player) + setVarbit(player, EW2JigCartVarbit, craneMat - 1, true) + setVarbit(player, EW2CraneStateVarbit, 1, true) } } @@ -676,13 +889,14 @@ class EW2Listeners : InteractionListener { if (currentPos == 1 && nextPos == 0) { if (craneMat == 2) { // if holding elemental bar, transform into hot bar - setVarbit(player, EW2CraneStateVarbit, 3) + setVarbit(player, EW2CraneStateVarbit, 3, true) playAudio(player, Sounds.EW2_CRANE_LOWER_3169) } } setVarbit(player, EW2CranePosVarbit, nextPos, true) + lockInteractions(player, 5) animate(player, Animations.HUMAN_PULL_LEVER_4909) - syncCrane(player) + syncCrane(player) // also syncs the cart inside the crane function return@on true } @@ -699,6 +913,7 @@ class EW2Listeners : InteractionListener { val nextPos = currentPos xor 2 setVarbit(player, EW2CranePosVarbit, nextPos, true) + lockInteractions(player, 5) animate(player, Animations.HUMAN_PULL_LEVER_4909) syncCrane(player) return@on true @@ -739,7 +954,7 @@ class EW2Listeners : InteractionListener { animate(player, Animations.HUMAN_PULL_LEVER_4909) } 2 -> { // hot ele bar. changes state to hot smashed bar. - setVarbit(player, EW2JigCartVarbit, 3) + setVarbit(player, EW2JigCartVarbit, 3, true) // "hardy" is correct. see above. sendDialogue(player, "You pull the lever and the press responds. The hardy comes down, hitting the elemental bar. The bar has been flattened.") playAudio(player, Sounds.EW2_PRESS_BAR_3180) @@ -834,7 +1049,7 @@ class EW2Listeners : InteractionListener { else -> tankState } - setVarbit(player, EW2TankDoorStateVarbit, nextTankState) + setVarbit(player, EW2TankDoorStateVarbit, nextTankState, true) syncTankDoor(player) return@on true } @@ -865,35 +1080,34 @@ class EW2Listeners : InteractionListener { runTask(player, 1) { when (tankState) { 1 -> { // 1 door opened, grabber inside - setVarbit(player, EW2TankDoorStateVarbit, 2) // 2 door opened, grabber out + setVarbit(player, EW2TankDoorStateVarbit, 2, true) // 2 door opened, grabber out // no cart update } 2 -> { // 2 door opened, grabber out // only grab the hot bar. otherwise, put the grabber back in if(cartState == 3) { - setVarbit(player, EW2TankDoorStateVarbit, 4) // 4 door opened, hot bar inside + setVarbit(player, EW2TankDoorStateVarbit, 4, true) // 4 door opened, hot bar inside // remove hot bar cart } else { - setVarbit(player, EW2TankDoorStateVarbit, 1) // 1 door opened, grabber inside + setVarbit(player, EW2TankDoorStateVarbit, 1, true) // 1 door opened, grabber inside } } 4 -> { // 4 door opened, hot bar inside - setVarbit(player, EW2TankDoorStateVarbit, 5) // 5 door opened, hot bar out + setVarbit(player, EW2TankDoorStateVarbit, 5, true) // 5 door opened, hot bar out } 5 -> { // 5 door opened, hot bar out - setVarbit(player, EW2TankDoorStateVarbit, 1) // 1 door opened, grabber inside + setVarbit(player, EW2TankDoorStateVarbit, 1, true) // 1 door opened, grabber inside // add hot bar cart } 7 -> { // 7 door opened, cooled bar inside - setVarbit(player, EW2TankDoorStateVarbit, 8) // 8 door opened, cooled bar out + setVarbit(player, EW2TankDoorStateVarbit, 8, true) // 8 door opened, cooled bar out } 8 -> { // 8 door opened, cooled bar out - setVarbit(player, EW2TankDoorStateVarbit, 1) // 1 door opened, grabber inside + setVarbit(player, EW2TankDoorStateVarbit, 1, true) // 1 door opened, grabber inside // add cool bar cart } } - syncTankDoor(player) - syncCart(player) + syncTankDoor(player) // also syncs the cart inside there } return@on true } @@ -914,24 +1128,26 @@ class EW2Listeners : InteractionListener { return@on true } - player.locks.lockInteractions(4) + lockInteractions(player, 4) animate(player, 4861) // if closed, open the valve if(valveState == 0){ setVarbit(player, EW2ValveWestVarbit, 1, true) - runTask(player, 3) { + queueScript(player, 3, QueueStrength.SOFT) { syncTank(player) sendDialogue(player, "You open the valve.") playAudio(player, Sounds.EW2_WATER_VALVE_3185) + return@queueScript true } // if open, close the valve } else { setVarbit(player, EW2ValveWestVarbit, 0, true) - runTask(player, 3) { + queueScript(player, 3, QueueStrength.SOFT) { syncTank(player) sendDialogue(player, "You close the valve.") playAudio(player, Sounds.EW2_WATER_VALVE_3185) + return@queueScript true } } return@on true @@ -1038,6 +1254,7 @@ class EW2Listeners : InteractionListener { } addItemOrDrop(player, cogToGive) + syncCogs(player) animate(player, 537) sendMessage(player, "You remove the cog from the shaft.") } @@ -1070,6 +1287,7 @@ class EW2Listeners : InteractionListener { removeItem(player, item) } } + syncCogs(player) return@onUseWith true } @@ -1096,6 +1314,7 @@ class EW2Listeners : InteractionListener { removeItem(player, item) } } + syncCogs(player) return@onUseWith true } @@ -1122,6 +1341,7 @@ class EW2Listeners : InteractionListener { removeItem(player, item) } } + syncCogs(player) return@onUseWith true } @@ -1146,6 +1366,7 @@ class EW2Listeners : InteractionListener { playAudio(player, Sounds.EW2_FANBLADE_3174) setVarbit(player, EW2FanLeverVarbit, 1, true) syncFan(player) + syncCogs(player) // check cart and cool if necessary. otherwise nothing should happen. if (cartpos == 3 && cartstate == 4) { setVarbit(player, EW2JigCartVarbit, 5, true) @@ -1158,6 +1379,7 @@ class EW2Listeners : InteractionListener { sendDialogue(player, "The machine grinds to a standstill.") setVarbit(player, EW2FanLeverVarbit, 0, true) syncFan(player) + syncCogs(player) } } else { // allCogs != 57, cogs are not correct sendDialogue(player, "The machine starts up, but the fan does not blow.") @@ -1218,7 +1440,7 @@ class EW2Listeners : InteractionListener { 0, 0 ) - visualize(player, goodHatAnim, goodHatGfx) + visualize(player, EXTRACTOR_GOOD, EXTRACTOR_GOOD_GFX) playAudio(player, Sounds.EW2_EXTRACTOR_OPERATION_3166) sendGraphics(809, Location.create(1962, 5148, 0)) setVarbit(player, EW2ExtractorGun, 2, true) @@ -1246,7 +1468,7 @@ class EW2Listeners : InteractionListener { 0, 0 ) - visualize(player, badHatAnim, badHatGfx) + visualize(player, EXTRACTOR_BAD, EXTRACTOR_BAD_GFX) playAudio(player, Sounds.EW2_EXTRACTOR_ZAP_3173) impact(player, 4) sendMessage(player, "The extractor hat shocks you!.") diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Session.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Session.kt index c5fbc81ca..59995acee 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Session.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/EW2Session.kt @@ -1,6 +1,7 @@ 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.syncCogs 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 @@ -53,6 +54,7 @@ class EW2Session(val player: Player? = null) : MapArea { syncTank(p) syncTankDoor(p) syncFan(p) + syncCogs(p) } // clean up the instanced area diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/JunctionBoxInterface.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/JunctionBoxInterface.kt index 3aa5dccd9..ade19851b 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/JunctionBoxInterface.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop2/JunctionBoxInterface.kt @@ -92,10 +92,10 @@ class JunctionBoxInterface : InterfaceListener { setComponentVisibility(player, junctionBox, pipes[s3 - 1], false); placedCount++ } - setVarbit(player, pipesLeftVarbit, 3 - placedCount) + setVarbit(player, pipesLeftVarbit, 3 - placedCount, true) // reset any selected - setVarbit(player, pipeSelectVarbit, 0) + setVarbit(player, pipeSelectVarbit, 0, true) return@onOpen true } @@ -136,14 +136,14 @@ class JunctionBoxInterface : InterfaceListener { b4 -> 4; b5 -> 5; b6 -> 6 else -> 0 } - setVarbit(player, pipeSelectVarbit, selection) + setVarbit(player, pipeSelectVarbit, selection, true) } else { val pipeIndex = getPipeIndex(currentSelection, buttonID, b1, b2, b3, b4, b5, b6) if (pipeIndex != -1) { // check if this specific pipe is already placed if (isPipeAlreadyPlaced(player, pipeIndex + 1)) { - setVarbit(player, pipeSelectVarbit, 0) + setVarbit(player, pipeSelectVarbit, 0, true) return@on true } @@ -155,15 +155,15 @@ class JunctionBoxInterface : InterfaceListener { // pipe status is saved in these varbits and recalled on open. they are linked to main quest progression status val saved = when { getVarbit(player, firstSelect) == 0 -> { - setVarbit(player, firstSelect, pipeIndex + 1); true + setVarbit(player, firstSelect, pipeIndex + 1, true); true } getVarbit(player, secondSelect) == 0 -> { - setVarbit(player, secondSelect, pipeIndex + 1); true + setVarbit(player, secondSelect, pipeIndex + 1, true); true } getVarbit(player, thirdSelect) == 0 -> { - setVarbit(player, thirdSelect, pipeIndex + 1); true + setVarbit(player, thirdSelect, pipeIndex + 1, true); true } else -> false @@ -171,10 +171,10 @@ class JunctionBoxInterface : InterfaceListener { if (saved) { val left = getVarbit(player, pipesLeftVarbit) - setVarbit(player, pipesLeftVarbit, left - 1) + setVarbit(player, pipesLeftVarbit, left - 1, true) } } - setVarbit(player, pipeSelectVarbit, 0) + setVarbit(player, pipeSelectVarbit, 0, true) } // if there are no pipes left, we have placed them all. hammer time! @@ -215,10 +215,10 @@ class JunctionBoxInterface : InterfaceListener { // Hide the pipe on interface setComponentVisibility(player, junctionBox, pipes[pipeIndex], true) // Reset the varbit - setVarbit(player, v, 0) + setVarbit(player, v, 0, true) // Increment pipes left val left = getVarbit(player, pipesLeftVarbit) - setVarbit(player, pipesLeftVarbit, left + 1) + setVarbit(player, pipesLeftVarbit, left + 1, true) playAudio(player, Sounds.EW2_REMOVE_PIPE_3181) return true } diff --git a/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt b/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt index 468bdaf96..987cc7b84 100644 --- a/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/SpawnCommandSet.kt @@ -1,8 +1,14 @@ package core.game.system.command.sets +import core.api.addScenery +import core.api.animateScenery +import core.api.delayScript import core.api.log +import core.api.queueScript import core.api.sendMessage +import core.api.stopExecuting import core.cache.Cache +import core.game.interaction.QueueStrength import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder import core.game.node.entity.npc.NPC @@ -121,6 +127,52 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){ log(this::class.java, Log.FINE, "object = $`object`") } + /** + * Spawn object with given ID at the player's location and animate it + */ + define("objanim", usage = "::objanim [objectId] [animFrom] [(opt)animTo] [(opt)delay]", description = "Spawns an object and animates it with the provided anim ID. If a second ID is provided, it iterates through the anims.") { player, args -> + if (args.size < 3) { + sendMessage(player, "Usage: ::objanim [objectId] [animFrom] [(opt)animTo] [(opt)delay]") + return@define + } + + val objectId = args[1].toIntOrNull() + val animFrom = args[2].toIntOrNull() + + val animTo = args.getOrNull(3)?.toIntOrNull() + val delay = args.getOrNull(4)?.toIntOrNull() ?: 3 + + if (objectId == null || animFrom == null) { + sendMessage(player, "Invalid arguments. Please use numbers.") + return@define + } + + // spawn the object + val obj = core.game.node.scenery.Scenery(objectId, player.location) + addScenery(obj) + + // animate a single anim + if (animTo == null) { + animateScenery(player, obj, animFrom) + sendMessage(player, "Spawned object $objectId and playing animation $animFrom.") + return@define + } + + // loop through the anims if it's a range + sendMessage(player, "Spawned object $objectId, playing anims $animFrom to $animTo.") + queueScript(player, 1, QueueStrength.STRONG) { stage: Int -> + val currentAnimId = animFrom + stage + animateScenery(player, obj, currentAnimId) + sendMessage(player, "Playing object animation $currentAnimId") + if (currentAnimId >= animTo) { + return@queueScript stopExecuting(player) + } + return@queueScript delayScript(player, delay) + } + + return@define + } + define("objectgrid", usage = "::objectgrid start-id end-id type rotation", description = "Spawns a 10-wide grid cycling through the given object id range.") { player, args -> if(args!!.size != 5) { reject(player, "Usage: objectgrid beginId endId type rotation")