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
This commit is contained in:
Player Name 2025-11-27 11:44:43 +00:00 committed by Ryan
parent feb2b10247
commit 3d5cbd90ed
30 changed files with 381 additions and 470 deletions

View file

@ -2,22 +2,57 @@ package content.global.ame
import core.ServerConstants import core.ServerConstants
import core.api.* 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.Player
import core.game.node.entity.player.link.TeleportManager.TeleportType import core.game.node.entity.player.link.TeleportManager.TeleportType
import core.game.world.map.Location 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) { fun kidnapPlayer(npc: NPC, player: Player, dest: Location, playerLine: String? = null, callback: (player: Player, npc: NPC) -> Unit) {
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. val lockDuration = if (playerLine != null) 4 else 6
if (getAttribute(player, "/save:original-loc", null) == null) { lock(player, lockDuration)
setAttribute(player, "/save:original-loc", player.location) 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<Location?>(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) { fun returnPlayer(player: Player) {
player.locks.unlockTeleport() player.locks.unlockTeleport()
val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION ?: Location.create(3222, 3218, 0)) val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION)
teleport(player, destination) teleport(player, destination!!)
unlock(player) unlock(player)
removeAttributes(player, "/save:original-loc", "kidnapped-by-random") removeAttributes(player, "/save:original-loc", "kidnapped-by-random")
} }

View file

@ -4,6 +4,7 @@ import content.global.ame.events.surpriseexam.MysteriousOldManNPC
import core.api.playGlobalAudio import core.api.playGlobalAudio
import core.api.poofClear import core.api.poofClear
import core.api.sendMessage import core.api.sendMessage
import core.api.setAttribute
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.MovementPulse import core.game.interaction.MovementPulse
import core.game.node.entity.Entity 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.map.path.Pathfinder
import core.game.world.update.flag.context.Graphics import core.game.world.update.flag.context.Graphics
import core.integrations.discord.Discord import core.integrations.discord.Discord
import core.tools.RandomFunction
import core.tools.secondsToTicks import core.tools.secondsToTicks
import core.tools.ticksToCycles import core.tools.ticksToCycles
import org.rs09.consts.NPCs
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.min import kotlin.math.min
@ -43,6 +46,7 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
event.loot = loot event.loot = loot
event.player = player event.player = player
event.spawnLocation = RegionManager.getSpawnLocation(player, this) event.spawnLocation = RegionManager.getSpawnLocation(player, this)
setAttribute(event, "spawned-by-ame", true)
return event return event
} }
@ -101,7 +105,6 @@ abstract class RandomEventNPC(id: Int) : NPC(id) {
} }
open fun onTimeUp() { open fun onTimeUp() {
noteAndTeleport()
terminate() 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 val index = min(ids.size, ceil(player.properties.currentCombatLevel / 20.0).toInt()) - 1
return ids[index] return ids[index]
} }
fun sayLine(npc: NPC, phrases: Array<String>, 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)
}
}
}
} }

View file

@ -97,5 +97,4 @@ enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = n
} }
} }
} }
} }

View file

@ -1,5 +1,6 @@
package content.global.ame.events package content.global.ame.events
import core.api.getAttribute
import core.game.node.entity.Entity import core.game.node.entity.Entity
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior import core.game.node.entity.npc.NPCBehavior
@ -8,12 +9,12 @@ import org.rs09.consts.NPCs
class HostileRandomEventBehavior : NPCBehavior( 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.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.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.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.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 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 { 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
} }
} }

View file

@ -4,42 +4,19 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC 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.map.Location
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.NPCs 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) { class PiousPeteNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PRIEST_3206) {
override fun init() { override fun init() {
super.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. // 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) face(player)
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> kidnapPlayer(this, player, Location(1972, 5002, 0)) { player, _ ->
when (stage) { CandlelightInterface.initCandlelight(player)
0 -> { openDialogue(player, PiousPeteStartingDialogueFile(), NPC(NPCs.PIOUS_PETE_3207))
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)
}
} }
} }

View file

