Implemented Elemental Workshop I quest

This commit is contained in:
Woahscam 2022-07-29 13:16:52 +00:00 committed by Ryan
parent f983912ebf
commit 91cdd5a95a
11 changed files with 1078 additions and 18 deletions

View file

@ -63624,5 +63624,71 @@
"maxAmount": "1"
}
]
},
{
"default": [
{
"minAmount": "1",
"weight": "1.0",
"id": "2892",
"maxAmount": "1"
},
{
"minAmount": "1",
"weight": "1.0",
"id": "1480",
"maxAmount": "1"
}
],
"charm": [],
"ids": "4910",
"description": "Earth Elemental",
"main": [
{
"minAmount": "1",
"weight": "1.0",
"id": "0",
"maxAmount": "1"
},
{
"minAmount": "1",
"weight": "1.0",
"id": "0",
"maxAmount": "1"
}
]
},
{
"default": [
{
"minAmount": "1",
"weight": "1.0",
"id": "2892",
"maxAmount": "1"
},
{
"minAmount": "1",
"weight": "1.0",
"id": "1480",
"maxAmount": "1"
}
],
"charm": [],
"ids": "4911",
"description": "Elemental Rock",
"main": [
{
"minAmount": "1",
"weight": "1.0",
"id": "0",
"maxAmount": "1"
},
{
"minAmount": "1",
"weight": "1.0",
"id": "0",
"maxAmount": "1"
}
]
}
]

View file

@ -10445,22 +10445,23 @@
"melee_animation": "4868",
"range_animation": "0",
"combat_audio": "1531,1533,1532",
"attack_speed": "7",
"respawn_delay": "60",
"defence_animation": "0",
"attack_speed": "6",
"magic_level": "10",
"respawn_delay": "50",
"defence_animation": "4869",
"weakness": "7",
"magic_animation": "0",
"death_animation": "4870",
"name": "Earth elemental",
"defence_level": "32",
"defence_level": "35",
"safespot": null,
"movement_radius": "4",
"lifepoints": "45",
"lifepoints": "35",
"strength_level": "32",
"id": "1020",
"aggressive": "true",
"range_level": "1",
"attack_level": "32"
"aggressive": "false",
"range_level": "30",
"attack_level": "20"
},
{
"examine": "An air elemental.",
@ -42052,20 +42053,23 @@
"attack_level": "1"
},
{
"examine": "An earth elemental.",
"melee_animation": "4868",
"attack_speed": "7",
"respawn_delay": "60",
"combat_audio": "1531,1533,1532",
"attack_speed": "6",
"magic_level": "10",
"respawn_delay": "5",
"defence_animation": "4869",
"death_animation": "4870",
"name": "Earth elemental",
"defence_level": "1",
"defence_level": "35",
"safespot": null,
"lifepoints": "35",
"strength_level": "1",
"strength_level": "35",
"id": "4910",
"aggressive": "true",
"range_level": "1",
"attack_level": "1"
"aggressive": "false",
"range_level": "30",
"attack_level": "20"
},
{
"examine": "An elemental rock.",

View file

@ -7015,6 +7015,10 @@
"npc_id": "4909",
"loc_data": "{2831,3352,0,0,7}"
},
{
"npc_id": "4911",
"loc_data": "{2703,9894,0,0,0}-{2705,9897,0,0,0}-{2706,9904,0,0,0}-{2707,9906,0,0,0}-{2704,9906,0,0,0}-{2706,9910,0,0,0}-{2703,9911,0,0,0}-{2699,9915,0,0,0}-{2697,9915,0,0,0}-{2694,9915,0,0,0}-{2692,9914,0,0,0}-{2696,9910,0,0,0}-{2694,9908,0,0,0}-{2693,9906,0,0,0}-{2696,9906,0,0,0}-{2698,9901,0,0,0}-{2698,9900,0,0,0}-{2696,9900,0,0,0}-{2690,9903,0,0,0}-{2692,9902,0,0,0}-{2693,9900,0,0,0}-{2692,9900,0,0,0}-{2691,9899,0,0,0}-{2692,9895,0,0,0}-{2693,9891,0,0,0}-{2695,9889,0,0,0}-{2692,9885,0,0,0}-{2699,9882,0,0,0}-{2695,9881,0,0,0}-{2693,9881,0,0,0}-{2690,9880,0,0,0}-{2700,9879,0,0,0}-{2696,9880,0,0,0}-{2694,9878,0,0,0}-{2695,9877,0,0,0}-{2699,9876,0,0,0}-{2692,9876,0,0,0}-{2692,9874,0,0,0}-{2699,9872,0,0,0}-{2698,9871,0,0,0}-{2701,9870,0,0,0}-{2695,9870,0,0,0}-{2695,9869,0,0,0}-{2702,9868,0,0,0}-{2701,9867,0,0,0}-{2697,9866,0,0,0}-{2699,9865,0,0,0}"
},
{
"npc_id": "4920",
"loc_data": "{3289,5496,0,0,5}-{3272,5490,0,0,0}-{3188,5510,0,1,6}-{3194,5510,0,1,2}"

View file

@ -51,9 +51,7 @@ public enum SpecialLadders implements LadderAchievementCheck {
player.getAchievementDiaryManager().finishTask(player,DiaryType.SEERS_VILLAGE,1,3);
}
},
SEERS_VILLAGE_SPINNING_HOUSE_ROOFTOP_DOWN(new Location(2715,3472,3), new Location(2714,3472,1)),
ELEMENTAL_WORKSHOP_STAIRS_DOWN(Location.create(2710,3497, 0), Location.create(2713,9887, 0)),
ELEMENTAL_WORKSHOP_STAIRS_UP(Location.create(2714,9887, 0), Location.create(2709,3498, 0));
SEERS_VILLAGE_SPINNING_HOUSE_ROOFTOP_DOWN(new Location(2715,3472,3), new Location(2714,3472,1));
private static HashMap<Location,Location> destinationMap = new HashMap<>();
private static HashMap<Location,SpecialLadders> ladderMap = new HashMap<>();

