From 3d5cbd90edd4c95f33a0ed5f78e888c872334d18 Mon Sep 17 00:00:00 2001 From: Player Name Date: Thu, 27 Nov 2025 11:44:43 +0000 Subject: [PATCH] Improved random event kidnapping logic Rewrote home teleport Corrected shades and zombies to only multiply xp by 1/16 if they were random events Unified non-hostile random event chat mechanics Fixed random events repeating their opening message and/or saying their timeout message prematurely Genie will now authentically address female players as 'Mistress' Fixed hostile randoms wrongly teleporting the player after 3 minutes Made quizmaster dialogues unclosable and added a hook to restart them if you lose it Made the pillory dialog unclosable Implemented rock golem switch between ranged and melee Replaced sandwich lady chat lines with authentic quotes Added authentic dialogue for interacting with another player's sandwich lady Added a teleport block for the surprise exam random event --- .../main/content/global/ame/KidnapHelper.kt | 49 ++++++- .../main/content/global/ame/RandomEventNPC.kt | 24 +++- .../main/content/global/ame/RandomEvents.kt | 1 - .../ame/events/HostileRandomEventBehavior.kt | 7 +- .../ame/events/candlelight/PiousPeteNPC.kt | 31 +---- .../global/ame/events/certer/CerterNPC.kt | 37 +++--- .../ame/events/drilldemon/DrillDemonUtils.kt | 11 -- .../events/drilldemon/SergeantDamienNPC.kt | 28 ++-- .../events/drunkendwarf/DrunkenDwarfNPC.kt | 36 +++-- .../global/ame/events/evilbob/EvilBobNPC.kt | 32 +---- .../global/ame/events/evilbob/EvilBobUtils.kt | 8 -- .../ame/events/freakyforester/FreakUtils.kt | 7 - .../freakyforester/FreakyForesterNPC.kt | 30 +---- .../global/ame/events/genie/GenieNPC.kt | 21 ++- .../content/global/ame/events/maze/MazeNPC.kt | 49 ++----- .../global/ame/events/pillory/PilloryNPC.kt | 35 +---- .../quizmaster/QuizMasterDialogueFile.kt | 125 +++++++++++------- .../ame/events/quizmaster/QuizMasterNPC.kt | 42 ++---- .../ame/events/rockgolem/RockGolemBehavior.kt | 25 ++++ .../events/sandwichlady/SandwichLadyRENPC.kt | 31 ++++- .../surpriseexam/MysteriousOldManNPC.kt | 22 +-- .../surpriseexam/SupriseExamListeners.kt | 3 +- .../events/surpriseexam/SurpriseExamUtils.kt | 3 - .../global/handlers/npc/NPCTalkListener.kt | 12 +- .../global/skill/magic/HomeTeleportHelper.kt | 46 +++++++ .../magic/ancient/AncientTeleportPlugin.java | 14 +- .../skill/magic/lunar/LunarListeners.kt | 3 +- .../skill/magic/modern/ModernListeners.kt | 5 +- .../core/game/node/entity/player/Player.java | 16 +-- .../entity/player/link/TeleportManager.java | 98 +------------- 30 files changed, 381 insertions(+), 470 deletions(-) create mode 100644 Server/src/main/content/global/ame/events/rockgolem/RockGolemBehavior.kt create mode 100644 Server/src/main/content/global/skill/magic/HomeTeleportHelper.kt diff --git a/Server/src/main/content/global/ame/KidnapHelper.kt b/Server/src/main/content/global/ame/KidnapHelper.kt index 6fa40c6d1..716fb6277 100644 --- a/Server/src/main/content/global/ame/KidnapHelper.kt +++ b/Server/src/main/content/global/ame/KidnapHelper.kt @@ -2,22 +2,57 @@ package content.global.ame import core.ServerConstants import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.TeleportManager.TeleportType import core.game.world.map.Location +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.Sounds -fun kidnapPlayer(player: Player, loc: Location, type: TeleportType) { - setAttribute(player, "kidnapped-by-random", true) //only used in POH code when you leave the hut, so does not need /save. Do not rely on this outside of its intended POH use case. - if (getAttribute(player, "/save:original-loc", null) == null) { - setAttribute(player, "/save:original-loc", player.location) +fun kidnapPlayer(npc: NPC, player: Player, dest: Location, playerLine: String? = null, callback: (player: Player, npc: NPC) -> Unit) { + val lockDuration = if (playerLine != null) 4 else 6 + lock(player, lockDuration) + queueScript(player, 1, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + if (playerLine != null) { + sendChat(player, playerLine) + return@queueScript delayScript(player, 2) + } + return@queueScript delayScript(player, 0) + } + 1 -> { + sendGraphics(Graphics(1576, 0, 0), player.location) + animate(player,8939) + playAudio(player, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 3) + } + 2 -> { + setAttribute(player, "kidnapped-by-random", true) + if (getAttribute(player, "/save:original-loc", null) == null) { + setAttribute(player, "/save:original-loc", player.location) + } + teleport(player, dest, TeleportType.INSTANT) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player, 8941) + resetAnimator(player) + callback(player, npc) + return@queueScript delayScript(player, 2) + } + 3 -> { + removeAttribute(player, "kidnapped-by-random") //this is not needed at this point anymore and will reenable the original-loc sanity check tick action + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } } - teleport(player, loc, type) } fun returnPlayer(player: Player) { player.locks.unlockTeleport() - val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION ?: Location.create(3222, 3218, 0)) - teleport(player, destination) + val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION) + teleport(player, destination!!) unlock(player) removeAttributes(player, "/save:original-loc", "kidnapped-by-random") } diff --git a/Server/src/main/content/global/ame/RandomEventNPC.kt b/Server/src/main/content/global/ame/RandomEventNPC.kt index b03a075f9..b4da55bb3 100644 --- a/Server/src/main/content/global/ame/RandomEventNPC.kt +++ b/Server/src/main/content/global/ame/RandomEventNPC.kt @@ -4,6 +4,7 @@ import content.global.ame.events.surpriseexam.MysteriousOldManNPC import core.api.playGlobalAudio import core.api.poofClear import core.api.sendMessage +import core.api.setAttribute import core.api.utils.WeightBasedTable import core.game.interaction.MovementPulse import core.game.node.entity.Entity @@ -19,8 +20,10 @@ import core.game.world.map.RegionManager import core.game.world.map.path.Pathfinder import core.game.world.update.flag.context.Graphics import core.integrations.discord.Discord +import core.tools.RandomFunction import core.tools.secondsToTicks import core.tools.ticksToCycles +import org.rs09.consts.NPCs import org.rs09.consts.Sounds import kotlin.math.ceil import kotlin.math.min @@ -43,6 +46,7 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { event.loot = loot event.player = player event.spawnLocation = RegionManager.getSpawnLocation(player, this) + setAttribute(event, "spawned-by-ame", true) return event } @@ -101,7 +105,6 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { } open fun onTimeUp() { - noteAndTeleport() terminate() } @@ -144,4 +147,23 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { val index = min(ids.size, ceil(player.properties.currentCombatLevel / 20.0).toInt()) - 1 return ids[index] } + + fun sayLine(npc: NPC, phrases: Array, hasOpeningPhrase: Boolean, hasOverTimePhrase: Boolean) { + if (!timerPaused && (ticksLeft % 20 == 0 || ticksLeft <= 2)) { //unless the Certer interface is up, speak every 20 ticks, or in the 2nd-to-last tick before attack/note-&-teleport + var playDwarfWhistle = true + if (ticksLeft == secondsToTicks(180) && hasOpeningPhrase) { + sendChat(phrases[0]) + } else if (ticksLeft <= 2 && hasOverTimePhrase) { + sendChat(phrases[phrases.size - 1]) + playDwarfWhistle = false + } else { + val start = if (hasOpeningPhrase) 0 else 1 + val end = if (hasOverTimePhrase) phrases.size - 2 else phrases.size - 1 + sendChat(phrases[RandomFunction.random(start, end + 1)]) + } + if (npc.id == NPCs.DRUNKEN_DWARF_956 && playDwarfWhistle) { + playGlobalAudio(this.location, Sounds.DWARF_WHISTLE_2297) + } + } + } } diff --git a/Server/src/main/content/global/ame/RandomEvents.kt b/Server/src/main/content/global/ame/RandomEvents.kt index 88522b4a0..db5ad9b2e 100644 --- a/Server/src/main/content/global/ame/RandomEvents.kt +++ b/Server/src/main/content/global/ame/RandomEvents.kt @@ -97,5 +97,4 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n } } } - } diff --git a/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt b/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt index 37083b045..952bd0289 100644 --- a/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt +++ b/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt @@ -1,5 +1,6 @@ package content.global.ame.events +import core.api.getAttribute import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPCBehavior @@ -8,12 +9,12 @@ import org.rs09.consts.NPCs class HostileRandomEventBehavior : NPCBehavior( NPCs.EVIL_CHICKEN_2463, NPCs.EVIL_CHICKEN_2464, NPCs.EVIL_CHICKEN_2465, NPCs.EVIL_CHICKEN_2466, NPCs.EVIL_CHICKEN_2467, NPCs.EVIL_CHICKEN_2468, NPCs.RIVER_TROLL_391, NPCs.RIVER_TROLL_392, NPCs.RIVER_TROLL_393, NPCs.RIVER_TROLL_394, NPCs.RIVER_TROLL_395, NPCs.RIVER_TROLL_396, - NPCs.ROCK_GOLEM_413, NPCs.ROCK_GOLEM_414, NPCs.ROCK_GOLEM_415, NPCs.ROCK_GOLEM_416, NPCs.ROCK_GOLEM_417, NPCs.ROCK_GOLEM_418, NPCs.SHADE_425, NPCs.SHADE_426, NPCs.SHADE_427, NPCs.SHADE_428, NPCs.SHADE_429, NPCs.SHADE_430, NPCs.SHADE_431, NPCs.TREE_SPIRIT_438, NPCs.TREE_SPIRIT_439, NPCs.TREE_SPIRIT_440, NPCs.TREE_SPIRIT_441, NPCs.TREE_SPIRIT_442, NPCs.TREE_SPIRIT_443, NPCs.ZOMBIE_419, NPCs.ZOMBIE_420, NPCs.ZOMBIE_421, NPCs.ZOMBIE_422, NPCs.ZOMBIE_423, NPCs.ZOMBIE_424 ) { override fun getXpMultiplier(self: NPC, attacker: Entity): Double { - return super.getXpMultiplier(self, attacker) / 16.0 + val xprate = super.getXpMultiplier(self, attacker) + return if (getAttribute(self, "spawned-by-ame", false)) xprate / 16.0 else xprate } -} +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/candlelight/PiousPeteNPC.kt b/Server/src/main/content/global/ame/events/candlelight/PiousPeteNPC.kt index 70cf1e11c..5231b6f9c 100644 --- a/Server/src/main/content/global/ame/events/candlelight/PiousPeteNPC.kt +++ b/Server/src/main/content/global/ame/events/candlelight/PiousPeteNPC.kt @@ -4,42 +4,19 @@ import content.global.ame.RandomEventNPC import content.global.ame.kidnapPlayer import core.api.* import core.api.utils.WeightBasedTable -import core.game.interaction.QueueStrength import core.game.node.entity.npc.NPC -import core.game.node.entity.player.link.TeleportManager import core.game.world.map.Location -import core.game.world.update.flag.context.Graphics import org.rs09.consts.NPCs -import org.rs09.consts.Sounds -/** "::revent -p player_name -e candlelight" **/ class PiousPeteNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PRIEST_3206) { - override fun init() { super.init() // Supposed to be "I'm sorry to drag you away from your tasks, but I need a little help with something." but it's too goddamn long. - sendChat("${player.username.capitalize()}! I need a little help with something.") + sendChat("${player.username}! I need a little help with something.") face(player) - queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> - when (stage) { - 0 -> { - lock(player, 6) - sendGraphics(Graphics(1576, 0, 0), player.location) - animate(player,8939) - playAudio(player, Sounds.TELEPORT_ALL_200) - return@queueScript delayScript(player, 3) - } - 1 -> { - CandlelightInterface.initCandlelight(player) - kidnapPlayer(player, Location(1972, 5002, 0), TeleportManager.TeleportType.INSTANT) - // AntiMacro.terminateEventNpc(player) - sendGraphics(Graphics(1577, 0, 0), player.location) - animate(player,8941) - openDialogue(player, PiousPeteStartingDialogueFile(), NPC(NPCs.PIOUS_PETE_3207)) - return@queueScript stopExecuting(player) - } - else -> return@queueScript stopExecuting(player) - } + kidnapPlayer(this, player, Location(1972, 5002, 0)) { player, _ -> + CandlelightInterface.initCandlelight(player) + openDialogue(player, PiousPeteStartingDialogueFile(), NPC(NPCs.PIOUS_PETE_3207)) } } diff --git a/Server/src/main/content/global/ame/events/certer/CerterNPC.kt b/Server/src/main/content/global/ame/events/certer/CerterNPC.kt index daf3bc822..20b172404 100644 --- a/Server/src/main/content/global/ame/events/certer/CerterNPC.kt +++ b/Server/src/main/content/global/ame/events/certer/CerterNPC.kt @@ -2,29 +2,19 @@ package content.global.ame.events.certer import core.game.node.entity.npc.NPC import core.game.node.entity.player.link.emote.Emotes -import core.tools.RandomFunction import org.rs09.consts.NPCs import content.global.ame.RandomEventNPC import core.api.animate +import core.api.lock import core.api.utils.WeightBasedTable class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GILES_2538) { - lateinit var pName: String lateinit var phrases: Array override fun tick() { - // Don't speak if we have the interface opened - if (!timerPaused) { - // Over allotted time phrase - if (ticksLeft <= 2) { - player.lock(2) - sendChat(phrases[4]) - - // Say a phrase every 20 ticks starting at 280 ticks - // as to not interfere with the init chat phrase - } else if (ticksLeft <= 280 && ticksLeft % 20 == 0) { - sendChat(phrases[RandomFunction.random(1, 3)]) - } + sayLine(this, phrases, true, true) + if (ticksLeft == 2) { + lock(player, 2) } super.tick() } @@ -36,15 +26,20 @@ class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NP override fun init() { super.init() - pName = player.username.capitalize() - phrases = arrayOf("Greetings $pName, I need your help.", - "ehem... Hello $pName, please talk to me!", - "Hello, are you there $pName?", - "It's really rude to ignore someone, $pName!", - "No-one ignores me!") + phrases = arrayOf( + "Greetings ${player.username}, I need your help.", + "ehem... Hello ${player.username}, please talk to me!", + "Hello, are you there ${player.username}?", + "It's really rude to ignore someone, ${player.username}!", + "No-one ignores me!" + ) player.setAttribute("random:pause", false) player.setAttribute("certer:reward", false) - sendChat(phrases[0]) animate(this, Emotes.BOW.animation, true) } + + override fun onTimeUp() { + noteAndTeleport() + terminate() + } } \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt index a6638fecb..8c90bd77f 100644 --- a/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt +++ b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt @@ -1,12 +1,9 @@ package content.global.ame.events.drilldemon -import content.global.ame.kidnapPlayer import content.global.ame.returnPlayer import core.api.* import core.game.interaction.QueueStrength import core.game.node.entity.player.Player -import core.game.node.entity.player.link.TeleportManager -import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import core.game.world.update.flag.context.Animation import org.rs09.consts.Items @@ -24,13 +21,6 @@ object DrillDemonUtils { val DD_AREA = ZoneBorders(3158, 4817, 3168, 4823) val DD_NPC = NPCs.SERGEANT_DAMIEN_2790 - fun teleport(player: Player) { - kidnapPlayer(player, Location.create(3163, 4819, 0), TeleportManager.TeleportType.INSTANT) - player.interfaceManager.closeDefaultTabs() - setComponentVisibility(player, 548, 69, true) - setComponentVisibility(player, 746, 12, true) - } - fun changeSignsAndAssignTask(player: Player) { setVarp(player, DD_SIGN_VARP, 0) val tempList = arrayListOf(DD_SIGN_JOG, DD_SIGN_JUMP, DD_SIGN_PUSHUP, DD_SIGN_SITUP).shuffled().toMutableList() @@ -94,6 +84,5 @@ object DrillDemonUtils { } return@queueScript stopExecuting(player) } - } } diff --git a/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienNPC.kt b/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienNPC.kt index af4e8fe3c..2a3ec7f51 100644 --- a/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienNPC.kt +++ b/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienNPC.kt @@ -3,32 +3,22 @@ package content.global.ame.events.drilldemon import core.game.node.entity.npc.NPC import org.rs09.consts.NPCs import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer import core.api.* import core.api.utils.WeightBasedTable -import core.game.interaction.QueueStrength -import core.game.system.timer.impl.AntiMacro -import core.tools.secondsToTicks +import core.game.world.map.Location class SergeantDamienNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SERGEANT_DAMIEN_2790) { - override fun init() { super.init() - sendChat(player.username+ "! Drop and give me 20!") - queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> - when (stage) { - 0 -> { - lock(player, secondsToTicks(30)) - DrillDemonUtils.teleport(player) - AntiMacro.terminateEventNpc(player) - return@queueScript delayScript(player, 2) - } - 1 -> { - openDialogue(player, SeargentDamienDialogue(isCorrect = true, eventStart = true), NPCs.SERGEANT_DAMIEN_2790) - return@queueScript stopExecuting(player) - } - else -> return@queueScript stopExecuting(player) - } + sendChat("${player.username}! Drop and give me 20!") + face(player) + kidnapPlayer(this, player, Location(3163, 4819, 0)) { player, _ -> + player.interfaceManager.closeDefaultTabs() + setComponentVisibility(player, 548, 69, true) + setComponentVisibility(player, 746, 12, true) + openDialogue(player, SeargentDamienDialogue(isCorrect = true, eventStart = true), NPCs.SERGEANT_DAMIEN_2790) } } diff --git a/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfNPC.kt b/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfNPC.kt index 1485dd8c4..f4a6842ff 100644 --- a/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfNPC.kt +++ b/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfNPC.kt @@ -9,35 +9,27 @@ import org.rs09.consts.NPCs import org.rs09.consts.Sounds class DrunkenDwarfNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.DRUNKEN_DWARF_956) { - private val phrases = arrayOf("Oi, are you der @name!","Dunt ignore your matey!","Aww comeon, talk to ikle me @name!") - private var attackPhrase = false + lateinit var phrases: Array private var attackDelay = 0 - private var lastPhraseTime = 0 - - private fun sendPhrases() { - if (getWorldTicks() > lastPhraseTime + 5) { - playGlobalAudio(this.location, Sounds.DWARF_WHISTLE_2297) - sendChat(this, phrases.random().replace("@name",player.username.capitalize())) - this.face(player) - lastPhraseTime = getWorldTicks() - } - } override fun init() { super.init() - playGlobalAudio(this.location, Sounds.DWARF_WHISTLE_2297) - sendChat(this, "'Ello der ${player.username.capitalize()}! *hic*") + phrases = arrayOf( + "'Ello der ${player.username}! *hic*", + "Oi, are you der ${player.username}!", + "Dunt ignore your matey!", + "Aww comeon, talk to ikle me ${player.username}!", + "I hates you, ${player.username}!" + ) } override fun tick() { - if (RandomFunction.roll(20) && !attackPhrase) - sendPhrases() + sayLine(this, phrases, true, true) if (ticksLeft <= 10) { ticksLeft = 10 - if (!attackPhrase) - sendChat("I hates you, ${player.username.capitalize()}!").also { attackPhrase = true } - if (attackDelay <= getWorldTicks()) + if (attackDelay <= getWorldTicks()) { this.attack(player) + } } super.tick() } @@ -47,4 +39,10 @@ class DrunkenDwarfNPC(override var loot: WeightBasedTable? = null) : RandomEvent this.pulseManager.clear() openDialogue(player, DrunkenDwarfDialogue(), this.asNpc()) } + + override fun onTimeUp() { + if (attackDelay <= getWorldTicks()) { + this.attack(player) + } + } } \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt b/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt index fc0fe2826..4995b6b41 100644 --- a/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt @@ -1,40 +1,22 @@ package content.global.ame.events.evilbob import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer import core.api.* import core.api.utils.WeightBasedTable -import core.game.interaction.QueueStrength import core.game.node.entity.npc.NPC -import core.game.system.timer.impl.AntiMacro +import core.game.world.map.Location import org.rs09.consts.NPCs -import org.rs09.consts.Sounds class EvilBobNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.EVIL_BOB_2478) { - override fun init() { super.init() sendChat("meow") - queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> - when (stage) { - 0 -> { - lock(player, 6) - sendChat(player, "No... what? Nooooooooooooo!") - animate(player, EvilBobUtils.teleAnim) - player.graphics(EvilBobUtils.telegfx) - playAudio(player, Sounds.TELEPORT_ALL_200) - EvilBobUtils.giveEventFishingSpot(player) - return@queueScript delayScript(player, 3) - } - 1 -> { - sendMessage(player, "Welcome to Scape2009.") - EvilBobUtils.teleport(player) - resetAnimator(player) - openDialogue(player, EvilBobDialogue(), NPCs.EVIL_BOB_2479) - AntiMacro.terminateEventNpc(player) - return@queueScript stopExecuting(player) - } - else -> return@queueScript stopExecuting(player) - } + face(player) + kidnapPlayer(this, player, Location(3419, 4776, 0), "No... what? Nooooooooooooo!") { player, _ -> + EvilBobUtils.giveEventFishingSpot(player) + sendMessage(player, "Welcome to Scape2009.") + openDialogue(player, EvilBobDialogue(), NPCs.EVIL_BOB_2479) } } diff --git a/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt b/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt index d70eab940..58f8e5aee 100644 --- a/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt @@ -1,12 +1,8 @@ package content.global.ame.events.evilbob -import content.global.ame.kidnapPlayer -import content.global.ame.returnPlayer import core.api.* import core.game.node.entity.player.Player -import core.game.node.entity.player.link.TeleportManager import core.game.node.entity.skill.Skills -import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics @@ -53,10 +49,6 @@ object EvilBobUtils { } } - fun teleport(player: Player) { - kidnapPlayer(player, Location.create(3419, 4776, 0), TeleportManager.TeleportType.INSTANT) - } - fun cleanup(player: Player) { removeAttributes(player, assignedFishingZone, eventComplete, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen) removeAll(player, Items.FISHLIKE_THING_6202) diff --git a/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt b/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt index e247bfeb1..84e491f89 100644 --- a/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt @@ -1,13 +1,10 @@ package content.global.ame.events.freakyforester -import content.global.ame.kidnapPlayer import content.global.ame.returnPlayer import core.api.* import org.rs09.consts.Items import org.rs09.consts.NPCs import core.game.node.entity.player.Player -import core.game.node.entity.player.link.TeleportManager -import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import core.tools.RandomFunction @@ -28,10 +25,6 @@ object FreakUtils{ player.dialogueInterpreter.open(FreakyForesterDialogue(), freakNpc) } - fun teleport(player: Player) { - kidnapPlayer(player, Location.create(2599, 4777 ,0), TeleportManager.TeleportType.INSTANT) - } - fun cleanup(player: Player) { returnPlayer(player) removeAttributes(player, freakTask, freakComplete, pheasantKilled) diff --git a/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt b/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt index 4b0366263..1ef52dde6 100644 --- a/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt @@ -1,39 +1,21 @@ package content.global.ame.events.freakyforester import content.global.ame.RandomEventNPC +import content.global.ame.kidnapPlayer import core.api.* import org.rs09.consts.NPCs import core.api.utils.WeightBasedTable -import core.game.interaction.QueueStrength import core.game.node.entity.npc.NPC -import core.game.system.timer.impl.AntiMacro -import core.game.world.update.flag.context.Graphics -import org.rs09.consts.Sounds +import core.game.world.map.Location class FreakyForesterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.FREAKY_FORESTER_2458) { - override fun init() { super.init() sendChat("Ah, ${player.username}, just the person I need!") - queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> - when (stage) { - 0 -> { - lock(player, 6) - sendGraphics(Graphics(308, 100, 50), player.location) - animate(player,714) - playAudio(player, Sounds.TELEPORT_ALL_200) - return@queueScript delayScript(player, 3) - } - 1 -> { - FreakUtils.teleport(player) - FreakUtils.giveFreakTask(player) - AntiMacro.terminateEventNpc(player) - openDialogue(player, FreakyForesterDialogue(), FreakUtils.freakNpc) - resetAnimator(player) - return@queueScript stopExecuting(player) - } - else -> return@queueScript stopExecuting(player) - } + face(player) + kidnapPlayer(this, player, Location(2599, 4777, 0)) { player, _ -> + FreakUtils.giveFreakTask(player) + openDialogue(player, FreakyForesterDialogue(), FreakUtils.freakNpc) } } diff --git a/Server/src/main/content/global/ame/events/genie/GenieNPC.kt b/Server/src/main/content/global/ame/events/genie/GenieNPC.kt index 6f3cf39a1..b4700a9a5 100644 --- a/Server/src/main/content/global/ame/events/genie/GenieNPC.kt +++ b/Server/src/main/content/global/ame/events/genie/GenieNPC.kt @@ -4,27 +4,40 @@ import core.game.node.entity.npc.NPC import core.tools.RandomFunction import org.rs09.consts.NPCs import content.global.ame.RandomEventNPC +import core.api.lock import core.api.playAudio import core.api.utils.WeightBasedTable import org.rs09.consts.Sounds class GenieNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GENIE_409) { - val phrases = arrayOf("Greetings, @name!","Ehem... Master @name?","Are you there, Master @name?","No one ignores me!") + lateinit var phrases: Array override fun tick() { - if(RandomFunction.random(1,15) == 5){ - sendChat(phrases.random().replace("@name",player.username.capitalize())) + sayLine(this, phrases, true, true) + if (ticksLeft == 2) { + lock(player, 2) } super.tick() } override fun init() { super.init() + val honorific = if (player.isMale) "Master" else "Mistress" + phrases = arrayOf( + "Greetings, ${player.username}!", + "Ehem... $honorific ${player.username}?", + "Are you there, $honorific ${player.username}?", + "No one ignores me!" + ) playAudio(player, Sounds.GENIE_APPEAR_2301) - sendChat(phrases.random().replace("@name",player.username.capitalize())) } override fun talkTo(npc: NPC) { player.dialogueInterpreter.open(GenieDialogue(),npc) } + + override fun onTimeUp() { + noteAndTeleport() + terminate() + } } diff --git a/Server/src/main/content/global/ame/events/maze/MazeNPC.kt b/Server/src/main/content/global/ame/events/maze/MazeNPC.kt index e4befe00e..a51f0b044 100644 --- a/Server/src/main/content/global/ame/events/maze/MazeNPC.kt +++ b/Server/src/main/content/global/ame/events/maze/MazeNPC.kt @@ -4,54 +4,29 @@ import content.global.ame.RandomEventNPC import content.global.ame.kidnapPlayer import core.api.* import core.api.utils.WeightBasedTable -import core.game.interaction.QueueStrength import core.game.node.entity.npc.NPC -import core.game.node.entity.player.link.TeleportManager -import core.game.system.timer.impl.AntiMacro -import core.game.world.map.Location -import core.game.world.map.build.DynamicRegion -import core.game.world.update.flag.context.Graphics import org.rs09.consts.NPCs -import org.rs09.consts.Sounds class MazeNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { - override fun init() { super.init() sendChat("Aha, you'll do ${player.username}!") face(player) - queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> - when (stage) { - 0 -> { - lock(player, 6) - sendGraphics(Graphics(1576, 0, 0), player.location) - animate(player,8939) - playAudio(player, Sounds.TELEPORT_ALL_200) - return@queueScript delayScript(player, 3) - } - 1 -> { - MazeInterface.initMaze(player) - // Note: This event is NOT instanced: - // Sources: - // https://youtu.be/2gpzn9oNdy0 (2007) - // https://youtu.be/Tni1HURgnxg (2008) - // https://youtu.be/igdwDZOv9LU (2008) - // https://youtu.be/0oBCkLArUmc (2011 - even with personal Mysterious Old Man) - "Sorry, this is not the old man you are looking for." - // https://youtu.be/FMuKZm-Ikgs (2011) - // val region = DynamicRegion.create(11591) - kidnapPlayer(player, MazeInterface.STARTING_POINTS.random(), TeleportManager.TeleportType.INSTANT) // 10 random spots - AntiMacro.terminateEventNpc(player) - sendGraphics(Graphics(1577, 0, 0), player.location) - animate(player,8941) - removeAttribute(player, MazeInterface.MAZE_ATTRIBUTE_CHESTS_OPEN) - return@queueScript stopExecuting(player) - } - else -> return@queueScript stopExecuting(player) - } + // Note: This event is NOT instanced: + // Sources: + // https://youtu.be/2gpzn9oNdy0 (2007) + // https://youtu.be/Tni1HURgnxg (2008) + // https://youtu.be/igdwDZOv9LU (2008) + // https://youtu.be/0oBCkLArUmc (2011 - even with personal Mysterious Old Man) - "Sorry, this is not the old man you are looking for." + // https://youtu.be/FMuKZm-Ikgs (2011) + // val region = DynamicRegion.create(11591) + kidnapPlayer(this, player, MazeInterface.STARTING_POINTS.random()) { player, _ -> + MazeInterface.initMaze(player) + removeAttribute(player, MazeInterface.MAZE_ATTRIBUTE_CHESTS_OPEN) } } override fun talkTo(npc: NPC) { - // Do nothing. + sendMessage(player, "He isn't interested in talking to you.") } } \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt b/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt index f833d9ce2..3917b035c 100644 --- a/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt +++ b/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt @@ -4,47 +4,22 @@ import content.global.ame.RandomEventNPC import content.global.ame.kidnapPlayer import core.api.* import core.api.utils.WeightBasedTable -import core.game.interaction.QueueStrength +import core.game.component.Component.setUnclosable import core.game.node.entity.npc.NPC -import core.game.node.entity.player.link.TeleportManager -import core.game.system.timer.impl.AntiMacro -import core.game.world.map.Location -import core.game.world.update.flag.context.Graphics import org.rs09.consts.NPCs -import org.rs09.consts.Sounds -// "::revent [-p] player name [-e event name]" class PilloryNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PILLORY_GUARD_2791) { - override fun init() { super.init() sendChat("${player.username}, you're under arrest!") face(player) - player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.") - queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> - when (stage) { - 0 -> { - lock(player, 6) - sendGraphics(Graphics(1576, 0, 0), player.location) - animate(player,8939) - playAudio(player, Sounds.TELEPORT_ALL_200) - return@queueScript delayScript(player, 3) - } - 1 -> { - PilloryInterface.initPillory(player) - val dest = PilloryInterface.LOCATIONS.random() //9 random spots! - kidnapPlayer(player, dest, TeleportManager.TeleportType.INSTANT) - AntiMacro.terminateEventNpc(player) - sendGraphics(Graphics(1577, 0, 0), player.location) - animate(player,8941) - return@queueScript stopExecuting(player) - } - else -> return@queueScript stopExecuting(player) - } + kidnapPlayer(this, player, PilloryInterface.LOCATIONS.random()) { player, _ -> + PilloryInterface.initPillory(player) + setUnclosable(player, player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.")) } } override fun talkTo(npc: NPC) { - //player.dialogueInterpreter.open(FreakyForesterDialogue(),npc) + sendMessage(player, "He isn't interested in talking to you.") } } \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt index 4790a8550..4bb2ff79d 100644 --- a/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt +++ b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt @@ -5,15 +5,17 @@ import core.ServerConstants import core.api.* import core.api.utils.WeightBasedTable import core.api.utils.WeightedItem -import core.game.dialogue.DialogueFile +import core.game.component.Component +import core.game.dialogue.DialogueLabeller +import core.game.dialogue.DialogueOption import core.game.dialogue.FacialExpression import core.game.interaction.QueueStrength import core.game.node.entity.player.Player -import core.tools.END_DIALOGUE import org.rs09.consts.Components import org.rs09.consts.Items +import org.rs09.consts.NPCs -class QuizMasterDialogueFile : DialogueFile() { +class QuizMasterDialogueFile : DialogueLabeller() { companion object { const val QUIZMASTER_INTERFACE = Components.MACRO_QUIZSHOW_191 const val QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT = "/save:quizmaster:questions-correct" @@ -74,56 +76,77 @@ class QuizMasterDialogueFile : DialogueFile() { } - override fun handle(componentID: Int, buttonID: Int) { - when (stage) { - 0 -> npc(FacialExpression.FRIENDLY,"WELCOME to the GREATEST QUIZ SHOW in the", "whole of ${ServerConstants.SERVER_NAME}:", "O D D O N E O U T").also { stage++ } - 1 -> player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...").also { stage++ } - 2 -> npc(FacialExpression.FRIENDLY,"Please welcome our newest contestant:", "${player?.username}!", "Just pick the O D D O N E O U T.", "Four questions right, and then you win!").also { stage++ } - 3 -> { - setAttribute(player!!, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, randomQuestion(player!!)) - player!!.interfaceManager.openChatbox(QUIZMASTER_INTERFACE) - stage++ - } - 4-> { - if (buttonID == getAttribute(player!!, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, 0)) { - // Correct Answer - setAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, getAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + 1) - if (getAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) >= 4) { - npc(FacialExpression.FRIENDLY,"CONGRATULATIONS!", "You are a WINNER!", "Please choose your PRIZE!") - stage = 5 - } else { - npc(FacialExpression.FRIENDLY,"Wow, you're a smart one!", "You're absolutely RIGHT!", "Okay, next question!") - stage = 3 - } + override fun addConversation() { + assignToIds(NPCs.QUIZ_MASTER_2477) + afterClose { player -> + loadLabel(player, "question") + } + + npc(FacialExpression.FRIENDLY,"WELCOME to the GREATEST QUIZ SHOW in the", "whole of ${ServerConstants.SERVER_NAME}:", "O D D O N E O U T", unclosable = true) + player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...", unclosable = true) + npc(FacialExpression.FRIENDLY,"Please welcome our newest contestant:", "${player?.username}!", "Just pick the O D D O N E O U T.", "Four questions right, and then you win!", unclosable = true) + goto("question") + + label("question") + manual(unclosable = true) { player, _ -> + setAttribute(player, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, randomQuestion(player)) + val comp = Component(QUIZMASTER_INTERFACE) + player.interfaceManager.openChatbox(comp) + return@manual comp + } + exec { player, _ -> + if (buttonID == getAttribute(player, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, 0)) { + // Correct Answer + setAttribute(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, getAttribute(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + 1) + if (getAttribute(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) >= 4) { + goto("winner") } else { - // Wrong Answer - npc(FacialExpression.FRIENDLY,"WRONG!", "That's just WRONG!", "Okay, next question!") - stage = 3 + goto("right") } - } - // Random Item should be "Mystery Box", but the current MYSTERY_BOX_6199 is already inauthentically used by Giftmas. - 5 -> options("1000 Coins", "Random Item").also { stage++ } - 6 -> { - resetAnimator(player!!) - returnPlayer(player!!) - when (buttonID) { - 1 -> { - queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int -> - addItemOrDrop(player!!, Items.COINS_995, 1000) - return@queueScript stopExecuting(player!!) - } - } - 2 -> { - queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int -> - addItemOrDrop(player!!, tableRoll.roll()[0].id) - return@queueScript stopExecuting(player!!) - } - } - } - removeAttributes(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER) - stage = END_DIALOGUE - end() + } else { + goto("wrong") } } + + label("right") + npc(FacialExpression.FRIENDLY,"Wow, you're a smart one!", "You're absolutely RIGHT!", "Okay, next question!", unclosable = true) + goto("question") + + label("wrong") + npc(FacialExpression.FRIENDLY,"WRONG!", "That's just WRONG!", "Okay, next question!", unclosable = true) + goto("question") + + label("winner") + npc(FacialExpression.FRIENDLY,"CONGRATULATIONS!", "You are a WINNER!", "Please choose your PRIZE!", unclosable = true) + options( + DialogueOption("money", "1000 Coins", skipPlayer = true), + DialogueOption("item", "Random Item", skipPlayer = true) + ) + + label("money") + exec { player, _ -> + queueScript(player, 0, QueueStrength.SOFT) { _ -> + addItemOrDrop(player, Items.COINS_995, 1000) + return@queueScript stopExecuting(player) + } + } + goto("cleanup") + + label("item") + exec { player, _ -> + queueScript(player, 0, QueueStrength.SOFT) { _ -> + addItemOrDrop(player, tableRoll.roll()[0].id) + return@queueScript stopExecuting(player) + } + } + goto("cleanup") + + label("cleanup") + exec { player, _ -> + resetAnimator(player) + returnPlayer(player) + removeAttributes(player, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER) + } + goto("nowhere") } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt index 26407a1dc..444fc1f1b 100644 --- a/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt +++ b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt @@ -4,14 +4,9 @@ import content.global.ame.RandomEventNPC import content.global.ame.kidnapPlayer import core.api.* import core.api.utils.WeightBasedTable -import core.game.interaction.QueueStrength import core.game.node.entity.npc.NPC -import core.game.node.entity.player.link.TeleportManager -import core.game.system.timer.impl.AntiMacro import core.game.world.map.Location -import core.game.world.update.flag.context.Graphics import org.rs09.consts.NPCs -import org.rs09.consts.Sounds /** * Quiz Master NPC: @@ -27,35 +22,14 @@ class QuizMasterNPC(var type: String = "", override var loot: WeightBasedTable? override fun init() { super.init() sendChat("Hey ${player.username}! It's your lucky day!") - queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> - when (stage) { - 0 -> { - lock(player, 6) - sendGraphics(Graphics(1576, 0, 0), player.location) - animate(player,8939) - playAudio(player, Sounds.TELEPORT_ALL_200) - return@queueScript delayScript(player, 3) - } - 1 -> { - kidnapPlayer(player, Location(1952, 4764, 1), TeleportManager.TeleportType.INSTANT) - setAttribute(player, QuizMasterDialogueFile.QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) - AntiMacro.terminateEventNpc(player) - sendGraphics(Graphics(1577, 0, 0), player.location) - animate(player,8941) - sendMessage(player, "Answer four questions correctly in a row to be teleported back where you came from.") - sendMessage(player, "You will need to relog in if you lose the quiz dialog.") // Inauthentic, but there to notify the player in case. - return@queueScript delayScript(player, 6) - } - 2 -> { - face(player, Location(1952, 4768, 1)) - animate(player,2378) - // This is not needed as when you enter the QuizMasterBorders, it should fire off the dialogue - // openDialogue(player, QuizMasterDialogueFile(), this.asNpc()) - return@queueScript stopExecuting(player) - } - else -> return@queueScript stopExecuting(player) - } - + face(player) + kidnapPlayer(this, player, Location(1952, 4764, 0)) { player, _ -> + setAttribute(player, QuizMasterDialogueFile.QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + sendMessage(player, "Answer four questions correctly in a row to be teleported back where you came from.") + sendMessage(player, "You will need to relog in if you lose the quiz dialog.") // Inauthentic, but there to notify the player in case. + face(player, Location(1952, 4768, 1)) + animate(player,2378) + // Quiz dialogue gets opened automatically on zone entry. } } diff --git a/Server/src/main/content/global/ame/events/rockgolem/RockGolemBehavior.kt b/Server/src/main/content/global/ame/events/rockgolem/RockGolemBehavior.kt new file mode 100644 index 000000000..bd397a5b9 --- /dev/null +++ b/Server/src/main/content/global/ame/events/rockgolem/RockGolemBehavior.kt @@ -0,0 +1,25 @@ +package content.global.ame.events.rockgolem + +import core.game.node.entity.Entity +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.combat.CombatSwingHandler +import core.game.node.entity.combat.MultiSwingHandler +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import org.rs09.consts.NPCs + +class RockGolemBehavior() : NPCBehavior( + NPCs.ROCK_GOLEM_413, NPCs.ROCK_GOLEM_414, NPCs.ROCK_GOLEM_415, NPCs.ROCK_GOLEM_416, NPCs.ROCK_GOLEM_417, NPCs.ROCK_GOLEM_418 +) { + val rangeHandler = SwitchAttack(CombatStyle.RANGE) + val meleeHandler = SwitchAttack(CombatStyle.MELEE) + val combatHandler = MultiSwingHandler(rangeHandler, meleeHandler) + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + return combatHandler + } + + override fun getXpMultiplier(self: NPC, attacker: Entity): Double { + return super.getXpMultiplier(self, attacker) / 16.0 + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt index 1c5a14956..99e0a0e68 100644 --- a/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt +++ b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt @@ -5,32 +5,49 @@ import core.tools.RandomFunction import org.rs09.consts.Items import org.rs09.consts.NPCs import content.global.ame.RandomEventNPC +import core.api.lock import core.api.utils.WeightBasedTable class SandwichLadyRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SANDWICH_LADY_3117) { - val phrases = arrayOf("Hello, @name, can you hear me?","Sandwiches, @name!","Are you ignoring me, @name??","Yoohoo! Sandwiches, @name!","Hello, @name?", "Come get your sandwiches, @name!", "How could you ignore me like this, @name?!", "Do you even want your sandwiches, @name?") + lateinit var phrases: Array var assigned_item = 0 - val items = arrayOf(Items.BAGUETTE_6961,Items.TRIANGLE_SANDWICH_6962,Items.SQUARE_SANDWICH_6965,Items.ROLL_6963,Items.MEAT_PIE_2327,Items.KEBAB_1971,Items.CHOCOLATE_BAR_1973) + val items = arrayOf(Items.BAGUETTE_6961, Items.TRIANGLE_SANDWICH_6962, Items.SQUARE_SANDWICH_6965, Items.ROLL_6963, Items.MEAT_PIE_2327, Items.KEBAB_1971, Items.CHOCOLATE_BAR_1973) override fun tick() { - if(RandomFunction.random(1,15) == 5){ - sendChat(phrases.random().replace("@name",player.username.capitalize())) + sayLine(this, phrases, true, true) + if (ticksLeft == 2) { + lock(player, 2) } super.tick() } override fun init() { super.init() + phrases = arrayOf( + // https://www.youtube.com/watch?v=ek8r3ZS929E + // She always starts with "Sandwiches, ${player.username}!" but she ALSO picks that at random, hence duplicate it with hasOpeningPhrase = true + "Sandwiches, ${player.username}!", + "Sandwiches, ${player.username}!", + "Come on ${player.username}, I made these specially!!", + "All types of sandwiches, ${player.username}.", + "Did you hear me ${player.username}?", + "You think I made these just for fun?!!?", + "How could you ignore me like this, ${player.username}?!" //unknown if authentic but it was already here + ) assignItem() - sendChat(phrases.random().replace("@name",player.username.capitalize())) + } + + override fun onTimeUp() { + noteAndTeleport() + terminate() } fun assignItem(){ assigned_item = items.random() - player.setAttribute("sandwich-lady:item",assigned_item) + player.setAttribute("sandwich-lady:item", assigned_item) } override fun talkTo(npc: NPC) { - player.dialogueInterpreter.open(SandwichLadyDialogue(false),npc) + player.dialogueInterpreter.open(SandwichLadyDialogue(false), npc) } } diff --git a/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt b/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt index 04818dda8..2a3896836 100644 --- a/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt @@ -5,32 +5,16 @@ import content.global.ame.kidnapPlayer import core.api.* import org.rs09.consts.NPCs import core.api.utils.WeightBasedTable -import core.game.interaction.QueueStrength import core.game.node.entity.npc.NPC -import core.game.node.entity.player.link.TeleportManager import core.game.world.map.Location -import core.game.world.update.flag.context.Graphics -import org.rs09.consts.Sounds class MysteriousOldManNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { override fun init() { super.init() sendChat("Surprise exam, ${player.username}!") - queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> - when (stage) { - 0 -> { - lock(player, 6) - sendGraphics(Graphics(1576, 0, 0), player.location) - animate(player,8939) - playAudio(player, Sounds.TELEPORT_ALL_200) - return@queueScript delayScript(player, 3) - } - 1 -> { - kidnapPlayer(player, Location(1886, 5025, 0), TeleportManager.TeleportType.INSTANT) - return@queueScript stopExecuting(player) - } - else -> return@queueScript stopExecuting(player) - } + face(player) + kidnapPlayer(this, player, Location(1886, 5025, 0)) { _, _ -> + /* nothing needed */ } } diff --git a/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt b/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt index 6d8ad6b4f..ad19ffbda 100644 --- a/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.kt @@ -2,7 +2,6 @@ package content.global.ame.events.surpriseexam import core.game.component.Component import core.game.node.entity.player.Player -import core.game.node.item.Item import core.game.world.map.Location import org.rs09.consts.Items import org.rs09.consts.NPCs @@ -66,6 +65,6 @@ class SupriseExamListeners : InteractionListener, MapArea { } override fun getRestrictions(): Array { - return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP) + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.TELEPORT, ZoneRestriction.OFF_MAP) } } diff --git a/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt b/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt index 4f1530eeb..e1cbdd5a8 100644 --- a/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt @@ -1,15 +1,12 @@ package content.global.ame.events.surpriseexam -import content.global.ame.kidnapPlayer import content.global.ame.returnPlayer import core.api.* import core.game.node.entity.impl.PulseType import core.game.node.entity.player.Player import core.game.system.task.Pulse -import core.game.world.map.Location import org.rs09.consts.Components import org.rs09.consts.Items -import core.game.node.entity.player.link.TeleportManager object SurpriseExamUtils { val SE_KEY_INDEX = "supexam:index" diff --git a/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt b/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt index 5235fc82a..4f5764b0d 100644 --- a/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt +++ b/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt @@ -12,6 +12,7 @@ import core.game.system.timer.impl.AntiMacro import core.game.worldevents.holiday.HolidayRandomEventNPC import core.game.worldevents.holiday.HolidayRandomEvents import core.game.worldevents.holiday.HolidayRandoms +import org.rs09.consts.NPCs /** * Handles the NPC talk-to option. @@ -37,11 +38,18 @@ class NPCTalkListener : InteractionListener { val npc = node.asNpc() if(RandomEvents.randomIDs.contains(node.id)){ if(AntiMacro.getEventNpc(player) == null || AntiMacro.getEventNpc(player) != node.asNpc() || AntiMacro.getEventNpc(player)?.finalized == true) { - player.sendMessage("They aren't interested in talking to you.") + // Why the fuck is this here of all places? Now look at what you've made me do: + if (npc.id == NPCs.SANDWICH_LADY_3117) { + // https://www.youtube.com/watch?v=ek8r3ZS929E + player.dialogueInterpreter.sendDialogue("The sandwich lady doesn't seem interested in selling you any", "refreshments.") + } else { + sendMessage(player, "They aren't interested in talking to you.") + } } else { AntiMacro.getEventNpc(player)?.talkTo(node.asNpc()) } return@on true + //TODO bring sanity here } if (HolidayRandomEvents.holidayRandomIDs.contains(node.id) && node is HolidayRandomEventNPC) { if(HolidayRandoms.getEventNpc(player) == null || HolidayRandoms.getEventNpc(player) != node.asNpc() || HolidayRandoms.getEventNpc(player)?.finalized == true) { @@ -79,4 +87,4 @@ class NPCTalkListener : InteractionListener { return@on player.dialogueInterpreter.open(npc.id, npc) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/magic/HomeTeleportHelper.kt b/Server/src/main/content/global/skill/magic/HomeTeleportHelper.kt new file mode 100644 index 000000000..1b67a38e9 --- /dev/null +++ b/Server/src/main/content/global/skill/magic/HomeTeleportHelper.kt @@ -0,0 +1,46 @@ +package content.global.skill.magic + +import core.api.delayScript +import core.api.playGlobalAudio +import core.api.queueScript +import core.api.sendMessage +import core.api.stopExecuting +import core.game.event.TeleportEvent +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.TeleportManager +import core.game.world.map.Location +import core.game.world.map.zone.ZoneRestriction + +val HOME_ANIMATIONS = arrayOf(1722, 1723, 1724, 1725, 2798, 2799, 2800, 3195, 4643, 4645, 4646, 4847, 4848, 4849, 4850, 4851, 4852, 65535) +val HOME_GRAPHICS = arrayOf(775, 800, 801, 802, 803, 804, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 65535) +fun getAudio(count: Int): Int { + return when (count) { + 0 -> 193 + 4 -> 194 + 11 -> 195 + else -> -1 + } +} + +fun homeTeleport(player: Player, dest: Location) { + if (player.timers.getTimer("teleblock") != null) { + sendMessage(player, "A magical force prevents you from teleporting.") + return + } + if (player.locks.isTeleportLocked || player.zoneMonitor.isRestricted(ZoneRestriction.TELEPORT)) { + sendMessage(player, "A magical force has stopped you from teleporting.") + return + } + queueScript(player, 0, QueueStrength.WEAK) { stage -> + if (stage == 18) { + player.properties.teleportLocation = dest + player.dispatch(TeleportEvent(TeleportManager.TeleportType.NORMAL, TeleportMethod.SPELL, -1, dest)) + return@queueScript stopExecuting(player) + } + playGlobalAudio(player.location, getAudio(stage)) + player.packetDispatch.sendGraphic(HOME_GRAPHICS[stage]) + player.packetDispatch.sendAnimation(HOME_ANIMATIONS[stage]) + return@queueScript delayScript(player, 1) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java b/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java index 3944c3c7e..f212f3877 100644 --- a/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java +++ b/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java @@ -1,5 +1,6 @@ package content.global.skill.magic.ancient; +import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.entity.combat.spell.MagicSpell; import core.game.node.entity.combat.spell.Runes; @@ -13,8 +14,12 @@ import core.game.world.GameWorld; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; +import core.tools.Log; import core.tools.RandomFunction; +import static content.global.skill.magic.HomeTeleportHelperKt.homeTeleport; +import static core.api.ContentAPIKt.log; + /** * Represents the plugin used to handle all ancient teleporting plugins. * @author 'Vexia @@ -55,7 +60,14 @@ public final class AncientTeleportPlugin extends MagicSpell { entity.asPlayer().sendMessage("A magical force has stopped you from teleporting."); return false; } - if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), getSpellId() == 28 ? TeleportType.HOME : TeleportType.ANCIENT)) { + boolean isHomeTeleport = getSpellId() == 28; + if (isHomeTeleport) { + if (!entity.isPlayer()) { + log(this.getClass(), Log.ERR, "Why the fuck is a non-player entity trying to cast ancient-magick home teleport?!"); + return false; + } + homeTeleport((Player) entity, Location.create(3087, 3495, 0)); + } else if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), TeleportType.ANCIENT)) { if (!super.meetsRequirements(entity, true, true)) { entity.getTeleporter().getCurrentTeleport().stop(); return false; diff --git a/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt index 632173652..fedf14c27 100644 --- a/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt +++ b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt @@ -5,6 +5,7 @@ import content.global.skill.farming.CompostBins import content.global.skill.farming.CompostType import content.global.skill.farming.FarmingPatch import content.global.skill.magic.SpellListener +import content.global.skill.magic.homeTeleport import content.global.skill.magic.spellconsts.Lunar import core.api.* import core.game.component.CloseEvent @@ -36,7 +37,7 @@ class LunarListeners : SpellListener("lunar"), Commands { // Level 0 onCast(Lunar.HOME_TELEPORT, NONE) { player, _ -> requires(player) - player.teleporter.send(Location.create(2100, 3914, 0),TeleportManager.TeleportType.HOME) + homeTeleport(player, Location.create(2100, 3914, 0)) setDelay(player,true) } diff --git a/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt index d070123a3..d817cd31c 100644 --- a/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt +++ b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt @@ -4,10 +4,12 @@ import content.data.Quests import content.global.skill.magic.SpellListener import content.global.skill.magic.SpellUtils.hasRune import content.global.skill.magic.TeleportMethod +import content.global.skill.magic.homeTeleport import content.global.skill.magic.spellconsts.Modern import content.global.skill.prayer.Bones import content.global.skill.smithing.smelting.Bar import content.global.skill.smithing.smelting.SmeltingPulse +import content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners import core.ServerConstants import core.api.* import core.game.event.ItemAlchemizationEvent @@ -23,7 +25,6 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.TeleportManager import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.Skills -import content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners import core.game.node.item.Item import core.game.world.map.Location import core.game.world.update.flag.context.Animation @@ -39,7 +40,7 @@ class ModernListeners : SpellListener("modern"){ return@onCast } requires(player) - player.teleporter.send(ServerConstants.HOME_LOCATION,TeleportManager.TeleportType.HOME) + homeTeleport(player, ServerConstants.HOME_LOCATION ?: Location(3222, 3218, 0)) setDelay(player,true) } diff --git a/Server/src/main/core/game/node/entity/player/Player.java b/Server/src/main/core/game/node/entity/player/Player.java index 05867a01d..0bac8c9f7 100644 --- a/Server/src/main/core/game/node/entity/player/Player.java +++ b/Server/src/main/core/game/node/entity/player/Player.java @@ -468,15 +468,13 @@ public class Player extends Entity { // Check if the player is on the map, runs only every 6 seconds for performance reasons. // This is only a sanity check to detect improper usage of the 'original-loc' attribute, hence only do this work if the attribute is set. - // Only runs when the player is not movement/interaction-locked, so that original-loc does not get wiped e.g. in the middle of the player teleporting to their POH. - if (GameWorld.getTicks() % 10 == 0 && !getLocks().isMovementLocked() && !getLocks().isInteractionLocked()) { - if (ContentAPIKt.getAttribute(this, "/save:original-loc", null) != null) { - int rid = location.getRegionId(); - Region r = RegionManager.forId(rid); - if (!(r instanceof DynamicRegion) && !getZoneMonitor().isRestricted(ZoneRestriction.OFF_MAP)) { - log(this.getClass(), Log.ERR, "Player " + getUsername() + " has the original-loc attribute set but isn't actually off-map! This indicates a bug in the code that set that attribute. The original-loc is " + getAttribute("/save:original-loc") + ", the current region is " + rid + ". Good luck debugging!"); - ContentAPIKt.removeAttribute(this, "original-loc"); - } + // Does not run if the player is currently in the middle of a kidnap sequence (to avoid false positives while the teleport is taking place). + if (GameWorld.getTicks() % 10 == 0 && ContentAPIKt.getAttribute(this, "/save:original-loc", null) != null && !ContentAPIKt.getAttribute(this, "kidnapped-by-random", false)) { + int rid = location.getRegionId(); + Region r = RegionManager.forId(rid); + if (!(r instanceof DynamicRegion) && !getZoneMonitor().isRestricted(ZoneRestriction.OFF_MAP)) { + log(this.getClass(), Log.ERR, "Player " + getUsername() + " has the original-loc attribute set but isn't actually off-map! This indicates a bug in the code that set that attribute. The original-loc is " + getAttribute("/save:original-loc") + ", the current region is " + rid + ". Good luck debugging!"); + ContentAPIKt.removeAttribute(this, "original-loc"); } } } diff --git a/Server/src/main/core/game/node/entity/player/link/TeleportManager.java b/Server/src/main/core/game/node/entity/player/link/TeleportManager.java index e9f8e5b04..3da78d55b 100644 --- a/Server/src/main/core/game/node/entity/player/link/TeleportManager.java +++ b/Server/src/main/core/game/node/entity/player/link/TeleportManager.java @@ -26,16 +26,6 @@ public class TeleportManager { */ public static final int WILDY_TELEPORT = 1 << 16 | 8; - /** - * The animations used in the home teleport. - */ - private final static int[] HOME_ANIMATIONS = {1722, 1723, 1724, 1725, 2798, 2799, 2800, 3195, 4643, 4645, 4646, 4847, 4848, 4849, 4850, 4851, 4852, 65535}; - - /** - * The graphics used in the home teleport. - */ - private final static int[] HOME_GRAPHICS = {775, 800, 801, 802, 803, 804, 1703, 1704, 1705, 1706, 1707, 1708, 1709, 1710, 1711, 1712, 1713, 65535}; - /** * The entity being handled. */ @@ -62,7 +52,6 @@ public class TeleportManager { */ public TeleportManager(Entity entity) { this.entity = entity; - lastTeleport = TeleportType.HOME.getPulse(entity, ServerConstants.HOME_LOCATION); } /** @@ -116,16 +105,11 @@ public class TeleportManager { } this.teleportType = teleportType; entity.getWalkingQueue().reset(); - lastTeleport = currentTeleport; currentTeleport = type.getPulse(entity, location); entity.getPulseManager().clear(); - if (type == TeleportType.HOME) { - entity.getPulseManager().run(type.getPulse(entity, location)); - } else { - entity.lock(12); - entity.getImpactHandler().setDisabledTicks(teleportType == -1 ? 5 : 12); - GameWorld.getPulser().submit(currentTeleport); - } + entity.lock(12); + entity.getImpactHandler().setDisabledTicks(teleportType == -1 ? 5 : 12); + GameWorld.getPulser().submit(currentTeleport); if (entity instanceof Player) { ((Player) entity).getInterfaceManager().close(); } @@ -143,22 +127,6 @@ public class TeleportManager { } } - /** - * Get the home teleport audio based on tick count. - * @param count - */ - private static int getAudio(int count){ - switch(count){ - case 0: - return 193; - case 4: - return 194; - case 11: - return 195; - } - return -1; - } - /** * Gets the entity. * @return the Entity @@ -167,14 +135,6 @@ public class TeleportManager { return entity; } - /** - * Gets the last teleport pulse. - * @return the Pulse - */ - public final Pulse getLastTeleport() { - return lastTeleport; - } - /** * Gets the current teleport pulse. * @return the Pulse @@ -183,7 +143,6 @@ public class TeleportManager { return currentTeleport; } - /** * Represents a NodeType for Teleporter * @author SonicForce41 @@ -330,57 +289,6 @@ public class TeleportManager { }; } }, - HOME(new TeleportSettings(4847, 4857, 800, 804)) { - @Override - public Pulse getPulse(final Entity entity, final Location location) { - return new TeleportPulse(entity) { - int count; - Player player; - - @Override - public boolean pulse() { - switch (count) { - case 18: - player.getProperties().setTeleportLocation(location); - return true; - default: - playGlobalAudio(entity.getLocation(), getAudio(count)); - player.getPacketDispatch().sendGraphic(HOME_GRAPHICS[count]); - player.getPacketDispatch().sendAnimation(HOME_ANIMATIONS[count]); - break; - } - count++; - return false; - } - @Override - public void start() { - player = ((Player) entity); - /*if (player.getSavedData().getGlobalData().getHomeTeleportDelay() > System.currentTimeMillis() && !player.isDonator()) { - long milliseconds = player.getSavedData().getGlobalData().getHomeTeleportDelay() - System.currentTimeMillis(); - int minutes = (int) Math.round(milliseconds / 120000); - if (minutes > 15) { - player.getSavedData().getGlobalData().setHomeTeleportDelay(System.currentTimeMillis() + 1200000); - milliseconds = player.getSavedData().getGlobalData().getHomeTeleportDelay() - System.currentTimeMillis(); - minutes = (int) Math.round(milliseconds / 120000); - } - if (minutes != 0) { - player.getPacketDispatch().sendMessage("You need to wait another " + minutes + " " + (minutes == 1 ? "minute" : "minutes") + " to cast this spell."); - stop(); - return; - } - }*/ - super.start(); - } - - @Override - public void stop() { - super.stop(); - entity.getAnimator().forceAnimation(new Animation(-1)); - player.graphics(new Graphics(-1)); - } - }; - } - }, OBELISK(new TeleportSettings(8939, 8941, 661, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) {