@ -2,29 +2,19 @@ package content.global.ame.events.certer
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import core.game.node.entity.player.link.emote.Emotes import core.game.node.entity.player.link.emote.Emotes
import core.tools.RandomFunction
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import core.api.animate import core.api.animate
import core.api.lock
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GILES_2538) { class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GILES_2538) {
lateinit var pName: String
lateinit var phrases: Array<String> lateinit var phrases: Array<String>
override fun tick() { override fun tick() {
// Don't speak if we have the interface opened sayLine(this, phrases, true, true)
if (!timerPaused) { if (ticksLeft == 2) {
// Over allotted time phrase lock(player, 2)
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)])
}
} }
super.tick() super.tick()
} }
@ -36,15 +26,20 @@ class CerterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NP
override fun init() { override fun init() {
super.init() super.init()
pName = player.username.capitalize() phrases = arrayOf(
phrases = arrayOf("Greetings $pName, I need your help.", "Greetings ${player.username}, I need your help.",
"ehem... Hello $pName, please talk to me!", "ehem... Hello ${player.username}, please talk to me!",
"Hello, are you there $pName?", "Hello, are you there ${player.username}?",
"It's really rude to ignore someone, $pName!", "It's really rude to ignore someone, ${player.username}!",
"No-one ignores me!") "No-one ignores me!"
)
player.setAttribute("random:pause", false) player.setAttribute("random:pause", false)
player.setAttribute("certer:reward", false) player.setAttribute("certer:reward", false)
sendChat(phrases[0])
animate(this, Emotes.BOW.animation, true) animate(this, Emotes.BOW.animation, true)
} }
override fun onTimeUp() {
noteAndTeleport()
terminate()
}
} }

View file

@ -1,12 +1,9 @@
package content.global.ame.events.drilldemon package content.global.ame.events.drilldemon
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer import content.global.ame.returnPlayer
import core.api.* import core.api.*
import core.game.interaction.QueueStrength import core.game.interaction.QueueStrength
import core.game.node.entity.player.Player 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.map.zone.ZoneBorders
import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Animation
import org.rs09.consts.Items import org.rs09.consts.Items
@ -24,13 +21,6 @@ object DrillDemonUtils {
val DD_AREA = ZoneBorders(3158, 4817, 3168, 4823) val DD_AREA = ZoneBorders(3158, 4817, 3168, 4823)
val DD_NPC = NPCs.SERGEANT_DAMIEN_2790 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) { fun changeSignsAndAssignTask(player: Player) {
setVarp(player, DD_SIGN_VARP, 0) setVarp(player, DD_SIGN_VARP, 0)
val tempList = arrayListOf(DD_SIGN_JOG, DD_SIGN_JUMP, DD_SIGN_PUSHUP, DD_SIGN_SITUP).shuffled().toMutableList() 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) return@queueScript stopExecuting(player)
} }
} }
} }

View file

@ -3,32 +3,22 @@ package content.global.ame.events.drilldemon
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength import core.game.world.map.Location
import core.game.system.timer.impl.AntiMacro
import core.tools.secondsToTicks
class SergeantDamienNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SERGEANT_DAMIEN_2790) { class SergeantDamienNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SERGEANT_DAMIEN_2790) {
override fun init() { override fun init() {
super.init() super.init()
sendChat(player.username+ "! Drop and give me 20!") sendChat("${player.username}! Drop and give me 20!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> face(player)
when (stage) { kidnapPlayer(this, player, Location(3163, 4819, 0)) { player, _ ->
0 -> { player.interfaceManager.closeDefaultTabs()
lock(player, secondsToTicks(30)) setComponentVisibility(player, 548, 69, true)
DrillDemonUtils.teleport(player) setComponentVisibility(player, 746, 12, true)
AntiMacro.terminateEventNpc(player) openDialogue(player, SeargentDamienDialogue(isCorrect = true, eventStart = true), NPCs.SERGEANT_DAMIEN_2790)
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)
}
} }
} }

View file

@ -9,35 +9,27 @@ import org.rs09.consts.NPCs
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
class DrunkenDwarfNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.DRUNKEN_DWARF_956) { 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!") lateinit var phrases: Array<String>
private var attackPhrase = false
private var attackDelay = 0 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() { override fun init() {
super.init() super.init()
playGlobalAudio(this.location, Sounds.DWARF_WHISTLE_2297) phrases = arrayOf(
sendChat(this, "'Ello der ${player.username.capitalize()}! *hic*") "'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() { override fun tick() {
if (RandomFunction.roll(20) && !attackPhrase) sayLine(this, phrases, true, true)
sendPhrases()
if (ticksLeft <= 10) { if (ticksLeft <= 10) {
ticksLeft = 10 ticksLeft = 10
if (!attackPhrase) if (attackDelay <= getWorldTicks()) {
sendChat("I hates you, ${player.username.capitalize()}!").also { attackPhrase = true }
if (attackDelay <= getWorldTicks())
this.attack(player) this.attack(player)
}
} }
super.tick() super.tick()
} }
@ -47,4 +39,10 @@ class DrunkenDwarfNPC(override var loot: WeightBasedTable? = null) : RandomEvent
this.pulseManager.clear() this.pulseManager.clear()
openDialogue(player, DrunkenDwarfDialogue(), this.asNpc()) openDialogue(player, DrunkenDwarfDialogue(), this.asNpc())
} }
override fun onTimeUp() {
if (attackDelay <= getWorldTicks()) {
this.attack(player)
}
}
} }