View file

@ -7,6 +7,7 @@ import core.game.node.entity.player.Player;
import core.game.node.item.Item;
import core.plugin.Initializable;
import core.plugin.Plugin;
import org.rs09.consts.Items;
/**
* Represents the plugin used to handle the "read" option of a book.
@ -22,6 +23,8 @@ public class BookreadOption extends OptionHandler {
ItemDefinition.forId(292).getHandlers().put("option:read", this);
ItemDefinition.forId(757).getHandlers().put("option:read", this);
ItemDefinition.forId(1856).getHandlers().put("option:read", this);
ItemDefinition.forId(Items.BATTERED_BOOK_2886).getHandlers().put("option:read", this);
ItemDefinition.forId(Items.SLASHED_BOOK_9715).getHandlers().put("option:read", this);
ItemDefinition.forId(9003).getHandlers().put("option:read", this);
ItemDefinition.forId(9004).getHandlers().put("option:read", this);
ItemDefinition.forId(11710).getHandlers().put("option:read", this);
@ -52,6 +55,10 @@ public class BookreadOption extends OptionHandler {
return 49610758;
case 9003:
return 49610759;
case Items.BATTERED_BOOK_2886:
return 49610760;
case Items.SLASHED_BOOK_9715:
return 49610761;
/*case 9004:
return 423943;*/
case 11710:

View file

@ -0,0 +1,60 @@
package rs09.game.content.quest.members.elementalworkshop
import api.setQuestStage
import core.game.content.dialogue.DialoguePlugin
import core.game.content.dialogue.book.Book
import core.game.content.dialogue.book.Page
import core.game.node.entity.player.Player
import core.plugin.Initializable
import org.rs09.consts.Items
@Initializable
class BatteredBookHandler : Book {
constructor(player: Player?) : super(player, "Book of the elemental shield", Items.BATTERED_BOOK_2886, *EWUtils.PAGES) {}
constructor() {
/**
* empty.
*/
}
override fun finish() {
val stage = player.questRepository.getStage("Elemental Workshop I")
if (stage == 0) {
setQuestStage(player, "Elemental Workshop I", 1)
}
}
override fun display(set: Array<Page>) {
player.lock()
player.interfaceManager.open(getInterface())
for (i in 55..76) {
player.packetDispatch.sendString("", getInterface().id, i)
}
player.packetDispatch.sendString("", getInterface().id, 77)
player.packetDispatch.sendString("", getInterface().id, 78)
player.packetDispatch.sendString(getName(), getInterface().id, 6)
for (page in set) {
for (line in page.lines) {
player.packetDispatch.sendString(line.message, getInterface().id, line.child)
}
}
player.packetDispatch.sendInterfaceConfig(getInterface().id, 51, index < 1)
val lastPage = index == sets.size - 1
player.packetDispatch.sendInterfaceConfig(getInterface().id, 53, lastPage)
if (lastPage) {
finish()
}
player.unlock()
}
override fun newInstance(player: Player): DialoguePlugin {
return BatteredBookHandler(player)
}
override fun getIds(): IntArray {
return intArrayOf(EWUtils.BATTERED_BOOK_ID)
}
}

View file

