fuck these dynamic regions though

This commit is contained in:
aidan 2026-07-07 20:57:54 -05:00
parent 7d24bf1f85
commit d879416a85
6 changed files with 271 additions and 278 deletions

View file

@ -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)
}
}
}

View file

@ -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)
}
}

View file

@ -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<ActivityPlugin>("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))
}
}
}

View file

@ -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<ZoneBorders> {
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)
}
}
}
}

View file

@ -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)

View file

@ -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
}
}