View file

@ -1,40 +1,22 @@
package content.global.ame.events.evilbob package content.global.ame.events.evilbob
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC 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.NPCs
import org.rs09.consts.Sounds
class EvilBobNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.EVIL_BOB_2478) { class EvilBobNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.EVIL_BOB_2478) {
override fun init() { override fun init() {
super.init() super.init()
sendChat("meow") sendChat("meow")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> face(player)
when (stage) { kidnapPlayer(this, player, Location(3419, 4776, 0), "No... what? Nooooooooooooo!") { player, _ ->
0 -> { EvilBobUtils.giveEventFishingSpot(player)
lock(player, 6) sendMessage(player, "Welcome to Scape2009.")
sendChat(player, "No... what? Nooooooooooooo!") openDialogue(player, EvilBobDialogue(), NPCs.EVIL_BOB_2479)
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)
}
} }
} }

View file

@ -1,12 +1,8 @@
package content.global.ame.events.evilbob package content.global.ame.events.evilbob
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer
import core.api.* import core.api.*
import core.game.node.entity.player.Player 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.node.entity.skill.Skills
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneBorders
import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Animation
import core.game.world.update.flag.context.Graphics 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) { fun cleanup(player: Player) {
removeAttributes(player, assignedFishingZone, eventComplete, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen) removeAttributes(player, assignedFishingZone, eventComplete, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen)
removeAll(player, Items.FISHLIKE_THING_6202) removeAll(player, Items.FISHLIKE_THING_6202)

View file

@ -1,13 +1,10 @@
package content.global.ame.events.freakyforester package content.global.ame.events.freakyforester
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer import content.global.ame.returnPlayer
import core.api.* import core.api.*
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import core.game.node.entity.player.Player 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.map.zone.ZoneBorders
import core.tools.RandomFunction import core.tools.RandomFunction
@ -28,10 +25,6 @@ object FreakUtils{
player.dialogueInterpreter.open(FreakyForesterDialogue(), freakNpc) player.dialogueInterpreter.open(FreakyForesterDialogue(), freakNpc)
} }
fun teleport(player: Player) {
kidnapPlayer(player, Location.create(2599, 4777 ,0), TeleportManager.TeleportType.INSTANT)
}
fun cleanup(player: Player) { fun cleanup(player: Player) {
returnPlayer(player) returnPlayer(player)
removeAttributes(player, freakTask, freakComplete, pheasantKilled) removeAttributes(player, freakTask, freakComplete, pheasantKilled)

View file

@ -1,39 +1,21 @@
package content.global.ame.events.freakyforester package content.global.ame.events.freakyforester
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC import core.game.node.entity.npc.NPC
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.Sounds
class FreakyForesterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.FREAKY_FORESTER_2458) { class FreakyForesterNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.FREAKY_FORESTER_2458) {
override fun init() { override fun init() {
super.init() super.init()
sendChat("Ah, ${player.username}, just the person I need!") sendChat("Ah, ${player.username}, just the person I need!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> face(player)
when (stage) { kidnapPlayer(this, player, Location(2599, 4777, 0)) { player, _ ->
0 -> { FreakUtils.giveFreakTask(player)
lock(player, 6) openDialogue(player, FreakyForesterDialogue(), FreakUtils.freakNpc)
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)
}
} }
} }

View file

@ -4,27 +4,40 @@ import core.game.node.entity.npc.NPC
import core.tools.RandomFunction import core.tools.RandomFunction
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import core.api.lock
import core.api.playAudio import core.api.playAudio
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import org.rs09.consts.Sounds import org.rs09.consts.Sounds
class GenieNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.GENIE_409) { 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<String>
override fun tick() { override fun tick() {
if(RandomFunction.random(1,15) == 5){ sayLine(this, phrases, true, true)
sendChat(phrases.random().replace("@name",player.username.capitalize())) if (ticksLeft == 2) {
lock(player, 2)
} }
super.tick() super.tick()
} }
override fun init() { override fun init() {
super.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) playAudio(player, Sounds.GENIE_APPEAR_2301)
sendChat(phrases.random().replace("@name",player.username.capitalize()))
} }
override fun talkTo(npc: NPC) { override fun talkTo(npc: NPC) {
player.dialogueInterpreter.open(GenieDialogue(),npc) player.dialogueInterpreter.open(GenieDialogue(),npc)
} }
override fun onTimeUp() {
noteAndTeleport()
terminate()
}
} }