@ -0,0 +1,536 @@
package rs09.game.content.quest.members.elementalworkshop
import api.*
import core.game.content.dialogue.FacialExpression
import core.game.content.global.action.DoorActionHandler
import core.game.node.entity.player.link.audio.Audio
import core.game.node.entity.skill.Skills
import core.game.node.item.Item
import core.game.system.task.Pulse
import core.game.world.map.Location
import core.game.world.update.flag.context.Animation
import org.rs09.consts.*
import rs09.game.content.quest.members.elementalworkshop.EWUtils.BELLOWS_STATE
import rs09.game.content.quest.members.elementalworkshop.EWUtils.FURNACE_STATE
import rs09.game.content.quest.members.elementalworkshop.EWUtils.LEFT_WATER_CONTROL_STATE
import rs09.game.content.quest.members.elementalworkshop.EWUtils.RIGHT_WATER_CONTROL_STATE
import rs09.game.content.quest.members.elementalworkshop.EWUtils.WATER_WHEEL_STATE
import rs09.game.content.quest.members.elementalworkshop.EWUtils.currentStage
import rs09.game.interaction.InteractionListener
import rs09.game.system.SystemLogger
class EWListeners : InteractionListener {
/* Items */
private val batteredBook = Item(Items.BATTERED_BOOK_2886)
private val batteredKey = Item(Items.BATTERED_KEY_2887)
private val slashedBook = Item(Items.SLASHED_BOOK_9715)
private val emptyStoneBowl = Item(Items.A_STONE_BOWL_2888)
private val fullStoneBowl = Item(Items.A_STONE_BOWL_2889)
private val elementalOre = Item(Items.ELEMENTAL_ORE_2892)
private val elementalMetal = Item(Items.ELEMENTAL_METAL_2893)
private val elementalShield = Item(Items.ELEMENTAL_SHIELD_2890)
private val bellowFixReqItems = intArrayOf(
Items.NEEDLE_1733,
Items.THREAD_1734,
Items.LEATHER_1741
)
private val elementalShieldReqItems = intArrayOf(
Items.SLASHED_BOOK_9715,
Items.HAMMER_2347,
Items.ELEMENTAL_METAL_2893
)
/* Scenery */
private val bookcase = Scenery.BOOKCASE_26113
private val lavaTrough = intArrayOf(
Scenery.LAVA_TROUGH_18519, Scenery.LAVA_TROUGH_18520,
Scenery.LAVA_TROUGH_18521, Scenery.LAVA_TROUGH_18522,
Scenery.LAVA_TROUGH_18523
)
// Handling for on/off state (one obj id for both left and right)
private val waterControls = intArrayOf(
Scenery.WATER_CONTROLS_18509, Scenery.WATER_CONTROLS_18510
)
/* Animations */
private val turnWaterControlAnimation = Animation(Animations.HUMAN_TURN_LARGE_VALVE_4861)
private val fillStoneBowlAnimation = Animation(Animations.HUMAN_FILL_STONE_BOWL_4862)
private val fixBellowsAnimation = Animation(Animations.HUMAN_SEW_LARGE_SCENERY_4862)
private val smeltElementalBar = Animation(Animations.HUMAN_FURNACE_SMELTING_3243)
/* Sound effects */
private val fillStoneBowlSFX = Audio(Sounds.FILL_STONE_BOWL_1537, 1)
private val fillFurnaceWithLavaSFX = Audio(Sounds.FILL_FURNACE_WITH_LAVA_1538)
private val pullLeverResetGatesSFX = Audio(Sounds.PULL_LEVER_RESET_GATE_1540)
private val turnWaterControlsSFX = Audio(Sounds.TURN_METAL_WATER_VALVE)
private val pullLeverEnabledSFX = Audio(Sounds.PULL_LEVER_ENABLED_1547)
private val pullLeverDisabledSFX = Audio(Sounds.PULL_LEVER_DISABLED_1548)
private val bellowLeverSFX = Audio(Sounds.PULL_LEVER_GENERAL_2400)
/* In-game locations */
private val leftWaterControlsLocation = Location.create(2713, 9907, 0)
private val rightWaterControlsLocation = Location.create(2726, 9907, 0)
private val DISABLED = 0
private val ENABLED = 1
override fun defineListeners() {
/* * * * * * * * * * * * * * * * * * *
* Seers Village House listeners *
* * * * * * * * * * * * * * * * * */
// Bookcase (quest start)
on(Scenery.BOOKCASE_26113, SCENERY, "search") { player, _ ->
val stage = player.questRepository.getStage("Elemental Workshop I")
if (stage < 3) {
// Player already has battered book in inventory
if (player.inventory.containsItem(batteredBook)) {
sendItemDialogue(
player, Item(Items.BATTERED_BOOK_2886), "There is a book here titled 'The Elemental Shield'. " +
"It can stay here, as you have a copy in your backpack."
)
return@on true
}
// Player needs to receive a battered book
sendItemDialogue(player, Item(Items.BATTERED_BOOK_2886), "You find a book titled 'The Elemental Shield'.")
addItem(player, batteredBook.id)
return@on true
}
// --- AFTER QUEST START ---
if (player.inventory.containsItem(slashedBook)) {
sendItemDialogue(player, Item(Items.SLASHED_BOOK_9715),
"There is a book here titled 'The Elemental Shield'. " +
"It can stay here, as you have a copy in your backpack."
)
player.inventory.addIfDoesntHave(batteredKey)
return@on true
}
sendItemDialogue(player, Item(Items.SLASHED_BOOK_9715), "You find a book titled 'The Elemental Shield'.")
addItem(player, slashedBook.id)
player.inventory.addIfDoesntHave(batteredKey)
return@on true
}
// Knife on Battered book -> Slashed book + Battered key
onUseWith(ITEM, Items.KNIFE_946, Items.BATTERED_BOOK_2886) { player, _, with ->
val stage = currentStage(player)
if (stage >= 1) {
lock(player, 2)
player.pulseManager.run(object : Pulse() {
var count = 0
override fun pulse(): Boolean {
when (count) {
0 -> sendMessage(player, "You make a small cut in the spine of the book.")
1 -> {
sendMessage(player, "Inside you find a small, old, battered key.")
replaceSlot(player, with.asItem().slot, slashedBook)
addItemOrDrop(player, Items.BATTERED_KEY_2887)
setQuestStage(player, "Elemental Workshop I", 3)
return true
}
}
count++
return false
}
})
return@onUseWith true
} else {
sendMessage(player, "Nothing interesting happens.")
return@onUseWith true
}
}
/* * * * * * * * * * * * * * * * * * *
* Seers Village Smithy listeners *
* * * * * * * * * * * * * * * * * */
// Odd looking wall handler
on(intArrayOf(Scenery.ODD_LOOKING_WALL_26114, Scenery.ODD_LOOKING_WALL_26115), SCENERY, "open") { player, wall ->
// Player is allowed to exit without key
if (player.location == Location.create(2710, 3496, 0) || player.location == Location.create(2709, 3496, 0)) {
DoorActionHandler.handleAutowalkDoor(player, wall.asScenery())
return@on true
}
// Player does not have battered key in inventory
if (!inInventory(player, Items.BATTERED_KEY_2887)) {
sendMessage(player, "You see a small hole in the wall but no way to open it.")
return@on true
}
// Increment quest stage
if (questStage(player, "Elemental Workshop I") < 5) {
setQuestStage(player, "Elemental Workshop I", 5)
}
// Allow player through the wall
sendMessage(player, "You use the battered key to open the doors.")
DoorActionHandler.handleAutowalkDoor(player, wall.asScenery())
return@on true
}
on(Scenery.STAIRCASE_3415, SCENERY, "climb-down") { player, _ ->
teleport(player, Location.create(2716, 9888, 0))
// If it is the players first time in the workshop
if (currentStage(player) < 6) {
sendPlayerDialogue(player,
"Now to explore this area thoroughly, to find what " +
"forgotten secrets it contains.", FacialExpression.NEUTRAL)
setQuestStage(player, "Elemental Workshop I", 7)
}
return@on true
}
on(Scenery.STAIRCASE_3416, SCENERY, "climb-up") { player, _ ->
teleport(player, Location.create(2709, 3498, 0))
return@on true
}
/* * * * * * * * * * * * * * * *
* Workshop Area listeners *
* * * * * * * * * * * * * * */
// Center hatch, inaccessible
// NOTE: REMOVE ME/ADD CORRECT CHECKS FOR ELEMENTAL WORKSHOP II
on(Scenery.HATCH_18595, SCENERY, "open") { player, _ ->
sendDialogue(player, "You're unable to open the locked hatch.")
return@on true
}
// Stone bowl box, player can get more at any time
on(Scenery.BOXES_3397, SCENERY, "search") { player, _ ->
// If the player doesn't have a stone bowl in their inventory
if (player.inventory.addIfDoesntHave(emptyStoneBowl)) {
sendMessage(player, "You find a stone bowl.")
} else {
sendMessage(player, "It's empty.")
}
return@on true
}
// Needle crate, player can only receive once
on(Scenery.CRATE_3400, SCENERY, "search") { player, _ ->
if (!getAttribute(player, "/save:ew1:got_needle", false)) {
setAttribute(player, "/save:ew1:got_needle", true)
addItem(player, Items.NEEDLE_1733)
sendMessage(player, "You find a needle.")
} else {
sendMessage(player, "You search the crate but find nothing.")
}
return@on true
}
// Leather crate, player can only receive once
on(Scenery.CRATE_3394, SCENERY, "search") { player, _ ->
if (!getAttribute(player, "/save:ew1:got_leather", false)) {
setAttribute(player, "/save:ew1:got_leather", true)
addItem(player, Items.LEATHER_1741)
sendMessage(player, "You find some leather.")
} else {
sendMessage(player, "You search the crate but find nothing.")
}
return@on true
}
// Workbenches/Anvil
onUseWith(SCENERY, Items.ELEMENTAL_METAL_2893, Scenery.WORKBENCH_3402) { player, used, _ ->
// Warn player their smithing level is too low to make the shield
if (player.skills.getLevel(Skills.SMITHING) < 20) {
sendMessage(player, "You need a smithing level of 20 to create an elemental shield.")
return@onUseWith true
}
// Warn player they don't have the required book in their inventory
if (!player.inventory.containsAtLeastOneItem(intArrayOf(Items.SLASHED_BOOK_9715, Items.BATTERED_BOOK_2886))) {
sendMessage(player, "You are unsure what to do with the bar. Perhaps there is a book to help guide you.")
return@onUseWith true
}
// Warn player they don't have a hammer in their inventory
if (!inInventory(player, Items.HAMMER_2347)) {
sendMessage(player, "You don't have a tool available to shape the metal.")
return@onUseWith true
}
// Sanity error check (Should never get thrown)
if (!player.inventory.containsAll(*elementalShieldReqItems)) {
SystemLogger.logErr("${player.username} tried to forge an elemental shield without all the required items.")
return@onUseWith false
}
// Successfully smith the elemental shield
replaceSlot(player, used.asItem().slot, elementalShield)
sendMessage(player, "Following the instructions in the book you make an elemental shield.")
player.questRepository.getQuest("Elemental Workshop I").finish(player)
return@onUseWith true
}
/* * * * * * * * * * * * * *
* Fire room listeners *
* * * * * * * * * * * * */
// Lava trough stone bowl (empty)
onUseWith(SCENERY, Items.A_STONE_BOWL_2888, *lavaTrough) { player, used, trough ->
lock(player, (animationDuration(fillStoneBowlAnimation) + 1))
runTask(player) {
face(player, trough)
playAudio(player, fillStoneBowlSFX)
animate(player, fillStoneBowlAnimation)
replaceSlot(player, used.asItem().slot, fullStoneBowl)
sendMessage(player, "You fill the bowl with hot lava.")
}
return@onUseWith true
}
// Lava trough stone bowl (filled with lava)
onUseWith(SCENERY, fullStoneBowl.id, *lavaTrough) { player, _, _ ->
sendMessage(player, "The bowl is already full of lava.")
return@onUseWith true
}
// Pour lava into unlit furnace
onUseWith(SCENERY, fullStoneBowl.id, Scenery.FURNACE_18525) { player, used, _ ->
player.faceLocation(Location.create(2726, 9875, 0))
lock(player, 3)
submitIndividualPulse(player, object : Pulse() {
var count = 0
override fun pulse(): Boolean {
when (count) {
0 -> {
replaceSlot(player, used.asItem().slot, emptyStoneBowl)
playAudio(player, fillFurnaceWithLavaSFX)
sendMessage(player, "You empty the lava into the furnace.")
}
2 -> {
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, FURNACE_STATE, ENABLED, true)
sendMessage(player, "The furnace bursts to life.")
return true
}
}
count++
return false
}
})
return@onUseWith true
}
// Pour lava into lit furnace
onUseWith(SCENERY, fullStoneBowl.id, Scenery.FURNACE_18526) { player, used, _ ->
player.faceLocation(Location.create(2726, 9875, 0))
runTask(player) {
playAudio(player, fillFurnaceWithLavaSFX)
replaceSlot(player, used.asItem().slot, emptyStoneBowl)
sendMessage(player, "The lava makes little difference to the furnace's searing heat.")
}
return@onUseWith true
}
// Use elemental ore on lit furnace
onUseWith(SCENERY, Items.ELEMENTAL_ORE_2892, Scenery.FURNACE_18526) { player, _, _ ->
player.faceLocation(Location.create(2726, 9875, 0))
// Warn player their smithing level is too low to smelt the ore
if (player.skills.getLevel(Skills.SMITHING) < 20) {
sendMessage(player, "You need a smithing level of 20 to smelt elemental ore.")
return@onUseWith true
}
// Warn player the bellows must be on so the furnace is properly heated
if (!EWUtils.bellowsEnabled(player)) {
sendMessage(player, "The furnace needs to be hotter to be of any use.")
return@onUseWith true
}
// Warn player they don't have enough coal to smelt the bar
if (amountInInventory(player, Items.COAL_453) < 4) {
sendMessage(player, "You need four heaps of coal to smelt elemental ore.")
return@onUseWith true
}
val duration = animationDuration(smeltElementalBar)
lock(player, (duration + 1))
// Run the "smelting" pulse
submitIndividualPulse(player, object : Pulse() {
var count = 0
override fun pulse(): Boolean {
when (count) {
0 -> {
removeItem(player, Item(Items.COAL_453, 4))
removeItem(player, elementalOre)
animate(player, Animations.HUMAN_FURNACE_SMELTING_3243)
sendMessage(player, "You place the elemental ore and four heaps of coal into the furnace.")
}
duration -> {
addItem(player, Items.ELEMENTAL_METAL_2893)
sendMessage(player, "You retrieve a bar of elemental metal.")
rewardXP(player, Skills.SMITHING, 7.0)
return true
}
}
count++
return false
}
})
return@onUseWith true
}
/* * * * * * * * * * * * * *
* Water room listeners *
* * * * * * * * * * * * */
// Water controls
on(waterControls, SCENERY, "turn") { player, _ ->
// Notify player they can't change water control values while water wheel is running
if (EWUtils.waterWheelEnabled(player)) {
sendMessage(player, "Now that the water wheel is running, the valve seems locked off.")
return@on true
}
// Varbit offset
val offset: Int
// Whether it is enabled or disabled (used for xor toggle)
val enabled: Int
when (player.location) {
// Check left control
leftWaterControlsLocation -> {
offset = LEFT_WATER_CONTROL_STATE
enabled = EWUtils.leftWaterControlBit(player) xor 0x1
}
// Check right control
rightWaterControlsLocation -> {
offset = RIGHT_WATER_CONTROL_STATE
if (EWUtils.leftWaterControlEnabled(player)) {
enabled = 0
} else {
enabled = EWUtils.rightWaterControlBit(player) xor 0x1
}
}
// Sanity control check
else -> {
offset = -1
enabled = 0
SystemLogger.logErr("Unhandled location when determining enabled water controls! ${player.location}")
return@on false
}
}
// Run the turn valve pulse
lock(player, (animationDuration(turnWaterControlAnimation) + 1))
submitIndividualPulse(player, object : Pulse() {
var count = 0
override fun pulse(): Boolean {
when (count) {
0 -> {
playAudio(player, turnWaterControlsSFX)
animate(player, turnWaterControlAnimation)
sendMessage(player, "You turn the handle.")
}
3 -> {
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, offset, enabled, true)
return true
}
}
count++
return false
}
})
return@on true
}
// Water wheel lever handler
on(Scenery.LEVER_3406, SCENERY, "pull") { player, lever ->
// Default will happen no matter what
lock(player, 2)
sendMessage(player, "You pull the lever")
replaceScenery(lever.asScenery(), Scenery.LEVER_3417, 2)
// Check to see if the water wheel is running
if (EWUtils.waterWheelEnabled(player)) {
playAudio(player, pullLeverDisabledSFX)
sendMessage(player, "You hear the sound of a water wheel coming to a standstill.")
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, WATER_WHEEL_STATE, DISABLED, true)
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, BELLOWS_STATE, DISABLED, true)
return@on true
}
// Check to reset both valves to the "off" position if both aren't green
if (!EWUtils.leftWaterControlEnabled(player) || !EWUtils.rightWaterControlEnabled(player)) {
playAudio(player, pullLeverResetGatesSFX)
sendMessage(player, "You hear the sound of the flow gates resetting.")
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, LEFT_WATER_CONTROL_STATE, DISABLED, true)
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, RIGHT_WATER_CONTROL_STATE, DISABLED, true)
return@on true
}
// If both of the above are false, the water wheel can start running
playAudio(player, pullLeverEnabledSFX)
sendMessage(player, "You hear the sound of a water wheel starting up.")
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, WATER_WHEEL_STATE, ENABLED, true)
return@on true
}
/* * * * * * * * * * * * * *
* Air room listeners *
* * * * * * * * * * * * */
on(Scenery.BELLOWS_18516, SCENERY, "fix") { player, bellows ->
player.faceLocation(Location.create(2736, 9883, 0))
// Warn player bellows are already fixed
if (getAttribute(player, "/save:ew1:bellows_fixed", false)) {
sendMessage(player, "The bellows already look fixed to you.")
return@on true
}
// Warn player they don't have a high enough crafting level
if (player.skills.getLevel(Skills.CRAFTING) < 20) {
sendMessage(player, "You need a crafting level of 20 to fix the bellows.")
return@on true
}
// Warn player they don't have the required items
if (!player.inventory.containsAll(*bellowFixReqItems)) {
sendMessage(player, "You don't have the required item(s) to fix the bellows.")
return@on true
}
// If the player has all the requirements to fix the bellows
lock(player, (animationDuration(fixBellowsAnimation) + 1))
runTask(player) {
removeItem(player, Items.LEATHER_1741)
removeItem(player, Item(Items.THREAD_1734, 1))
animate(player, fixBellowsAnimation)
sendMessage(player, "You stitch the leather over the hole in the bellows.")
setAttribute(player, "/save:ew1:bellows_fixed", true)
}
return@on true
}
on(Scenery.LEVER_3409, SCENERY, "pull") { player, lever ->
// Warn player they have to fix the bellows before pulling the lever
if (!getAttribute(player, "/save:ew1:bellows_fixed", false)) {
sendPlayerDialogue(player, "I shouldn't risk damaging the bellows any further.")
return@on true
}
// Pull the lever checks
lock(player, 2)
sendMessage(player, "You pull the lever")
replaceScenery(lever.asScenery(), Scenery.LEVER_3417, 2)
playAudio(player, bellowLeverSFX)
// Warn player the water wheel is not running
if (!EWUtils.waterWheelEnabled(player)) {
sendMessage(player, "Nothing happens; the lever resets itself.")
return@on true
}
// Bellows lever "OFF" state
if (EWUtils.bellowsEnabled(player)) {
sendMessage(player, "The bellows stop pumping air.")
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, BELLOWS_STATE, DISABLED, true)
return@on true
}
// Bellows lever "ON" state
sendMessage(player, "The bellows pump air down the pipe.")
setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, BELLOWS_STATE, ENABLED, true)
return@on true
}
}
override fun defineDestinationOverrides() {
setDest(SCENERY, intArrayOf(Scenery.FURNACE_18525, Scenery.FURNACE_18526), "use") { _, _ ->
return@setDest Location.create(2724,9875)
}
setDest(SCENERY, intArrayOf( Scenery.BELLOWS_18516), "fix") { _, _ ->
return@setDest Location.create(2733, 9884, 0)
}
}
}

