diff --git a/Server/data/configs/item_configs.json b/Server/data/configs/item_configs.json
index bcfb80f63..e16807e0a 100644
--- a/Server/data/configs/item_configs.json
+++ b/Server/data/configs/item_configs.json
@@ -130888,7 +130888,7 @@
"bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
},
{
- "destroy_message": "You gained this orb from Dark Squall's base. You can probably get another one from visiting the same place.",
+ "destroy_message": "You can make another by casting the Charge Air Orb spell on the Abyssal Rift in the center of the Abyss.",
"examine": "This orb can used to teleport people...somehow.",
"durability": null,
"name": "Strange teleorb",
diff --git a/Server/src/main/content/global/handlers/item/SnowscapeTeleorb.kt b/Server/src/main/content/global/handlers/item/SnowscapeTeleorb.kt
new file mode 100644
index 000000000..9f5c33dae
--- /dev/null
+++ b/Server/src/main/content/global/handlers/item/SnowscapeTeleorb.kt
@@ -0,0 +1,140 @@
+package content.global.handlers.item
+
+import core.api.*
+import core.game.node.Node
+import core.game.node.entity.player.Player
+import core.game.node.entity.skill.Skills
+import core.game.node.item.Item
+import core.game.interaction.InteractionListener
+import core.game.interaction.IntType
+import core.game.world.map.Location
+import core.game.world.map.build.DynamicRegion
+import core.game.world.map.zone.ZoneRestriction
+import core.game.world.update.flag.context.Animation
+import core.game.world.update.flag.context.Graphics
+import core.game.node.entity.player.link.TeleportManager
+import core.game.dialogue.*
+import core.tools.START_DIALOGUE
+import org.rs09.consts.Items
+import org.rs09.consts.Sounds
+
+import core.game.component.Component
+
+/**
+ * Listener for the strange teleorb object. Normally this object is part of the While Guthix Sleeps quest, but it has been repurposed.
+ * The new purpose is a custom teleport artifact, allowing players to save a location and teleport back to it.
+ * @author
+ */
+class SnowscapeTeleorb : InteractionListener {
+
+ // The teleorb item id
+ val teleorb = 14534
+
+ // How many law runes are required for one teleport charge
+ val runesPerTeleport = 20
+
+ override fun defineListeners() {
+ on(teleorb, IntType.ITEM, "inspect") { player, node ->
+ inspect(player, node)
+ return@on true
+ }
+ on(teleorb, IntType.ITEM, "activate") { player, node ->
+ activate(player, node)
+ return@on true
+ }
+ }
+ private fun inspect(player: Player, node: Node) {
+ openDialogue(player,SnowscapeTeleorbDialogue())
+ }
+
+ private fun activate(player: Player, node: Node) {
+ val charges = getAttribute(player, "teleorb:charges",0) as Int
+ if (charges >= runesPerTeleport) {
+ val destination = getAttribute(player, "teleorb:location", Location.create(3236,3218,0))
+ player.getTeleporter().send(destination, TeleportManager.TeleportType.ENTRANA_MAGIC_DOOR)
+ setAttribute(player, "/save:teleorb:charges", charges - runesPerTeleport)
+ player.sendMessage("Charges remaining: " + ((getAttribute(player, "teleorb:charges",0) as Int) / runesPerTeleport))
+ rewardXP(player, Skills.MAGIC, 15.0 * runesPerTeleport)
+ } else {
+ player.sendMessage("
The teleorb is out of charges.")
+ }
+ }
+
+
+}
+
+
+
+
+class SnowscapeTeleorbDialogue : DialogueFile() {
+
+ // How many law runes are required for one teleport charge
+ val runesPerTeleport = 20
+
+ val teleorb = 14534
+ val lawRune = 563
+
+ override fun handle(componentID: Int, buttonID: Int) {
+ when (stage) {
+ START_DIALOGUE -> options("Update Location","Recharge","Show Info").also {stage++}
+
+ 1 -> when (buttonID) {
+ 1 -> player?.let {
+ end()
+ val region = it.getViewport().getRegion()
+ if (region == null || region is DynamicRegion || it.getZoneMonitor().isRestricted(ZoneRestriction.OFF_MAP)) {
+ it.sendMessage("This location cannot be saved!")
+ } else {
+ setAttribute(it, "/save:teleorb:location", it.getLocation())
+ sendInputDialogue(it, false, "Location saved. Enter a description of this location:",) { value ->
+ setAttribute(it, "/save:teleorb:description", value as String)
+ }
+ it.sendMessage("Location saved.")
+ it.graphics(Graphics(343))
+ it.animate(Animation(1818))
+ playAudio(it, Sounds.TELE_OTHER_CAST_199)
+ }
+ }
+ 2 -> options("Recharge with Law Runes","Recharge with additional orbs").also {stage++}
+ 3 -> player?.let {
+ end()
+ it.sendMessage("Current saved location: " + getAttribute(it, "teleorb:description", "Unknown").replace("_"," "))
+ it.sendMessage("Charges remaining: " + ((getAttribute(it, "teleorb:charges",0) as Int) / runesPerTeleport))
+ }
+ }
+ 2 -> when (buttonID) {
+ 1 -> player?.let {
+ end()
+ sendInputDialogue(it, true, "Each charge requires $runesPerTeleport Law Runes. How many runes would you like to use?",) { value ->
+ var amount = kotlin.math.min(amountInInventory(it,lawRune), value as Int)
+ if (removeItem(it, Item(lawRune, amount))) {
+ it.sendMessage("You charge the teleorb with $amount Law Runes.")
+ amount += (getAttribute(it, "teleorb:charges",0) as Int)
+ setAttribute(it, "/save:teleorb:charges", amount)
+ it.sendMessage("Current charges: " + ((getAttribute(it, "teleorb:charges",0) as Int) / runesPerTeleport))
+ it.graphics(Graphics(141,96))
+ it.animate(Animation(722))
+ playAudio(it, Sounds.LUNAR_EMBUE_RUNES_2888)
+ }
+ }
+ }
+ 2 -> player?.let {
+ end()
+ sendInputDialogue(it, true, "Each additional orb adds one charge. How many would you like to use?",) { value ->
+ var amount = kotlin.math.min(amountInInventory(it,teleorb) - 1, value as Int)
+ if (amount > 0 && removeItem(it, Item(teleorb, amount))) {
+ it.sendMessage("You destroy $amount teleorbs, fusing their energy into one")
+ amount = amount * runesPerTeleport
+ amount += (getAttribute(it, "teleorb:charges",0) as Int)
+ setAttribute(it, "/save:teleorb:charges", amount)
+ it.sendMessage("Current charges: " + ((getAttribute(it, "teleorb:charges",0) as Int) / runesPerTeleport))
+ it.graphics(Graphics(141,96))
+ it.animate(Animation(722))
+ playAudio(it, Sounds.LUNAR_EMBUE_RUNES_2888)
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Server/src/main/content/global/skill/magic/modern/ModernData.kt b/Server/src/main/content/global/skill/magic/modern/ModernData.kt
index 793a757b5..4740bd02d 100644
--- a/Server/src/main/content/global/skill/magic/modern/ModernData.kt
+++ b/Server/src/main/content/global/skill/magic/modern/ModernData.kt
@@ -50,6 +50,15 @@ enum class ChargeOrbData(
Graphics(150, 90),
Sounds.CHARGE_AIR_ORB_116,
Items.AIR_ORB_573
+ ),
+ CHARGE_TELE_ORB(
+ 7171,
+ arrayOf(Item(Items.COSMIC_RUNE_564, 3), Item(Items.AIR_RUNE_556, 30), Item(Items.UNPOWERED_ORB_567)),
+ 66,
+ 76.0,
+ Graphics(150, 90),
+ Sounds.CHARGE_AIR_ORB_116,
+ 14534
);
companion object{
val spellMap = HashMap()
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 edeee3520..16c7f8fbe 100644
--- a/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt
+++ b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt
@@ -138,6 +138,7 @@ class ModernListeners : SpellListener("modern"){
onCast(Modern.CHARGE_EARTH_ORB, OBJECT, Scenery.OBELISK_OF_EARTH_29415, 3, method = ::chargeOrb)
onCast(Modern.CHARGE_FIRE_ORB, OBJECT, Scenery.OBELISK_OF_FIRE_2153, 3, method = ::chargeOrb)
onCast(Modern.CHARGE_AIR_ORB, OBJECT, Scenery.OBELISK_OF_AIR_2152, 3, method = ::chargeOrb)
+ onCast(Modern.CHARGE_AIR_ORB, OBJECT, 7171, 3, method = ::chargeOrb)
}
private fun boneConvert(player: Player,bananas: Boolean){