View file

@ -4,54 +4,29 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC 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.NPCs
import org.rs09.consts.Sounds
class MazeNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { class MazeNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) {
override fun init() { override fun init() {
super.init() super.init()
sendChat("Aha, you'll do ${player.username}!") sendChat("Aha, you'll do ${player.username}!")
face(player) face(player)
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> // Note: This event is NOT instanced:
when (stage) { // Sources:
0 -> { // https://youtu.be/2gpzn9oNdy0 (2007)
lock(player, 6) // https://youtu.be/Tni1HURgnxg (2008)
sendGraphics(Graphics(1576, 0, 0), player.location) // https://youtu.be/igdwDZOv9LU (2008)
animate(player,8939) // https://youtu.be/0oBCkLArUmc (2011 - even with personal Mysterious Old Man) - "Sorry, this is not the old man you are looking for."
playAudio(player, Sounds.TELEPORT_ALL_200) // https://youtu.be/FMuKZm-Ikgs (2011)
return@queueScript delayScript(player, 3) // val region = DynamicRegion.create(11591)
} kidnapPlayer(this, player, MazeInterface.STARTING_POINTS.random()) { player, _ ->
1 -> { MazeInterface.initMaze(player)
MazeInterface.initMaze(player) removeAttribute(player, MazeInterface.MAZE_ATTRIBUTE_CHESTS_OPEN)
// 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)
}
} }
} }
override fun talkTo(npc: NPC) { override fun talkTo(npc: NPC) {
// Do nothing. sendMessage(player, "He isn't interested in talking to you.")
} }
} }

View file

@ -4,47 +4,22 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable 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.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.NPCs
import org.rs09.consts.Sounds
// "::revent [-p] <lt>player name<gt> [-e <lt>event name<gt>]"
class PilloryNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PILLORY_GUARD_2791) { class PilloryNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PILLORY_GUARD_2791) {
override fun init() { override fun init() {
super.init() super.init()
sendChat("${player.username}, you're under arrest!") sendChat("${player.username}, you're under arrest!")
face(player) face(player)
player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.") kidnapPlayer(this, player, PilloryInterface.LOCATIONS.random()) { player, _ ->
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> PilloryInterface.initPillory(player)
when (stage) { setUnclosable(player, player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from."))
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)
}
} }
} }
override fun talkTo(npc: NPC) { override fun talkTo(npc: NPC) {
//player.dialogueInterpreter.open(FreakyForesterDialogue(),npc) sendMessage(player, "He isn't interested in talking to you.")
} }
} }

View file