View file

@ -0,0 +1,109 @@
package rs09.game.content.quest.members.elementalworkshop
import api.getVarbitValue
import core.game.content.dialogue.book.BookLine
import core.game.content.dialogue.book.Page
import core.game.content.dialogue.book.PageSet
import core.game.node.entity.player.Player
import org.rs09.consts.Vars
object EWUtils {
// Helpers for the battered/slashed book
/**
* Represents the book id
*/
const val BATTERED_BOOK_ID = 49610760
const val SLASHED_BOOK_ID = 49610761
/**
* Represents the array of pages for this book.
*/
val PAGES = arrayOf(
PageSet(
Page(
BookLine("Within the pages of this", 55),
BookLine("book you will find the", 56),
BookLine("secret to working the", 57),
BookLine("very elements themselves.", 58),
BookLine("Early in the fifth age, a", 59),
BookLine("new ore was discovered.", 60),
BookLine("This ore has a unique", 61),
BookLine("property of absorbing,", 62),
BookLine("transforming or focusing", 63),
BookLine("elemental energy. A", 64),
BookLine("workshop was erected", 65),
),
Page(
BookLine("close by to work this new", 66),
BookLine("material. The workshop", 67),
BookLine("was set up for artisans", 68),
BookLine("and inventors to be able", 69),
BookLine("to come and create", 70),
BookLine("devices made from the", 71),
BookLine("unique ore, found only in", 72),
BookLine("the village of the Seers.", 73)
)
),
PageSet(
Page(
BookLine("After some time of", 55),
BookLine("successful industry the", 56),
BookLine("true power of this ore", 57),
BookLine("became apparent, as", 58),
BookLine("greater and more", 59),
BookLine("powerful weapons were", 60),
BookLine("created. Realising the", 61),
BookLine("threat this posed, the magi", 62),
BookLine("of the time closed down", 63),
BookLine("the workshop and bound", 64),
BookLine("it under lock and key,", 65)
),
Page(
BookLine("also trying to destroy all",66),
BookLine("knowledge of ",67),
BookLine("manufacturing processes.",68),
BookLine("Yet this book remains and",69),
BookLine("you may still find a way",70),
BookLine("to enter the workshop",71),
BookLine("within this leather bound",72),
BookLine("volume.",73),
)
)
)
/* OFFSETS */
const val LEFT_WATER_CONTROL_STATE = 3
const val RIGHT_WATER_CONTROL_STATE = 4
const val WATER_WHEEL_STATE = 5
const val FURNACE_STATE = 7
const val BELLOWS_STATE = 9
fun leftWaterControlBit(player: Player): Int {
return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, LEFT_WATER_CONTROL_STATE)
}
fun rightWaterControlBit(player: Player): Int {
return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, RIGHT_WATER_CONTROL_STATE)
}
fun leftWaterControlEnabled(player: Player): Boolean {
return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, LEFT_WATER_CONTROL_STATE) == 1
}
fun rightWaterControlEnabled(player: Player): Boolean {
return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, RIGHT_WATER_CONTROL_STATE) == 1
}
fun waterWheelEnabled(player: Player): Boolean {
return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, WATER_WHEEL_STATE) == 1
}
fun bellowsEnabled(player: Player): Boolean {
return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, BELLOWS_STATE) == 1
}
fun currentStage(player: Player): Int {
return player.questRepository.getStage("Elemental Workshop I")
}
}

