this onion has many layers:

Completely overhauled balloon minigame to use a grid system.
Overhauled crash sites
SHARK PATROL UWU
a bunch of other shit i forgot
This commit is contained in:
psyopcutie 2026-07-08 00:45:35 +02:00
parent 0734c04745
commit 673e011127
7 changed files with 485 additions and 175 deletions

View file

@ -157,7 +157,7 @@
},
{
"npc_id": "47",
"loc_data": "{2821,3170,0,1,1}-{3076,3282,0,1,5}-{3089,3266,0,1,4}-{3091,3266,0,1,4}-{3341,3267,0,1,5}-{3097,3364,0,1,3}-{3102,3363,0,1,5}-{3127,3487,0,1,4}-{3125,3486,0,1,6}-{3127,3486,0,1,4}-{2339,9356,0,1,0}-{2354,9390,0,1,0}-{2361,9403,0,1,0}-{2362,9347,0,1,0}-{2603,9480,0,1,1}-{2600,9477,0,1,0}-{2579,9496,0,1,4}-{2580,9508,0,1,0}-{2571,9522,0,1,4}-{2565,9505,0,1,1}-{2566,9510,0,1,6}-{2594,9497,0,1,4}-{2852,9642,0,1,6}-{2858,9632,0,1,3}-{2568,9620,0,1,0}-{2573,9612,0,1,0}-{2579,9631,0,1,0}-{2580,9600,0,1,0}-{2580,9614,0,1,0}-{2580,9620,0,1,0}-{2580,9626,0,1,0}-{2583,9632,0,1,0}-{2584,9625,0,1,0}-{2584,9637,0,1,0}-{2589,9644,0,1,0}-{2590,9601,0,1,0}-{2590,9638,0,1,0}-{2591,9601,0,1,0}-{2591,9621,0,1,0}-{2594,9636,0,1,0}-{2594,9644,0,1,0}-{2597,9604,0,1,0}-{2607,9615,0,1,0}-{2608,9628,0,1,0}-{2614,9651,0,1,0}-{2614,9656,0,1,0}-{2615,9647,0,1,0}-{2615,9661,0,1,0}-{2616,9633,0,1,0}-{2618,9630,0,1,0}-{3108,9754,0,1,5}-{3110,9754,0,1,5}-{3108,9750,0,1,5}-{2592,9831,0,1,3}-{2588,9825,0,1,6}-{2583,9829,0,1,4}-{2581,9841,0,1,0}-{2597,9823,0,1,2}-{2579,9805,0,1,3}-{2576,9804,0,1,0}-{2573,9805,0,1,5}-{2571,9808,0,1,3}-{2576,9810,0,1,2}-{2587,9802,0,1,2}-{2592,9800,0,1,4}-{2596,9805,0,1,6}-{2601,9802,0,1,5}-{2585,9801,0,1,7}-{2594,9803,0,1,0}-{2590,9806,0,1,3}-{2612,9808,0,1,6}-{2604,9810,0,1,6}-{2579,9821,0,1,2}-{2576,9812,0,1,6}-{2580,9813,0,1,6}-{2600,9813,0,1,4}-{2599,9809,0,1,4}-{3158,3226,0,1,5}-{3160,3202,0,1,4}-{3192,3203,0,1,0}-{3194,3204,0,1,0}-{3196,3206,0,1,0}-{3197,3204,0,1,0}-{2654,9640,0,1,6}-{2655,9637,0,1,4}-{2656,9639,0,1,7}-{2651,9636,0,1,5}-{2648,9637,0,1,4}-{2651,9642,0,1,1}-{2654,9640,0,1,0}-{2654,9635,0,1,6}-{2655,9635,0,1,3}-{2664,9626,0,1,6}-{2664,9624,0,1,1}-{2661,9623,0,1,1}-{2663,9623,0,1,3}-{2664,9626,0,1,6}-{2930,9699,0,1,0}-{2933,9697,0,1,0}-{2932,9685,0,1,0}-{2930,9693,0,1,0}-{3235,3224,0,1,3}-{3229,3220,0,1,4}-{3211,3211,0,1,3}-{3225,3220,0,1,1}-{3237,3215,0,1,5}-{3211,3210,0,1,7}-{3227,3220,0,1,7}-{3233,3227,0,1,5}-{3227,3210,0,1,6}-{3228,3222,0,1,4}-{3229,3226,0,1,0}-{3236,3217,0,1,4}-{3259,3230,0,1,4}-{3233,3237,0,1,7}-{3205,3204,0,1,0}-{3206,3204,0,1,0}-{3205,3203,0,1,0}-{3206,3202,0,1,0}-{3207,3202,0,1,0}-{3208,3203,0,1,0}-{3001,3202,0,1,5}-{3243,3687,0,1,5}-{3249,3669,0,1,3}-{3252,3675,0,1,4}-{3252,3680,0,1,3}-{3259,3683,0,1,0}-{3475,9840,0,1,6}-{3481,9842,0,1,1}-{3486,9843,0,1,7}-{3483,9824,0,1,4}-{3496,9808,0,0,5}-{3490,9815,0,1,1}-{3478,9834,0,0,3}-{3490,9824,0,1,4}-{3225,9862,0,1,4}-{3222,9861,0,1,6}-{3220,9860,0,1,6}-{3219,9865,0,1,6}-{3237,9862,0,1,4}-{2536,2982,0,1,3}-{2531,2980,0,1,0}-{2522,2981,0,1,4}-{2545,2989,0,1,4}-{2523,2970,0,1,2}-{3026,3174,0,1,5}-{3019,3176,0,1,7}-{2801,3158,0,1,2}-{2514,3193,0,1,6}-{2518,3192,0,1,3}-{2507,3181,0,1,3}-{2508,3178,0,1,6}-{2511,3183,0,1,3}-{2515,3182,0,1,1}-{3021,3205,0,1,6}-{3019,3292,0,1,7}-{3018,3295,0,1,7}-{2531,3325,0,1,3}-{2530,3327,0,1,5}-{2521,3331,0,1,3}-{2526,3328,0,1,3}-{2523,3331,0,1,4}-{2523,3334,0,1,1}-{2531,3329,0,1,5}-{2532,3333,0,1,5}-{3276,9871,0,1,1}-{3277,9871,0,1,3}-"
"loc_data": "{2821,3170,0,1,1}-{3341,3267,0,1,5}-{3076,3282,0,1,5}-{3089,3266,0,1,4}-{3091,3266,0,1,4}-{3097,3364,0,1,3}-{3102,3363,0,1,5}-{3127,3487,0,1,4}-{3125,3486,0,1,6}-{3127,3486,0,1,4}-{2339,9356,0,1,0}-{2354,9390,0,1,0}-{2361,9403,0,1,0}-{2362,9347,0,1,0}-{2603,9480,0,1,1}-{2600,9477,0,1,0}-{2579,9496,0,1,4}-{2580,9508,0,1,0}-{2571,9522,0,1,4}-{2565,9505,0,1,1}-{2566,9510,0,1,6}-{2594,9497,0,1,4}-{2852,9642,0,1,6}-{2858,9632,0,1,3}-{2568,9620,0,1,0}-{2573,9612,0,1,0}-{2579,9631,0,1,0}-{2580,9600,0,1,0}-{2580,9614,0,1,0}-{2580,9620,0,1,0}-{2580,9626,0,1,0}-{2583,9632,0,1,0}-{2584,9625,0,1,0}-{2584,9637,0,1,0}-{2589,9644,0,1,0}-{2590,9601,0,1,0}-{2590,9638,0,1,0}-{2591,9601,0,1,0}-{2591,9621,0,1,0}-{2594,9636,0,1,0}-{2594,9644,0,1,0}-{2597,9604,0,1,0}-{2607,9615,0,1,0}-{2608,9628,0,1,0}-{2614,9651,0,1,0}-{2614,9656,0,1,0}-{2615,9647,0,1,0}-{2615,9661,0,1,0}-{2616,9633,0,1,0}-{2618,9630,0,1,0}-{3108,9754,0,1,5}-{3110,9754,0,1,5}-{3108,9750,0,1,5}-{2592,9831,0,1,3}-{2588,9825,0,1,6}-{2583,9829,0,1,4}-{2581,9841,0,1,0}-{2597,9823,0,1,2}-{2579,9805,0,1,3}-{2576,9804,0,1,0}-{2573,9805,0,1,5}-{2571,9808,0,1,3}-{2576,9810,0,1,2}-{2587,9802,0,1,2}-{2592,9800,0,1,4}-{2596,9805,0,1,6}-{2601,9802,0,1,5}-{2585,9801,0,1,7}-{2594,9803,0,1,0}-{2590,9806,0,1,3}-{2612,9808,0,1,6}-{2604,9810,0,1,6}-{2579,9821,0,1,2}-{2576,9812,0,1,6}-{2580,9813,0,1,6}-{2600,9813,0,1,4}-{2599,9809,0,1,4}-{3158,3226,0,1,5}-{3160,3202,0,1,4}-{3192,3203,0,1,0}-{3194,3204,0,1,0}-{3196,3206,0,1,0}-{3197,3204,0,1,0}-{2654,9640,0,1,6}-{2655,9637,0,1,4}-{2656,9639,0,1,7}-{2651,9636,0,1,5}-{2648,9637,0,1,4}-{2651,9642,0,1,1}-{2654,9640,0,1,0}-{2654,9635,0,1,6}-{2655,9635,0,1,3}-{2664,9626,0,1,6}-{2664,9624,0,1,1}-{2661,9623,0,1,1}-{2663,9623,0,1,3}-{2664,9626,0,1,6}-{2930,9699,0,1,0}-{2933,9697,0,1,0}-{2932,9685,0,1,0}-{2930,9693,0,1,0}-{3235,3224,0,1,3}-{3229,3220,0,1,4}-{3211,3211,0,1,3}-{3225,3220,0,1,1}-{3237,3215,0,1,5}-{3211,3210,0,1,7}-{3227,3220,0,1,7}-{3233,3227,0,1,5}-{3227,3210,0,1,6}-{3228,3222,0,1,4}-{3229,3226,0,1,0}-{3236,3217,0,1,4}-{3259,3230,0,1,4}-{3233,3237,0,1,7}-{3205,3204,0,1,0}-{3206,3204,0,1,0}-{3205,3203,0,1,0}-{3206,3202,0,1,0}-{3207,3202,0,1,0}-{3208,3203,0,1,0}-{3001,3202,0,1,5}-{3243,3687,0,1,5}-{3249,3669,0,1,3}-{3252,3675,0,1,4}-{3252,3680,0,1,3}-{3259,3683,0,1,0}-{3475,9840,0,1,6}-{3481,9842,0,1,1}-{3486,9843,0,1,7}-{3483,9824,0,1,4}-{3496,9808,0,0,5}-{3490,9815,0,1,1}-{3478,9834,0,0,3}-{3490,9824,0,1,4}-{3225,9862,0,1,4}-{3222,9861,0,1,6}-{3220,9860,0,1,6}-{3219,9865,0,1,6}-{3237,9862,0,1,4}-{2536,2982,0,1,3}-{2531,2980,0,1,0}-{2522,2981,0,1,4}-{2545,2989,0,1,4}-{2523,2970,0,1,2}-{3026,3174,0,1,5}-{3019,3176,0,1,7}-{2801,3158,0,1,2}-{2514,3193,0,1,6}-{2518,3192,0,1,3}-{2507,3181,0,1,3}-{2508,3178,0,1,6}-{2511,3183,0,1,3}-{2515,3182,0,1,1}-{3021,3205,0,1,6}-{3019,3292,0,1,7}-{3018,3295,0,1,7}-{2531,3325,0,1,3}-{2530,3327,0,1,5}-{2521,3331,0,1,3}-{2526,3328,0,1,3}-{2523,3331,0,1,4}-{2523,3334,0,1,1}-{2531,3329,0,1,5}-{2532,3333,0,1,5}-{3276,9871,0,1,1}-{3277,9871,0,1,3}-"
},
{
"npc_id": "48",
@ -8641,15 +8641,15 @@
},
{
"npc_id": "5067",
"loc_data": "{1799,4889,0,1,4}-"
"loc_data": "{1799,4885,0,0,3}-"
},
{
"npc_id": "5068",
"loc_data": "{1807,4889,0,1,3}-"
"loc_data": "{1805,4885,0,0,3}-"
},
{
"npc_id": "5069",
"loc_data": "{1807,4893,0,1,3}-"
"loc_data": "{1807,4893,0,0,6}-"
},
{
"npc_id": "5070",

View file

@ -8,20 +8,25 @@ import core.api.delayScript
import core.api.face
import core.api.getAttribute
import core.api.getRegionBorders
import core.api.inBorders
import core.api.openDialogue
import core.api.openOverlay
import core.api.playAudio
import core.api.queueScript
import core.api.removeAttribute
import core.api.sendMessage
import core.api.sendDialogue
import core.api.sendPlayerDialogue
import core.api.setAttribute
import core.api.stopExecuting
import core.api.teleport
import core.api.unlock
import core.game.dialogue.DialogueFile
import core.game.dialogue.DialogueLabeller
import core.game.dialogue.FacialExpression
import core.game.interaction.IntType
import core.game.interaction.InteractionListener
import core.game.interaction.QueueStrength
import core.game.node.entity.Entity
import core.game.node.entity.player.Player
import core.game.world.map.Location
import core.game.world.map.zone.ZoneBorders
@ -73,7 +78,7 @@ object BalloonCrashSites {
fadeTransition(player) {
closeFlightInterface(player)
teleport(player, exit)
sendMessage(player, "You bail, but land safely on Entrana.")
sendDialogue(player, "You bail, but land safely on Entrana.")
}
}
@ -82,21 +87,23 @@ object BalloonCrashSites {
fadeTransition(player) {
closeFlightInterface(player)
teleport(player, OCEAN_CRASH_LANDING)
oceanSwimmingAnimations(player)
oceanSwimmingAnimation(player)
face(player, OCEAN_CRASH_FACE_LOCATION)
sendMessage(player, "The balloon crashes.")
sendDialogue(player, "The balloon crashes.")
}
}
fun leaveOceanCrashSite(player: Player) {
sendMessage(player, "You climb onto the plank and swim to shore.")
fadeTransition(player, fadeToBlack = true) {
player.appearance.setAnimations()
player.appearance.sync()
teleport(player, OCEAN_CRASH_EXIT)
face(player, OCEAN_CRASH_EXIT_FACE_LOCATION)
sendMessage(player, "You manage to swim back to shore.")
}
player.appearance.setAnimations()
player.appearance.sync()
teleport(player, OCEAN_CRASH_EXIT)
face(player, OCEAN_CRASH_EXIT_FACE_LOCATION)
sendDialogue(player, "You manage to swim back to shore.")
}
fun oceanSwimmingAnimation(player: Player) {
player.appearance.setAnimations(Animation(RENDER_ANIMATION_SWIM_188))
player.appearance.sync()
}
fun crashAtWoodlandSite(player: Player, exit: WoodlandExit) {
@ -106,13 +113,12 @@ object BalloonCrashSites {
closeFlightInterface(player)
teleport(player, WOODLAND_CRASH_LANDING)
face(player, WOODLAND_CRASH_FACE_LOCATION)
sendMessage(player, "The balloon crashes.")
sendDialogue(player, "The balloon crashes.")
}
}
fun leaveWoodlandCrashSite(player: Player) {
val exit = getWoodlandExit(player)
sendMessage(player, "You walk away from the crash site.")
fadeTransition(player, fadeToBlack = true) {
when (exit) {
WoodlandExit.FALADOR -> {
@ -177,23 +183,21 @@ object BalloonCrashSites {
return@queueScript stopExecuting(player)
}
}
private fun oceanSwimmingAnimations(player: Player) {
player.appearance.setAnimations(Animation(RENDER_ANIMATION_SWIM_188))
player.appearance.sync()
}
}
class BalloonCrashSiteListener : InteractionListener, MapArea {
override fun defineListeners() {
on(BalloonCrashSites.SCENERY_OCEAN_PLANK_19126, IntType.SCENERY, "use") { player, _ ->
BalloonCrashSites.leaveOceanCrashSite(player)
openDialogue(player, BalloonCrashExitDialogue("You climb onto the plank and swim to shore.") {
BalloonCrashSites.leaveOceanCrashSite(it)
})
return@on true
}
on(BalloonCrashSites.SCENERY_WOODLAND_DIRT_19125, IntType.SCENERY, "leave") { player, _ ->
BalloonCrashSites.leaveWoodlandCrashSite(player)
openDialogue(player, BalloonCrashExitDialogue("You walk away from the crash site.") {
BalloonCrashSites.leaveWoodlandCrashSite(it)
})
return@on true
}
@ -209,6 +213,15 @@ class BalloonCrashSiteListener : InteractionListener, MapArea {
}
override fun areaEnter(entity: Entity) {
if (entity !is Player || !inBorders(entity, 1795, 4883, 1810, 4898)) return
queueScript(entity, 0, QueueStrength.SOFT) {
BalloonCrashSites.oceanSwimmingAnimation(entity)
return@queueScript stopExecuting(entity)
}
}
override fun defineAreaBorders(): Array<ZoneBorders> {
return arrayOf(getRegionBorders(BalloonCrashSites.CRASH_SITE_REGION_7244))
}
@ -218,6 +231,20 @@ class BalloonCrashSiteListener : InteractionListener, MapArea {
}
}
/** Displays a crash site exit message and continues the exit after the player clicks Continue. */
private class BalloonCrashExitDialogue(private val message: String, private val onContinue: (Player) -> Unit) : DialogueFile() {
override fun handle(componentID: Int, buttonID: Int) {
when (stage) {
0 -> sendDialogue(player!!, message).also { stage++ }
1 -> {
val player = player!!
end()
onContinue(player)
}
}
}
}
class BalloonCrashDialogue : DialogueLabeller() {
override fun addConversation() {
assignToIds(BalloonCrashSites.NPC_OCEAN_AUGUSTE_5051)
@ -230,18 +257,17 @@ class BalloonCrashDialogue : DialogueLabeller() {
}
label("ocean_crash")
npc("Great Scott! Ocean! Ahhh!")
player("What happened?")
npc("Crashed, off course. Ocean! Oh dear... I feel very...")
player("Contemplative? Concerned for your life? Desperate?")
npc("No...more along the lines of ill...You might want to swim away now. Use the plank...")
player("How are you going to get back? What about the balloon?")
npc("I'm sure a fishing trawler will come by and give me a lift. Don't worry, I shall return.")
npc(FacialExpression.SCARED, "Great Scott! Ocean! Ahhh!")
player(FacialExpression.SCARED, "What happened?")
npc(FacialExpression.SCARED, "Crashed, off course. Ocean! Oh dear... I feel very...")
player(FacialExpression.ASKING, "Contemplative? Concerned for your life? Desperate?")
npc(FacialExpression.SCARED, "No...more along the lines of ill...You might want to swim away now. Use the plank...")
player(FacialExpression.SCARED, "How are you going to get back? What about the balloon?")
npc(FacialExpression.THINKING, "I'm sure a fishing trawler will come by and give me a lift. Don't worry, I shall return.")
label("woodland_crash")
npc("Well, that didn't go as planned.")
npc(FacialExpression.ANGRY, "Well, that didn't go as planned.")
player("So what now? How will we get the balloon back?")
npc("I'll have to get one of the monks to come pick it up. You head back on your own; just follow the path. I'll meet you back at Entrana.")
npc(FacialExpression.SAD, "I'll have to get one of the monks to come pick it up. You head back on your own; just follow the path. I'll meet you back at Entrana.")
}
}

View file

@ -0,0 +1,219 @@
package content.global.travel.balloon
/** Row and column within the balloon interface grid, starting from the top-left. */
data class BalloonGridPosition(val row: Int, val column: Int)
/**
* Defines the interface grid and collision points for the balloon minigame.
* @author Edith
*/
object BalloonFlightGrid {
private const val GRID_ROWS = 13
private const val GRID_COLUMNS = 20
private const val TOP_LEFT_GRID_CHILD = 258
private val FLIGHT_AREA_ROWS = 2..9
const val SCREEN_ONE = 1
const val SCREEN_TWO = 2
const val SCREEN_THREE = 3
private val childGrid = Array(GRID_ROWS) { row ->
IntArray(GRID_COLUMNS) { column -> TOP_LEFT_GRID_CHILD - row * GRID_COLUMNS + column }
}.apply {
// Children 98 and 99 are reversed in interface 470.
this[8][0] = 99
this[8][1] = 98
}
/** Interface model children that cause a collision on each flight screen. */
private val collisionGrids = mapOf(
BalloonTravelLocation.TAVERLEY to mapOf(
SCREEN_ONE to setOf(
78, 82, 83, 84, 85, 86, 87, 88, 89, 91,
92, 102, 103, 104, 105, 106, 107, 108, 109, 123,
124, 125, 127, 128, 145, 148, 155, 156, 172, 173,
174, 175, 177, 209, 210, 211, 212, 213, 227, 228,
229, 230
),
SCREEN_TWO to setOf(
83, 84, 90, 91, 92, 93, 94, 95, 96, 97,
103, 111, 112, 113, 114, 115, 116, 117, 123, 133,
134, 135, 136, 137, 154, 155, 156, 157, 158, 161,
179, 180, 181, 185, 186, 187, 188, 201, 202, 203,
204, 219
),
SCREEN_THREE to setOf(
78, 79, 80, 81, 82, 83, 84, 85, 86, 92,
93, 97, 98, 99, 100, 101, 102, 103, 106, 113,
118, 119, 120, 121, 126, 138, 146, 151, 168, 169,
170, 171, 174, 175, 176, 191, 192, 207, 208, 212,
213, 214, 215
)
),
BalloonTravelLocation.CRAFTING_GUILD to mapOf(
SCREEN_ONE to setOf(
78, 79, 80, 81, 82, 83, 84, 88, 89, 90,
91, 93, 96, 97, 98, 99, 100, 102, 103, 104,
108, 109, 110, 111, 113, 116, 117, 118, 119, 122,
124, 128, 129, 130, 131, 133, 137, 138, 148, 151,
162, 163, 164, 171, 175, 183, 192, 193, 194, 206,
220, 221, 233, 234, 235
),
SCREEN_TWO to setOf(
78, 79, 83, 84, 87, 89, 92, 96, 97, 98,
99, 103, 104, 107, 109, 112, 119, 124, 127, 132,
161, 168, 169, 170, 171, 187, 188, 193, 194, 195,
200, 201, 202, 205, 206, 207, 216, 217
),
SCREEN_THREE to setOf(
78, 79, 80, 83, 84, 85, 86, 87, 88, 89,
90, 97, 100, 103, 104, 105, 107, 108, 110, 120,
123, 130, 143, 163, 166, 167, 168, 173, 174, 175,
198, 199, 209, 210, 211, 212, 222, 223, 224, 228,
229, 230
)
),
BalloonTravelLocation.VARROCK to mapOf(
SCREEN_ONE to setOf(
78, 79, 80, 83, 86, 87, 88, 89, 90, 91,
93, 94, 95, 97, 99, 100, 107, 108, 109, 110,
111, 113, 114, 115, 117, 120, 122, 127, 129, 131,
135, 137, 144, 145, 146, 157, 161, 163, 170, 174,
188, 189, 190, 193, 194, 195, 198, 199, 200, 206,
207, 208, 216, 232
),
SCREEN_TWO to setOf(
79, 81, 82, 83, 85, 86, 87, 88, 89, 93,
94, 95, 97, 98, 101, 102, 103, 105, 106, 107,
108, 109, 113, 114, 115, 117, 119, 122, 125, 126,
127, 128, 129, 134, 135, 137, 139, 145, 146, 147,
148, 149, 154, 166, 167, 168, 171, 172, 173, 187,
190, 192, 200, 201, 202, 209, 214, 215, 216, 217,
218, 224, 225, 226, 236
),
SCREEN_THREE to setOf(
79, 80, 81, 83, 84, 85, 86, 87, 88, 89,
90, 91, 92, 93, 94, 97, 98, 100, 101, 103,
104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
114, 119, 120, 123, 124, 125, 126, 127, 128, 129,
130, 131, 132, 133, 134, 141, 143, 144, 145, 146,
147, 148, 149, 150, 151, 152, 153, 154, 163, 164,
167, 168, 171, 172, 183, 184, 187, 188, 191, 192,
198, 199, 201, 202, 225, 230
)
),
BalloonTravelLocation.CASTLE_WARS to mapOf(
SCREEN_ONE to setOf(
78, 80, 83, 84, 85, 92, 93, 97, 100, 105,
112, 120, 130, 136, 140, 147, 164, 165, 166, 169,
170, 175, 176, 177, 181, 182, 183, 193, 207, 212,
213, 214, 227, 228, 229
),
SCREEN_TWO to setOf(
79, 85, 86, 87, 88, 89, 90, 91, 92, 93,
95, 96, 107, 108, 109, 110, 111, 112, 113, 115,
116, 118, 129, 130, 131, 132, 133, 135, 136, 140,
141, 142, 152, 153, 156, 158, 159, 173, 180, 181,
189, 190, 191, 197, 203, 204, 205, 215, 216, 217,
228, 229, 230, 231
),
SCREEN_THREE to setOf(
78, 80, 83, 84, 85, 86, 87, 92, 93, 97,
99, 100, 103, 104, 105, 106, 107, 112, 113, 118,
120, 123, 124, 125, 126, 127, 133, 138, 140, 147,
153, 170, 171, 172, 179, 183, 184, 185, 186, 198,
199, 208, 209, 210, 211, 215, 216, 217, 222, 223
)
),
BalloonTravelLocation.GRAND_TREE to mapOf(
SCREEN_ONE to setOf(
78, 79, 80, 81, 84, 85, 89, 90, 93, 94,
97, 98, 99, 100, 104, 110, 118, 119, 130, 138,
144, 186, 187, 188, 191, 192, 193, 194, 195, 201,
202, 203, 214, 229, 230, 231, 232, 237
),
SCREEN_TWO to setOf(
79, 80, 81, 82, 83, 84, 87, 88, 89, 90,
91, 92, 94, 95, 96, 97, 100, 101, 102, 103,
104, 107, 108, 109, 110, 111, 112, 114, 115, 116,
117, 120, 122, 124, 127, 128, 129, 130, 132, 134,
135, 140, 142, 144, 147, 149, 150, 152, 154, 167,
172, 181, 183, 191, 192, 196, 197, 200, 201, 203,
204, 205, 208, 209, 214, 215, 216, 217, 219, 220,
224, 228
),
SCREEN_THREE to setOf(
78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 97, 98, 99, 100, 101,
102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
112, 120, 121, 122, 123, 124, 125, 126, 127, 128,
129, 130, 131, 132, 140, 141, 142, 143, 144, 145,
146, 147, 148, 149, 150, 151, 161, 162, 163, 164,
165, 166, 167, 168, 169, 170, 174, 175, 178, 182,
183, 187, 188, 189, 198, 199, 200, 212, 213, 214,
225
)
)
)
/** Grid columns for crashing over ocean. */
private val oceanColumns = mapOf(
BalloonTravelLocation.TAVERLEY to mapOf(
SCREEN_ONE to 17..19,
SCREEN_TWO to 0..7
),
BalloonTravelLocation.CRAFTING_GUILD to mapOf(
SCREEN_TWO to 4..19,
SCREEN_THREE to 0..2
),
BalloonTravelLocation.VARROCK to mapOf(
SCREEN_ONE to 2..6
),
BalloonTravelLocation.CASTLE_WARS to mapOf(
SCREEN_ONE to 4..19,
SCREEN_TWO to 0..5
),
BalloonTravelLocation.GRAND_TREE to mapOf(
SCREEN_ONE to 4..19
)
)
/** Checks whether balloon position is above the ocean. */
fun isOverOcean(destination: BalloonTravelLocation, screen: Int, position: BalloonGridPosition): Boolean {
return oceanColumns[destination]?.get(screen)?.contains(position.column) == true
}
/** Gets the interface child occupied by a grid position. */
fun getGridChild(position: BalloonGridPosition): Int? {
if (position.row !in childGrid.indices) {
return null
}
if (position.column !in childGrid[position.row].indices) {
return null
}
return childGrid[position.row][position.column]
}
/** Checks whether a grid position is inside the flight area. */
fun isInFlightArea(position: BalloonGridPosition): Boolean {
return position.row in FLIGHT_AREA_ROWS && position.column in 0 until GRID_COLUMNS
}
/** Checks whether either part of the balloon collides with the flight boundary or a model. */
fun willBalloonCollide(destination: BalloonTravelLocation, screen: Int, position: BalloonGridPosition): Boolean {
val topPosition = position.copy(row = position.row - 1)
if (position.column == GRID_COLUMNS) {
return topPosition.row !in FLIGHT_AREA_ROWS || position.row !in FLIGHT_AREA_ROWS
}
if (!isInFlightArea(topPosition) || !isInFlightArea(position)) {
return true
}
val collisionChildren = collisionGrids[destination]?.get(screen) ?: return true
val topChild = getGridChild(topPosition) ?: return true
val bottomChild = getGridChild(position) ?: return true
return topChild in collisionChildren || bottomChild in collisionChildren
}
}

View file

@ -7,6 +7,7 @@ import core.api.closeOverlay
import core.api.closeTabInterface
import core.api.face
import core.api.getAttribute
import core.api.getVarbit
import core.api.openInterface
import core.api.openOverlay
import core.api.openSingleTab
@ -29,7 +30,6 @@ import core.net.packet.context.MinimapStateContext
import core.net.packet.out.MinimapState
import core.plugin.Initializable
import org.rs09.consts.Components
import kotlin.math.abs
/**
* Handles the balloon flight minigame interface.
@ -54,13 +54,18 @@ class BalloonFlightInterface : InterfaceListener {
private const val BALLOON_FLIGHT_INTERFACE_470 = 470
private const val BALLOON_CONTROL_INTERFACE_471 = 471
private const val ATTRIBUTE_BALLOON_TOP = "enlightenedjourney_balloon_top"
private const val ATTRIBUTE_BALLOON_BOTTOM = "enlightenedjourney_balloon_bottom"
private const val ATTRIBUTE_BALLOON_ROW = "enlightenedjourney_balloon_row"
private const val ATTRIBUTE_BALLOON_COLUMN = "enlightenedjourney_balloon_column"
private const val ATTRIBUTE_FLIGHT_PROGRESS = "enlightenedjourney_balloon_flight_progress"
private const val ATTRIBUTE_FLIGHT_SCREEN = "enlightenedjourney_balloon_flight_screen"
private const val ATTRIBUTE_FLIGHT_DESTINATION = "enlightenedjourney_balloon_flight_destination"
private const val VARBIT_SANDBAGS_REMAINING_2880 = 2880
private const val VARBIT_LOGS_REMAINING_2881 = 2881
private const val STARTING_SANDBAGS = 8
private const val STARTING_LOGS = 10
private const val SOUND_SANDBAG_3249 = 3249
private const val SOUND_BURN_LOG_3251 = 3251
private const val SOUND_RELAX_3247 = 3247
@ -87,7 +92,8 @@ class BalloonFlightInterface : InterfaceListener {
setAttribute(player, ATTRIBUTE_FLIGHT_DESTINATION, BalloonTravelLocation.TAVERLEY)
setAttribute(player, ATTRIBUTE_FLIGHT_PROGRESS, 0)
setAttribute(player, ATTRIBUTE_FLIGHT_SCREEN, 1)
setBalloonPosition(player, startPosition.top, startPosition.bottom)
setBalloonPosition(player, startPosition)
updateFlightSupplies(player)
closeInterface(player)
hideGameTabs(player)
hideMinimap(player)
@ -101,7 +107,8 @@ class BalloonFlightInterface : InterfaceListener {
setAttribute(player, ATTRIBUTE_FLIGHT_DESTINATION, destination)
setAttribute(player, ATTRIBUTE_FLIGHT_PROGRESS, 0)
setAttribute(player, ATTRIBUTE_FLIGHT_SCREEN, 1)
setBalloonPosition(player, startPosition.top, startPosition.bottom)
setBalloonPosition(player, startPosition)
updateFlightSupplies(player)
closeInterface(player)
hideGameTabs(player)
hideMinimap(player)
@ -113,7 +120,7 @@ class BalloonFlightInterface : InterfaceListener {
return getAttribute(player, ATTRIBUTE_FLIGHT_DESTINATION, BalloonTravelLocation.TAVERLEY)
}
private fun getScreenPosition(destination: BalloonTravelLocation, screen: Int): BalloonPosition {
private fun getScreenPosition(destination: BalloonTravelLocation, screen: Int): BalloonGridPosition {
return when (destination) {
BalloonTravelLocation.TAVERLEY -> when (screen) {
1 -> BalloonFlightRoutes.TAVERLEY_START
@ -170,70 +177,85 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun setBalloonPosition(player: Player, topChild: Int, bottomChild: Int) {
setAttribute(player, ATTRIBUTE_BALLOON_TOP, topChild)
setAttribute(player, ATTRIBUTE_BALLOON_BOTTOM, bottomChild)
private fun setBalloonPosition(player: Player, position: BalloonGridPosition) {
setAttribute(player, ATTRIBUTE_BALLOON_ROW, position.row)
setAttribute(player, ATTRIBUTE_BALLOON_COLUMN, position.column)
}
/** Moves the balloon model on the interface according to the selected control. */
private fun moveBalloon(player: Player, move: BalloonMove) {
val startPosition = BalloonFlightRoutes.getStartPosition(getFlightDestination(player))
val oldTop = getAttribute(player, ATTRIBUTE_BALLOON_TOP, startPosition.top)
val oldBottom = getAttribute(player, ATTRIBUTE_BALLOON_BOTTOM, startPosition.bottom)
var newTop = oldTop
var newBottom = oldBottom
if (newBottom == 98) {
newBottom += 2
}
newTop += move.east
newBottom += move.east
repeat(abs(move.vertical)) {
if (move.vertical < 0) {
newTop = moveNorth(newTop)
newBottom = moveNorth(newBottom)
} else {
newTop = moveSouth(newTop)
newBottom = moveSouth(newBottom)
}
}
if (move.vertical < 0 && newTop - newBottom == 21) {
newBottom += 1
}
BalloonFlightScreens.clearBalloon(player, oldTop, oldBottom)
setBalloonPosition(player, newTop, newBottom)
BalloonFlightScreens.drawBalloon(player, newTop, newBottom)
private fun getBalloonPosition(player: Player, destination: BalloonTravelLocation, screen: Int): BalloonGridPosition {
val startPosition = getScreenPosition(destination, screen)
return BalloonGridPosition(
getAttribute(player, ATTRIBUTE_BALLOON_ROW, startPosition.row),
getAttribute(player, ATTRIBUTE_BALLOON_COLUMN, startPosition.column)
)
}
private fun moveNorth(child: Int) = child + if (child >= 111) 20 else 19
private fun moveSouth(child: Int) = child - 20
/** Moves the balloon model on the interface according to the selected control, or returns false if it would collide. */
private fun moveBalloon(player: Player, move: BalloonMove): Boolean {
val destination = getFlightDestination(player)
val screen = getAttribute(player, ATTRIBUTE_FLIGHT_SCREEN, 1)
val oldPosition = getBalloonPosition(player, destination, screen)
val newPosition = oldPosition.copy(row = oldPosition.row + move.vertical, column = oldPosition.column + move.east)
if (BalloonFlightGrid.willBalloonCollide(destination, screen, newPosition)) {
handleBalloonCrash(player, getCrashLocation(destination, screen, newPosition))
return false
}
BalloonFlightScreens.clearBalloon(player, oldPosition)
setBalloonPosition(player, newPosition)
BalloonFlightScreens.drawBalloon(player, newPosition)
return true
}
/** Sets the starting amount of sandbags and logs, and deducts them when used. */
private fun updateFlightSupplies(player: Player, buttonId: Int? = null): Boolean {
if (buttonId == null) {
setVarbit(player, VARBIT_SANDBAGS_REMAINING_2880, STARTING_SANDBAGS, true)
setVarbit(player, VARBIT_LOGS_REMAINING_2881, STARTING_LOGS, true)
return true
}
val varbit = when (buttonId) {
BalloonFlightRoutes.BUTTON_SANDBAG -> VARBIT_SANDBAGS_REMAINING_2880
BalloonFlightRoutes.BUTTON_BURN_LOG -> VARBIT_LOGS_REMAINING_2881
else -> return true
}
val remaining = getVarbit(player, varbit)
if (remaining <= 0) {
return false
}
setVarbit(player, varbit, remaining - 1, true)
return true
}
/** Handles a control button press and either advances, lands or crashes the route. */
private fun handleRouteButton(player: Player, buttonId: Int) {
private fun handleControlButton(player: Player, buttonId: Int) {
val destination = getFlightDestination(player)
val screen = getAttribute(player, ATTRIBUTE_FLIGHT_SCREEN, 1)
if (buttonId == BalloonFlightRoutes.BUTTON_BAIL) {
handleBalloonCrash(player, getCrashLocation(destination, screen))
val position = getBalloonPosition(player, destination, screen)
handleBalloonCrash(player, getCrashLocation(destination, screen, position))
return
}
val route = BalloonFlightRoutes.getSequence(destination, screen)
val move = controlMoves[buttonId] ?: return
val routeLength = BalloonFlightRoutes.getSequence(destination, screen).size
val progress = getAttribute(player, ATTRIBUTE_FLIGHT_PROGRESS, 0)
val expectedButton = route.getOrNull(progress)
val move = controlMoves[buttonId]
if (buttonId != expectedButton || move == null) {
handleBalloonCrash(player, getCrashLocation(destination, screen))
if (!updateFlightSupplies(player, buttonId)) {
return
}
playControlSound(player, buttonId)
moveBalloon(player, move)
if (progress + 1 >= route.size) {
if (!moveBalloon(player, move)) {
return
}
if (progress + 1 >= routeLength) {
advanceFlightScreen(player, destination, screen)
} else {
setAttribute(player, ATTRIBUTE_FLIGHT_PROGRESS, progress + 1)
@ -255,14 +277,24 @@ class BalloonFlightInterface : InterfaceListener {
BalloonFlightScreens.draw(player, destination, screen)
}
private fun getCrashLocation(destination: BalloonTravelLocation, screen: Int): BalloonCrashLocation {
private fun getCrashLocation(destination: BalloonTravelLocation, screen: Int, position: BalloonGridPosition): BalloonCrashLocation {
if (BalloonFlightGrid.isOverOcean(destination, screen, position)) {
return BalloonCrashLocation.OCEAN
}
if (screen == BalloonFlightGrid.SCREEN_ONE) {
return BalloonCrashLocation.ENTRANA
}
return when (destination) {
BalloonTravelLocation.TAVERLEY -> getTaverleyCrashLocation(screen)
BalloonTravelLocation.CRAFTING_GUILD -> getCraftingGuildCrashLocation(screen)
BalloonTravelLocation.VARROCK -> getVarrockCrashLocation(screen)
BalloonTravelLocation.CASTLE_WARS -> getCastleWarsCrashLocation(screen)
BalloonTravelLocation.GRAND_TREE -> getGrandTreeCrashLocation(screen)
else -> getTaverleyCrashLocation(screen)
BalloonTravelLocation.TAVERLEY,
BalloonTravelLocation.CRAFTING_GUILD,
BalloonTravelLocation.VARROCK -> BalloonCrashLocation.WOODLAND_FALADOR
BalloonTravelLocation.CASTLE_WARS,
BalloonTravelLocation.GRAND_TREE -> BalloonCrashLocation.WOODLAND_ARDOUGNE
else -> BalloonCrashLocation.ENTRANA
}
}
@ -274,13 +306,13 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun startTaverleyScreen(player: Player, screen: Int, position: BalloonPosition) {
private fun startTaverleyScreen(player: Player, screen: Int, position: BalloonGridPosition) {
BalloonFlightScreens.clear(player)
BalloonFlightScreens.draw(player, BalloonTravelLocation.TAVERLEY, screen)
setAttribute(player, ATTRIBUTE_FLIGHT_SCREEN, screen)
setAttribute(player, ATTRIBUTE_FLIGHT_PROGRESS, 0)
setBalloonPosition(player, position.top, position.bottom)
BalloonFlightScreens.drawBalloon(player, position.top, position.bottom)
setBalloonPosition(player, position)
BalloonFlightScreens.drawBalloon(player, position)
}
private fun landInTaverley(player: Player) {
@ -298,14 +330,6 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun getTaverleyCrashLocation(screen: Int): BalloonCrashLocation {
return when (screen) {
1 -> BalloonCrashLocation.ENTRANA
2 -> BalloonCrashLocation.OCEAN
else -> BalloonCrashLocation.WOODLAND_FALADOR
}
}
private fun advanceCraftingGuildScreen(player: Player, screen: Int) {
when (screen) {
1 -> startCraftingGuildScreen(player, 2, BalloonFlightRoutes.CRAFTING_GUILD_SECOND)
@ -314,13 +338,13 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun startCraftingGuildScreen(player: Player, screen: Int, position: BalloonPosition) {
private fun startCraftingGuildScreen(player: Player, screen: Int, position: BalloonGridPosition) {
BalloonFlightScreens.clear(player)
BalloonFlightScreens.draw(player, BalloonTravelLocation.CRAFTING_GUILD, screen)
setAttribute(player, ATTRIBUTE_FLIGHT_SCREEN, screen)
setAttribute(player, ATTRIBUTE_FLIGHT_PROGRESS, 0)
setBalloonPosition(player, position.top, position.bottom)
BalloonFlightScreens.drawBalloon(player, position.top, position.bottom)
setBalloonPosition(player, position)
BalloonFlightScreens.drawBalloon(player, position)
}
private fun landAtCraftingGuild(player: Player) {
@ -335,14 +359,6 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun getCraftingGuildCrashLocation(screen: Int): BalloonCrashLocation {
return when (screen) {
1 -> BalloonCrashLocation.ENTRANA
2 -> BalloonCrashLocation.OCEAN
else -> BalloonCrashLocation.WOODLAND_FALADOR
}
}
private fun advanceVarrockScreen(player: Player, screen: Int) {
when (screen) {
1 -> startVarrockScreen(player, 2, BalloonFlightRoutes.VARROCK_SECOND)
@ -351,13 +367,13 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun startVarrockScreen(player: Player, screen: Int, position: BalloonPosition) {
private fun startVarrockScreen(player: Player, screen: Int, position: BalloonGridPosition) {
BalloonFlightScreens.clear(player)
BalloonFlightScreens.draw(player, BalloonTravelLocation.VARROCK, screen)
setAttribute(player, ATTRIBUTE_FLIGHT_SCREEN, screen)
setAttribute(player, ATTRIBUTE_FLIGHT_PROGRESS, 0)
setBalloonPosition(player, position.top, position.bottom)
BalloonFlightScreens.drawBalloon(player, position.top, position.bottom)
setBalloonPosition(player, position)
BalloonFlightScreens.drawBalloon(player, position)
}
private fun landAtVarrock(player: Player) {
@ -372,13 +388,6 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun getVarrockCrashLocation(screen: Int): BalloonCrashLocation {
return when (screen) {
1 -> BalloonCrashLocation.ENTRANA
else -> BalloonCrashLocation.WOODLAND_FALADOR
}
}
private fun advanceCastleWarsScreen(player: Player, screen: Int) {
when (screen) {
1 -> startCastleWarsScreen(player, 2, BalloonFlightRoutes.CASTLE_WARS_SECOND)
@ -387,13 +396,13 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun startCastleWarsScreen(player: Player, screen: Int, position: BalloonPosition) {
private fun startCastleWarsScreen(player: Player, screen: Int, position: BalloonGridPosition) {
BalloonFlightScreens.clear(player)
BalloonFlightScreens.draw(player, BalloonTravelLocation.CASTLE_WARS, screen)
setAttribute(player, ATTRIBUTE_FLIGHT_SCREEN, screen)
setAttribute(player, ATTRIBUTE_FLIGHT_PROGRESS, 0)
setBalloonPosition(player, position.top, position.bottom)
BalloonFlightScreens.drawBalloon(player, position.top, position.bottom)
setBalloonPosition(player, position)
BalloonFlightScreens.drawBalloon(player, position)
}
private fun landAtCastleWars(player: Player) {
@ -408,13 +417,6 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun getCastleWarsCrashLocation(screen: Int): BalloonCrashLocation {
return when (screen) {
1, 2 -> BalloonCrashLocation.OCEAN
else -> BalloonCrashLocation.WOODLAND_ARDOUGNE
}
}
private fun advanceGrandTreeScreen(player: Player, screen: Int) {
when (screen) {
1 -> startGrandTreeScreen(player, 2, BalloonFlightRoutes.GRAND_TREE_SECOND)
@ -423,13 +425,13 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun startGrandTreeScreen(player: Player, screen: Int, position: BalloonPosition) {
private fun startGrandTreeScreen(player: Player, screen: Int, position: BalloonGridPosition) {
BalloonFlightScreens.clear(player)
BalloonFlightScreens.draw(player, BalloonTravelLocation.GRAND_TREE, screen)
setAttribute(player, ATTRIBUTE_FLIGHT_SCREEN, screen)
setAttribute(player, ATTRIBUTE_FLIGHT_PROGRESS, 0)
setBalloonPosition(player, position.top, position.bottom)
BalloonFlightScreens.drawBalloon(player, position.top, position.bottom)
setBalloonPosition(player, position)
BalloonFlightScreens.drawBalloon(player, position)
}
private fun landAtGrandTree(player: Player) {
@ -444,17 +446,10 @@ class BalloonFlightInterface : InterfaceListener {
}
}
private fun getGrandTreeCrashLocation(screen: Int): BalloonCrashLocation {
return when (screen) {
1 -> BalloonCrashLocation.OCEAN
else -> BalloonCrashLocation.WOODLAND_ARDOUGNE
}
}
private fun clearFlightAttributes(player: Player) {
removeAttributes(
player, ATTRIBUTE_BALLOON_TOP,
ATTRIBUTE_BALLOON_BOTTOM,
player, ATTRIBUTE_BALLOON_ROW,
ATTRIBUTE_BALLOON_COLUMN,
ATTRIBUTE_FLIGHT_DESTINATION,
ATTRIBUTE_FLIGHT_SCREEN,
ATTRIBUTE_FLIGHT_PROGRESS
@ -511,7 +506,7 @@ class BalloonFlightInterface : InterfaceListener {
openSingleTab(player, BALLOON_CONTROL_INTERFACE_471)
BalloonFlightScreens.clear(player)
drawFlightScreen(player, destination, screen)
BalloonFlightScreens.drawBalloon(player, startPosition.top, startPosition.bottom)
BalloonFlightScreens.drawBalloon(player, startPosition)
openOverlay(player, 333)
true
}
@ -526,7 +521,7 @@ class BalloonFlightInterface : InterfaceListener {
}
on(BALLOON_CONTROL_INTERFACE_471) { player, _, _, buttonId, _, _ ->
handleRouteButton(player, buttonId)
handleControlButton(player, buttonId)
true
}
}

View file

@ -7,8 +7,6 @@ import core.game.world.map.Location
* @author Edith
*/
data class BalloonPosition(val top: Int, val bottom: Int)
object BalloonFlightRoutes {
const val BUTTON_SANDBAG = 4
const val BUTTON_RELAX = 5
@ -17,26 +15,26 @@ object BalloonFlightRoutes {
const val BUTTON_BURN_LOG = 9
const val BUTTON_RED_ROPE = 10
/** Child slots used by the top and bottom balloon models. */
val TAVERLEY_START = BalloonPosition(118, 99)
val TAVERLEY_SECOND = BalloonPosition(119, 98)
val TAVERLEY_THIRD = BalloonPosition(179, 159)
/** Balloon starting grid positions for each flight screen. */
val TAVERLEY_START = BalloonGridPosition(8, 0)
val TAVERLEY_SECOND = BalloonGridPosition(8, 1)
val TAVERLEY_THIRD = BalloonGridPosition(5, 1)
val CRAFTING_GUILD_START = BalloonPosition(178, 158)
val CRAFTING_GUILD_SECOND = BalloonPosition(159, 139)
val CRAFTING_GUILD_THIRD = BalloonPosition(159, 139)
val CRAFTING_GUILD_START = BalloonGridPosition(5, 0)
val CRAFTING_GUILD_SECOND = BalloonGridPosition(6, 1)
val CRAFTING_GUILD_THIRD = BalloonGridPosition(6, 1)
val VARROCK_START = BalloonPosition(138, 118)
val VARROCK_SECOND = BalloonPosition(179, 159)
val VARROCK_THIRD = BalloonPosition(179, 159)
val VARROCK_START = BalloonGridPosition(7, 0)
val VARROCK_SECOND = BalloonGridPosition(5, 1)
val VARROCK_THIRD = BalloonGridPosition(5, 1)
val CASTLE_WARS_START = BalloonPosition(118, 99)
val CASTLE_WARS_SECOND = BalloonPosition(199, 179)
val CASTLE_WARS_THIRD = BalloonPosition(159, 139)
val CASTLE_WARS_START = BalloonGridPosition(8, 0)
val CASTLE_WARS_SECOND = BalloonGridPosition(4, 1)
val CASTLE_WARS_THIRD = BalloonGridPosition(6, 1)
val GRAND_TREE_START = BalloonPosition(178, 158)
val GRAND_TREE_SECOND = BalloonPosition(159, 139)
val GRAND_TREE_THIRD = BalloonPosition(159, 139)
val GRAND_TREE_START = BalloonGridPosition(5, 0)
val GRAND_TREE_SECOND = BalloonGridPosition(6, 1)
val GRAND_TREE_THIRD = BalloonGridPosition(6, 1)
/** Player landing locations. */
val TAVERLEY_LANDING = Location.create(2941, 3420, 0)
@ -162,8 +160,8 @@ object BalloonFlightRoutes {
}
}
/** Gets the starting child slots for the balloon models on the first screen of a route. */
fun getStartPosition(destination: BalloonTravelLocation): BalloonPosition {
/** Gets the starting grid position for the balloon models on the first screen of a route. */
fun getStartPosition(destination: BalloonTravelLocation): BalloonGridPosition {
return when (destination) {
BalloonTravelLocation.TAVERLEY -> TAVERLEY_START
BalloonTravelLocation.CRAFTING_GUILD -> CRAFTING_GUILD_START

View file

@ -38,20 +38,30 @@ object BalloonFlightScreens {
}
}
/** Draws the top and bottom balloon models at the given child slots. */
fun drawBalloon(player: Player, topChild: Int, bottomChild: Int) {
/** Draws the top and bottom balloon models at the given grid position. */
fun drawBalloon(player: Player, position: BalloonGridPosition) {
val (topChild, bottomChild) = getBalloonChildren(position) ?: return
player.packetDispatch.sendModelOnInterface(MODEL_BALLOON_TOP_19517, BALLOON_FLIGHT_INTERFACE_470, topChild, 2576)
resetModelAngle(player, topChild)
player.packetDispatch.sendModelOnInterface(MODEL_BALLOON_BOTTOM_19518, BALLOON_FLIGHT_INTERFACE_470, bottomChild, 2576)
resetModelAngle(player, bottomChild)
}
/** Clears the top and bottom balloon models at the given child slots. */
fun clearBalloon(player: Player, topChild: Int, bottomChild: Int) {
/** Clears the top and bottom balloon models at the given grid position. */
fun clearBalloon(player: Player, position: BalloonGridPosition) {
val (topChild, bottomChild) = getBalloonChildren(position) ?: return
player.packetDispatch.sendModelOnInterface(-1, BALLOON_FLIGHT_INTERFACE_470, topChild, 0)
player.packetDispatch.sendModelOnInterface(-1, BALLOON_FLIGHT_INTERFACE_470, bottomChild, 0)
}
/** Gets the top and bottom interface children for a balloon grid position. */
private fun getBalloonChildren(position: BalloonGridPosition): Pair<Int, Int>? {
val topPosition = position.copy(row = position.row - 1)
val topChild = BalloonFlightGrid.getGridChild(topPosition) ?: return null
val bottomChild = BalloonFlightGrid.getGridChild(position) ?: return null
return topChild to bottomChild
}
private fun resetModelAngle(player: Player, child: Int) {
player.packetDispatch.sendAngleOnInterface(BALLOON_FLIGHT_INTERFACE_470, child, 2576, 0, 0)
}

View file

@ -0,0 +1,62 @@
package content.global.travel.balloon
import core.api.getAttribute
import core.api.setAttribute
import core.game.node.entity.npc.NPC
import core.game.node.entity.npc.NPCBehavior
import core.game.world.map.Location
import org.rs09.consts.NPCs
/**
* Makes the sharks in the ocean crash site swim around in a clockwise patrol.
* All three should have their own slightly different route instead of a shared one but honestly I cba.
* @author Edith
*/
class OceanCrashSharkPatrol : NPCBehavior(NPCs.SHARK_5067, NPCs.SHARK_5068, NPCs.SHARK_5069) {
companion object {
private const val ATTRIBUTE_NEXT_WAYPOINT = "balloon_shark_next_waypoint"
private fun route(vararg coordinates: Pair<Int, Int>) =
Array(coordinates.size) { Location.create(coordinates[it].first, coordinates[it].second) }
private val PATROL_ROUTE = route(
1799 to 4885,
1798 to 4886,
1797 to 4887,
1797 to 4892,
1798 to 4893,
1798 to 4894,
1799 to 4894,
1803 to 4894,
1804 to 4893,
1805 to 4893,
1807 to 4893,
1807 to 4887,
1806 to 4886,
1805 to 4885
)
}
override fun onCreation(self: NPC) {
val startingWaypoint = PATROL_ROUTE.indexOf(self.location)
val nextWaypoint = if (startingWaypoint == -1) 0 else (startingWaypoint + 1) % PATROL_ROUTE.size
setAttribute(self, ATTRIBUTE_NEXT_WAYPOINT, nextWaypoint)
}
override fun tick(self: NPC): Boolean {
if (!self.walkingQueue.hasPath()) {
var nextWaypoint = getAttribute(self, ATTRIBUTE_NEXT_WAYPOINT, 0)
if (self.location == PATROL_ROUTE[nextWaypoint]) {
nextWaypoint = (nextWaypoint + 1) % PATROL_ROUTE.size
setAttribute(self, ATTRIBUTE_NEXT_WAYPOINT, nextWaypoint)
}
val destination = PATROL_ROUTE[nextWaypoint]
self.walkingQueue.reset()
self.walkingQueue.addPath(destination.x, destination.y)
}
return false
}
}