@ -5,15 +5,17 @@ import core.ServerConstants
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.api.utils.WeightedItem 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.dialogue.FacialExpression
import core.game.interaction.QueueStrength import core.game.interaction.QueueStrength
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.tools.END_DIALOGUE
import org.rs09.consts.Components import org.rs09.consts.Components
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.NPCs
class QuizMasterDialogueFile : DialogueFile() { class QuizMasterDialogueFile : DialogueLabeller() {
companion object { companion object {
const val QUIZMASTER_INTERFACE = Components.MACRO_QUIZSHOW_191 const val QUIZMASTER_INTERFACE = Components.MACRO_QUIZSHOW_191
const val QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT = "/save:quizmaster:questions-correct" const val QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT = "/save:quizmaster:questions-correct"
@ -74,56 +76,77 @@ class QuizMasterDialogueFile : DialogueFile() {
} }
override fun handle(componentID: Int, buttonID: Int) { override fun addConversation() {
when (stage) { assignToIds(NPCs.QUIZ_MASTER_2477)
0 -> npc(FacialExpression.FRIENDLY,"WELCOME to the GREATEST QUIZ SHOW in the", "whole of ${ServerConstants.SERVER_NAME}:", "<col=8A0808>O D D</col> <col=8A088A>O N E</col> <col=08088A>O U T</col>").also { stage++ } afterClose { player ->
1 -> player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...").also { stage++ } loadLabel(player, "question")
2 -> npc(FacialExpression.FRIENDLY,"Please welcome our newest contestant:", "<col=FF0000>${player?.username}</col>!", "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!!)) npc(FacialExpression.FRIENDLY,"WELCOME to the GREATEST QUIZ SHOW in the", "whole of ${ServerConstants.SERVER_NAME}:", "<col=8A0808>O D D</col> <col=8A088A>O N E</col> <col=08088A>O U T</col>", unclosable = true)
player!!.interfaceManager.openChatbox(QUIZMASTER_INTERFACE) player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...", unclosable = true)
stage++ npc(FacialExpression.FRIENDLY,"Please welcome our newest contestant:", "<col=FF0000>${player?.username}</col>!", "Just pick the O D D O N E O U T.", "Four questions right, and then you win!", unclosable = true)
} goto("question")
4-> {
if (buttonID == getAttribute(player!!, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, 0)) { label("question")
// Correct Answer manual(unclosable = true) { player, _ ->
setAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, getAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + 1) setAttribute(player, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, randomQuestion(player))
if (getAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) >= 4) { val comp = Component(QUIZMASTER_INTERFACE)
npc(FacialExpression.FRIENDLY,"<col=08088A>CONGRATULATIONS!</col>", "You are a <col=8A0808>WINNER</col>!", "Please choose your <col=08088A>PRIZE</col>!") player.interfaceManager.openChatbox(comp)
stage = 5 return@manual comp
} else { }
npc(FacialExpression.FRIENDLY,"Wow, you're a smart one!", "You're absolutely RIGHT!", "Okay, next question!") exec { player, _ ->
stage = 3 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 { } else {
// Wrong Answer goto("right")
npc(FacialExpression.FRIENDLY,"WRONG!", "That's just WRONG!", "Okay, next question!")
stage = 3
} }
} } else {
// Random Item should be "Mystery Box", but the current MYSTERY_BOX_6199 is already inauthentically used by Giftmas. goto("wrong")
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()
} }
} }
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,"<col=08088A>CONGRATULATIONS!</col>", "You are a <col=8A0808>WINNER</col>!", "Please choose your <col=08088A>PRIZE</col>!", 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")
} }
} }

View file