View file

@ -0,0 +1,88 @@
package rs09.game.content.quest.members.elementalworkshop
import api.getTool
import api.sendDialogue
import core.game.node.entity.Entity
import core.game.node.entity.npc.AbstractNPC
import core.game.node.entity.skill.Skills
import core.game.node.entity.skill.gather.SkillingTool
import core.game.system.task.Pulse
import core.game.world.map.Location
import core.game.world.update.flag.context.Animation
import core.plugin.Initializable
import org.rs09.consts.NPCs
import rs09.game.interaction.InteractionListener
/**
* Npc(s):
* Earth elemental - ID: 4910 (Attack-able form)
* Elemental rock - ID: 4911 (Sleeping "mine-able" form)
* Option(s):
* "Mine"
*
* @author Woah, with love
*/
@Initializable
class ElementalRockNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id,location), InteractionListener {
private val ELEMENTAL_ROCK_TRANSFORMATION_4865 = Animation(4865)
// Sanity transform back to elemental rock
override fun init() {
transform(NPCs.ELEMENTAL_ROCK_4911)
super.init()
}
override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC {
return ElementalRockNPC(id, location)
}
override fun getIds(): IntArray {
return intArrayOf(NPCs.EARTH_ELEMENTAL_4910, NPCs.ELEMENTAL_ROCK_4911)
}
override fun defineListeners() {
on(ids, NPC, "Mine") { player, node ->
val tool: SkillingTool? = getTool(player, true)
// Warn player they don't have a high enough mining level
if (player.skills.getStaticLevel(Skills.MINING) < 20) {
sendDialogue(player, "You need a mining level of at least 20 to mine elemental ore.")
return@on true
}
// Check to see if the player can proc the NPC
if (tool == null) {
sendDialogue(player, "You do not have a pickaxe which you have the Mining level to use.")
return@on true
}
// Transform the current NPC to 4910 (monster form)
pulseManager.run(object : Pulse() {
var count = 0
override fun pulse(): Boolean {
when (count) {
// Play the transformation Animation
0 -> node.asNpc().animate(ELEMENTAL_ROCK_TRANSFORMATION_4865)
// Yell at the player and transform into the combat NPC 4910
1 -> {
node.asNpc().sendChat("Grr... Ge'roff us!")
node.asNpc().transform(NPCs.EARTH_ELEMENTAL_4910)
}
// Start attacking the player
3 -> {
node.asNpc().attack(player)
return true
}
}
count++
return false
}
})
return@on true
}
}
// Reset back to rock NPC
override fun finalizeDeath(killer: Entity?) {
transform(NPCs.ELEMENTAL_ROCK_4911)
super.finalizeDeath(killer)
}
}

View file

@ -0,0 +1,135 @@
package rs09.game.content.quest.members.elementalworkshop
import api.Commands
import api.addItem
import api.setAttribute
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.game.world.map.Location
import core.plugin.Initializable
import org.rs09.consts.Items
import org.rs09.consts.Vars
import rs09.game.system.command.Privilege
/**
* Elemental Workshop I
*
* Original Development Team:
* Developer: Dylan C
* Graphics: Tom W
* Quality Assurance: Andrew C
* Audio: Ian T
*
* RS2 Rework Team:
* Developer: Dylan C
* Graphics: Tom W
* Quality Assurance: Andrew C
* QuestHelp: Rob M
*
* 2009scape adaptation:
* @author Woah, with love
*/
@Initializable
class ElementalWorkshopQuest : Quest("Elemental Workshop I", 52, 51, 1), Commands {
override fun newInstance(`object`: Any?): Quest {
return this
}
override fun drawJournal(player: Player?, stage: Int) {
super.drawJournal(player, stage)
player ?: return
var line = 11
if (stage == 0) {
line(player, "I can start this quest by reading a", line++)
line(player, "!!book?? found in !!Seers village??.", line++)
line++
line(player, "Minimum requirements:", line++)
line(player, if (player.skills.getStaticLevel(Skills.MINING) >= 20) "---Level 20 Mining/--" else "!!Level 20 Mining??", line++)
line(player, if (player.skills.getStaticLevel(Skills.SMITHING) >= 20) "---Level 20 Smithing/--" else "!!Level 20 Smithing??", line++)
line(player, if (player.skills.getStaticLevel(Skills.CRAFTING) >= 20) "---Level 20 Crafting/--" else "!!Level 20 Crafting??", line++)
} else {
// Player read through book on bookshelf
if (stage < 100) {
if (stage >= 1) {
line(player, "---I have found a battered book in a house in Seers Village./--", line++)
line(player, "---It tells of magic ore and a workshop created to fashion it./--", line++)
line++
if (stage <= 2) {
line(player, "Where is the workshop and how do I get in?", line++)
}
}
if (stage >= 3) {
line(player, "---Cutting open the spine of the book with a knife,/--", line++)
line(player, "---I found a key hidden under the leather binding./--", line++)
line++
if (stage <= 4) {
line(player, "Where is the workshop and how do I get in?", line++)
}
}
if (stage >= 5) {
line(player, "---I have found a secret door in the Seers Village smithy/--", line++)
line++
line(player, "---Where is the workshop and how do I get in?/--", line++)
line++
}
// Player climbed down staircase
if (stage == 7) {
line(player, "There is obviously lots to do here.", line++)
}
} else {
line(player, "---I have found a battered book in a house in Seers Village./--", line++)
line(player, "---It tells of magic ore and a workshop created to fashion it./--", line++)
line++
line(player, "---After fixing up the old workshop machinery, collecting ore", line++)
line(player, "---and smelting it I was able to create an Elemental Shield./--", line++)
line++
line(player, "!!QUEST COMPLETE!??", line++)
}
}
}
override fun finish(player: Player?) {
super.finish(player)
player ?: return
player.packetDispatch.sendString("1 Quest Point,", 277, 8 + 2)
player.packetDispatch.sendString("5,000 Crafting XP", 277, 9 + 2)
player.packetDispatch.sendString("5,000 Smithing XP", 277, 10 + 2)
player.packetDispatch.sendString("The ability to make", 277, 11 + 2)
player.packetDispatch.sendString("elemental shields.", 277, 12 + 2)
player.packetDispatch.sendItemZoomOnInterface(Items.ELEMENTAL_SHIELD_2890, 235, 277, 3 + 2)
player.skills.addExperience(Skills.CRAFTING, 5000.0)
player.skills.addExperience(Skills.SMITHING, 5000.0)
player.questRepository.syncronizeTab(player)
}
override fun getConfig(player: Player?, stage: Int): IntArray {
if (stage >= 100) return intArrayOf(Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, 1048576)
if (stage > 0) return intArrayOf(Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, 3)
else return intArrayOf(Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, 0)
}
override fun defineCommands() {
define("resetew", Privilege.ADMIN) { player, _ ->
setAttribute(player, "/save:ew1:got_needle", false)
setAttribute(player, "/save:ew1:got_leather", false)
setAttribute(player, "/save:ew1:bellows_fixed", false)
player.questRepository.setStageNonmonotonic(player.questRepository.forIndex(52), 0)
player.varpManager.get(Vars.VARP_QUEST_ELEMENTAL_WORKSHOP).clearBitRange(0, 31)
player.varpManager.get(Vars.VARP_QUEST_ELEMENTAL_WORKSHOP).send(player)
player.teleport(Location.create(2715, 3481, 0))
player.inventory.clear()
addItem(player, Items.KNIFE_946)
addItem(player, Items.BRONZE_PICKAXE_1265)
addItem(player, Items.NEEDLE_1733)
addItem(player, Items.THREAD_1734)
addItem(player, Items.LEATHER_1741)
addItem(player, Items.HAMMER_2347)
addItem(player, Items.COAL_453, 4)
}
}
}