@ -4,14 +4,9 @@ import content.global.ame.RandomEventNPC
import content.global.ame.kidnapPlayer import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC 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.Location
import core.game.world.update.flag.context.Graphics
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import org.rs09.consts.Sounds
/** /**
* Quiz Master NPC: * Quiz Master NPC:
@ -27,35 +22,14 @@ class QuizMasterNPC(var type: String = "", override var loot: WeightBasedTable?
override fun init() { override fun init() {
super.init() super.init()
sendChat("Hey ${player.username}! It's your lucky day!") sendChat("Hey ${player.username}! It's your lucky day!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> face(player)
when (stage) { kidnapPlayer(this, player, Location(1952, 4764, 0)) { player, _ ->
0 -> { setAttribute(player, QuizMasterDialogueFile.QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0)
lock(player, 6) sendMessage(player, "Answer four questions correctly in a row to be teleported back where you came from.")
sendGraphics(Graphics(1576, 0, 0), player.location) sendMessage(player, "You will need to relog in if you lose the quiz dialog.") // Inauthentic, but there to notify the player in case.
animate(player,8939) face(player, Location(1952, 4768, 1))
playAudio(player, Sounds.TELEPORT_ALL_200) animate(player,2378)
return@queueScript delayScript(player, 3) // Quiz dialogue gets opened automatically on zone entry.
}
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)
}
} }
} }

View file

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

View file

@ -5,32 +5,49 @@ import core.tools.RandomFunction
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import content.global.ame.RandomEventNPC import content.global.ame.RandomEventNPC
import core.api.lock
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
class SandwichLadyRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SANDWICH_LADY_3117) { 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<String>
var assigned_item = 0 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() { override fun tick() {
if(RandomFunction.random(1,15) == 5){ sayLine(this, phrases, true, true)
sendChat(phrases.random().replace("@name",player.username.capitalize())) if (ticksLeft == 2) {
lock(player, 2)
} }
super.tick() super.tick()
} }
override fun init() { override fun init() {
super.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() assignItem()
sendChat(phrases.random().replace("@name",player.username.capitalize())) }
override fun onTimeUp() {
noteAndTeleport()
terminate()
} }
fun assignItem(){ fun assignItem(){
assigned_item = items.random() assigned_item = items.random()
player.setAttribute("sandwich-lady:item",assigned_item) player.setAttribute("sandwich-lady:item", assigned_item)
} }
override fun talkTo(npc: NPC) { override fun talkTo(npc: NPC) {
player.dialogueInterpreter.open(SandwichLadyDialogue(false),npc) player.dialogueInterpreter.open(SandwichLadyDialogue(false), npc)
} }
} }

View file

@ -5,32 +5,16 @@ import content.global.ame.kidnapPlayer
import core.api.* import core.api.*
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
import core.api.utils.WeightBasedTable import core.api.utils.WeightBasedTable
import core.game.interaction.QueueStrength
import core.game.node.entity.npc.NPC 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.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) { class MysteriousOldManNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) {
override fun init() { override fun init() {
super.init() super.init()
sendChat("Surprise exam, ${player.username}!") sendChat("Surprise exam, ${player.username}!")
queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> face(player)
when (stage) { kidnapPlayer(this, player, Location(1886, 5025, 0)) { _, _ ->
0 -> { /* nothing needed */
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)
}
} }
} }

View file

@ -2,7 +2,6 @@ package content.global.ame.events.surpriseexam
import core.game.component.Component import core.game.component.Component
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.node.item.Item
import core.game.world.map.Location import core.game.world.map.Location
import org.rs09.consts.Items import org.rs09.consts.Items
import org.rs09.consts.NPCs import org.rs09.consts.NPCs
@ -66,6 +65,6 @@ class SupriseExamListeners : InteractionListener, MapArea {
} }
override fun getRestrictions(): Array<ZoneRestriction> { override fun getRestrictions(): Array<ZoneRestriction> {
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)
} }
} }

View file