View file

@ -0,0 +1,53 @@
package rs09.game.content.quest.members.elementalworkshop
import core.game.content.dialogue.DialoguePlugin
import core.game.content.dialogue.book.Book
import core.game.content.dialogue.book.Page
import core.game.node.entity.player.Player
import core.plugin.Initializable
import org.rs09.consts.Items
@Initializable
class SlashedBookHandler : Book {
constructor(player: Player?) : super(player, "Book of the elemental shield", Items.SLASHED_BOOK_9715, *EWUtils.PAGES) {}
constructor() {
/**
* empty.
*/
}
override fun finish() {}
override fun display(set: Array<Page>) {
player.lock()
player.interfaceManager.open(getInterface())
for (i in 55..76) {
player.packetDispatch.sendString("", getInterface().id, i)
}
player.packetDispatch.sendString("", getInterface().id, 77)
player.packetDispatch.sendString("", getInterface().id, 78)
player.packetDispatch.sendString(getName(), getInterface().id, 6)
for (page in set) {
for (line in page.lines) {
player.packetDispatch.sendString(line.message, getInterface().id, line.child)
}
}
player.packetDispatch.sendInterfaceConfig(getInterface().id, 51, index < 1)
val lastPage = index == sets.size - 1
player.packetDispatch.sendInterfaceConfig(getInterface().id, 53, lastPage)
if (lastPage) {
finish()
}
player.unlock()
}
override fun newInstance(player: Player): DialoguePlugin {
return BatteredBookHandler(player)
}
override fun getIds(): IntArray {
return intArrayOf(EWUtils.SLASHED_BOOK_ID)
}
}