@ -1,15 +1,12 @@
package content.global.ame.events.surpriseexam package content.global.ame.events.surpriseexam
import content.global.ame.kidnapPlayer
import content.global.ame.returnPlayer import content.global.ame.returnPlayer
import core.api.* import core.api.*
import core.game.node.entity.impl.PulseType import core.game.node.entity.impl.PulseType
import core.game.node.entity.player.Player import core.game.node.entity.player.Player
import core.game.system.task.Pulse import core.game.system.task.Pulse
import core.game.world.map.Location
import org.rs09.consts.Components import org.rs09.consts.Components
import org.rs09.consts.Items import org.rs09.consts.Items
import core.game.node.entity.player.link.TeleportManager
object SurpriseExamUtils { object SurpriseExamUtils {
val SE_KEY_INDEX = "supexam:index" val SE_KEY_INDEX = "supexam:index"

View file

@ -12,6 +12,7 @@ import core.game.system.timer.impl.AntiMacro
import core.game.worldevents.holiday.HolidayRandomEventNPC import core.game.worldevents.holiday.HolidayRandomEventNPC
import core.game.worldevents.holiday.HolidayRandomEvents import core.game.worldevents.holiday.HolidayRandomEvents
import core.game.worldevents.holiday.HolidayRandoms import core.game.worldevents.holiday.HolidayRandoms
import org.rs09.consts.NPCs
/** /**
* Handles the NPC talk-to option. * Handles the NPC talk-to option.
@ -37,11 +38,18 @@ class NPCTalkListener : InteractionListener {
val npc = node.asNpc() val npc = node.asNpc()
if(RandomEvents.randomIDs.contains(node.id)){ if(RandomEvents.randomIDs.contains(node.id)){
if(AntiMacro.getEventNpc(player) == null || AntiMacro.getEventNpc(player) != node.asNpc() || AntiMacro.getEventNpc(player)?.finalized == true) { 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 { } else {
AntiMacro.getEventNpc(player)?.talkTo(node.asNpc()) AntiMacro.getEventNpc(player)?.talkTo(node.asNpc())
} }
return@on true return@on true
//TODO bring sanity here
} }
if (HolidayRandomEvents.holidayRandomIDs.contains(node.id) && node is HolidayRandomEventNPC) { if (HolidayRandomEvents.holidayRandomIDs.contains(node.id) && node is HolidayRandomEventNPC) {
if(HolidayRandoms.getEventNpc(player) == null || HolidayRandoms.getEventNpc(player) != node.asNpc() || HolidayRandoms.getEventNpc(player)?.finalized == true) { 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) return@on player.dialogueInterpreter.open(npc.id, npc)
} }
} }
} }

View file

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

View file

@ -1,5 +1,6 @@
package content.global.skill.magic.ancient; 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.player.link.diary.DiaryType;
import core.game.node.entity.combat.spell.MagicSpell; import core.game.node.entity.combat.spell.MagicSpell;
import core.game.node.entity.combat.spell.Runes; 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.game.world.map.Location;
import core.plugin.Initializable; import core.plugin.Initializable;
import core.plugin.Plugin; import core.plugin.Plugin;
import core.tools.Log;
import core.tools.RandomFunction; 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. * Represents the plugin used to handle all ancient teleporting plugins.
* @author 'Vexia * @author 'Vexia
@ -55,7 +60,14 @@ public final class AncientTeleportPlugin extends MagicSpell {
entity.asPlayer().sendMessage("A magical force has stopped you from teleporting."); entity.asPlayer().sendMessage("A magical force has stopped you from teleporting.");
return false; 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)) { if (!super.meetsRequirements(entity, true, true)) {
entity.getTeleporter().getCurrentTeleport().stop(); entity.getTeleporter().getCurrentTeleport().stop();
return false; return false;

View file

@ -5,6 +5,7 @@ import content.global.skill.farming.CompostBins
import content.global.skill.farming.CompostType import content.global.skill.farming.CompostType
import content.global.skill.farming.FarmingPatch import content.global.skill.farming.FarmingPatch
import content.global.skill.magic.SpellListener import content.global.skill.magic.SpellListener
import content.global.skill.magic.homeTeleport
import content.global.skill.magic.spellconsts.Lunar import content.global.skill.magic.spellconsts.Lunar
import core.api.* import core.api.*
import core.game.component.CloseEvent import core.game.component.CloseEvent
@ -36,7 +37,7 @@ class LunarListeners : SpellListener("lunar"), Commands {
// Level 0 // Level 0
onCast(Lunar.HOME_TELEPORT, NONE) { player, _ -> onCast(Lunar.HOME_TELEPORT, NONE) { player, _ ->
requires(player) requires(player)
player.teleporter.send(Location.create(2100, 3914, 0),TeleportManager.TeleportType.HOME) homeTeleport(player, Location.create(2100, 3914, 0))
setDelay(player,true) setDelay(player,true)
} }

View file

@ -4,10 +4,12 @@ import content.data.Quests
import content.global.skill.magic.SpellListener import content.global.skill.magic.SpellListener
import content.global.skill.magic.SpellUtils.hasRune import content.global.skill.magic.SpellUtils.hasRune
import content.global.skill.magic.TeleportMethod import content.global.skill.magic.TeleportMethod
import content.global.skill.magic.homeTeleport
import content.global.skill.magic.spellconsts.Modern import content.global.skill.magic.spellconsts.Modern
import content.global.skill.prayer.Bones import content.global.skill.prayer.Bones
import content.global.skill.smithing.smelting.Bar import content.global.skill.smithing.smelting.Bar
import content.global.skill.smithing.smelting.SmeltingPulse import content.global.skill.smithing.smelting.SmeltingPulse
import content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners
import core.ServerConstants import core.ServerConstants
import core.api.* import core.api.*
import core.game.event.ItemAlchemizationEvent 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.TeleportManager
import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.player.link.diary.DiaryType
import core.game.node.entity.skill.Skills import core.game.node.entity.skill.Skills
import content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners
import core.game.node.item.Item import core.game.node.item.Item
import core.game.world.map.Location import core.game.world.map.Location
import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Animation
@ -39,7 +40,7 @@ class ModernListeners : SpellListener("modern"){
return@onCast return@onCast
} }
requires(player) requires(player)
player.teleporter.send(ServerConstants.HOME_LOCATION,TeleportManager.TeleportType.HOME) homeTeleport(player, ServerConstants.HOME_LOCATION ?: Location(3222, 3218, 0))
setDelay(player,true) setDelay(player,true)
} }

View file

@ -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. // 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. // 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. // 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 && !getLocks().isMovementLocked() && !getLocks().isInteractionLocked()) { if (GameWorld.getTicks() % 10 == 0 && ContentAPIKt.getAttribute(this, "/save:original-loc", null) != null && !ContentAPIKt.getAttribute(this, "kidnapped-by-random", false)) {
if (ContentAPIKt.getAttribute(this, "/save:original-loc", null) != null) { int rid = location.getRegionId();
int rid = location.getRegionId(); Region r = RegionManager.forId(rid);
Region r = RegionManager.forId(rid); if (!(r instanceof DynamicRegion) && !getZoneMonitor().isRestricted(ZoneRestriction.OFF_MAP)) {
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!");
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");
ContentAPIKt.removeAttribute(this, "original-loc");
}
} }
} }
} }

View file

@ -26,16 +26,6 @@ public class TeleportManager {
*/ */
public static final int WILDY_TELEPORT = 1 << 16 | 8; 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. * The entity being handled.
*/ */
@ -62,7 +52,6 @@ public class TeleportManager {
*/ */
public TeleportManager(Entity entity) { public TeleportManager(Entity entity) {
this.entity = entity; this.entity = entity;
lastTeleport = TeleportType.HOME.getPulse(entity, ServerConstants.HOME_LOCATION);
} }
/** /**
@ -116,16 +105,11 @@ public class TeleportManager {
} }
this.teleportType = teleportType; this.teleportType = teleportType;
entity.getWalkingQueue().reset(); entity.getWalkingQueue().reset();
lastTeleport = currentTeleport;
currentTeleport = type.getPulse(entity, location); currentTeleport = type.getPulse(entity, location);
entity.getPulseManager().clear(); entity.getPulseManager().clear();
if (type == TeleportType.HOME) { entity.lock(12);
entity.getPulseManager().run(type.getPulse(entity, location)); entity.getImpactHandler().setDisabledTicks(teleportType == -1 ? 5 : 12);
} else { GameWorld.getPulser().submit(currentTeleport);
entity.lock(12);
entity.getImpactHandler().setDisabledTicks(teleportType == -1 ? 5 : 12);
GameWorld.getPulser().submit(currentTeleport);
}
if (entity instanceof Player) { if (entity instanceof Player) {
((Player) entity).getInterfaceManager().close(); ((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. * Gets the entity.
* @return the Entity * @return the Entity
@ -167,14 +135,6 @@ public class TeleportManager {
return entity; return entity;
} }
/**
* Gets the last teleport pulse.
* @return the Pulse
*/
public final Pulse getLastTeleport() {
return lastTeleport;
}
/** /**
* Gets the current teleport pulse. * Gets the current teleport pulse.
* @return the Pulse * @return the Pulse
@ -183,7 +143,6 @@ public class TeleportManager {
return currentTeleport; return currentTeleport;
} }
/** /**
* Represents a NodeType for Teleporter * Represents a NodeType for Teleporter
* @author SonicForce41 * @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)) { OBELISK(new TeleportSettings(8939, 8941, 661, -1)) {
@Override @Override
public Pulse getPulse(final Entity entity, final Location location) { public Pulse getPulse(final Entity entity, final Location location) {