From e9fd74b3794966360d643a42b573345b1d64cdc6 Mon Sep 17 00:00:00 2001 From: Oven Bread Date: Wed, 4 Oct 2023 23:30:51 +0000 Subject: [PATCH] Refactored canoe handling Canoe station should now have the full animation suite Canoe making now scales with axe and level of woodcutting Interfaces are now fully updated with proper hide/show/animation Canoes now sink at the end of the journey Added a black screen backdrop overlay seen only in HD --- .../content/global/travel/canoe/Canoe.java | 86 ---- .../travel/canoe/CanoeInterfaceListeners.kt | 141 ------- .../global/travel/canoe/CanoeListener.kt | 372 ++++++++++++++++++ .../travel/canoe/CanoeStationListener.kt | 101 ----- .../content/global/travel/canoe/CanoeUtils.kt | 122 ------ .../varrock/diary/VarrockAchivementDiary.kt | 6 +- .../entity/player/link/InterfaceManager.java | 4 + 7 files changed, 379 insertions(+), 453 deletions(-) delete mode 100644 Server/src/main/content/global/travel/canoe/Canoe.java delete mode 100644 Server/src/main/content/global/travel/canoe/CanoeInterfaceListeners.kt create mode 100644 Server/src/main/content/global/travel/canoe/CanoeListener.kt delete mode 100644 Server/src/main/content/global/travel/canoe/CanoeStationListener.kt delete mode 100644 Server/src/main/content/global/travel/canoe/CanoeUtils.kt diff --git a/Server/src/main/content/global/travel/canoe/Canoe.java b/Server/src/main/content/global/travel/canoe/Canoe.java deleted file mode 100644 index d78377227..000000000 --- a/Server/src/main/content/global/travel/canoe/Canoe.java +++ /dev/null @@ -1,86 +0,0 @@ -package content.global.travel.canoe; - -/** - * Represents a canoe to craft. - * @author 'Vexia - * @date 09/11/2013 - */ -public enum Canoe { - LOG(12, 30, 30,0,0), - DUGOUT(27, 60, 31,9,3), - STABLE_DUGOUT(42, 90, 32,10,2), - WAKA(57, 150, 33,8,5); - - /** - * Constructs a new {@code Canoe.java} {@code Object}. - * @param level the woodcutting level requirement. - * @param experience the experience. - * @param child the child. - */ - Canoe(final int level, final double experience, final int child, final int silhouetteChild, final int textChild) { - this.level = level; - this.experience = experience; - this.child = child; - this.silhouetteChild = silhouetteChild; - this.textChild = textChild; - this.maxDist = ordinal() + 1; - } - - public final int silhouetteChild; - public final int textChild; - public final int maxDist; - - /** - * Represents the woodcutting level requirement to craft the canoe. - */ - private final int level; - - /** - * Represents the experience received for crafting the canoe. - */ - private final double experience; - - /** - * Represents the child's id on the interface. - */ - private final int child; - - /** - * Gets the woodcutting level requirement. - * @return The required level. - */ - public int getRequiredLevel() { - return level; - } - - /** - * Gets the experience received. - * @return The experience amount. - */ - public double getExperience() { - return experience; - } - - /** - * Gets the child. - * @return The child. - */ - public int getChild() { - return child; - } - - /** - * Method used to get the canoe from the child. - * @param child the child. - * @return True if so - */ - public static Canoe getCanoeFromChild(final int child) { - for (Canoe canoe : values()) { - if(canoe.getChild() == child) - { - return canoe; - } - } - return null; - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/travel/canoe/CanoeInterfaceListeners.kt b/Server/src/main/content/global/travel/canoe/CanoeInterfaceListeners.kt deleted file mode 100644 index dbc997b95..000000000 --- a/Server/src/main/content/global/travel/canoe/CanoeInterfaceListeners.kt +++ /dev/null @@ -1,141 +0,0 @@ -package content.global.travel.canoe - -import core.api.* -import core.cache.def.impl.VarbitDefinition -import core.game.component.Component -import core.game.node.entity.skill.Skills -import content.data.skill.SkillingTool -import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation -import core.net.packet.PacketRepository -import core.net.packet.context.MinimapStateContext -import core.net.packet.out.MinimapState -import core.tools.RandomFunction -import org.rs09.consts.Components -import core.game.interaction.InterfaceListener -import kotlin.math.abs - -class CanoeInterfaceListeners : InterfaceListener { - - val SHAPE_INTERFACE = Components.CANOE_52 - val DESTINATION_INTERFACE = Components.CANOE_STATIONS_MAP_53 - - private val boatChilds = intArrayOf(47, 48, 3, 6, 49) - private val locationChilds = intArrayOf(50, 47, 44, 36) - - - override fun defineInterfaceListeners() { - - onOpen(SHAPE_INTERFACE){player, _ -> - CanoeUtils.checkCanoe(player, Canoe.DUGOUT) - CanoeUtils.checkCanoe(player, Canoe.STABLE_DUGOUT) - CanoeUtils.checkCanoe(player, Canoe.WAKA) - return@onOpen true - } - - on(SHAPE_INTERFACE) { player, _, _, buttonID, _, _ -> - player.interfaceManager.close() - val canoe = Canoe.getCanoeFromChild(buttonID) - val varbit = player.getAttribute("canoe-varbit", VarbitDefinition.forObjectID(0)) - - val axe: SkillingTool? = SkillingTool.getHatchet(player) - if (axe == null) { - player.packetDispatch.sendMessage("You do not have an axe which you have the woodcutting level to use.") - return@on true - } - - lock(player, 4) - animate(player, CanoeUtils.getShapeAnimation(axe)) - player.pulseManager.run(object : Pulse(3) { - override fun pulse(): Boolean { - if (RandomFunction.random(if (canoe == Canoe.WAKA) 8 else 6) == 1) { - setVarbit(player,varbit, CanoeUtils.getCraftValue(canoe, false)) - player.skills.addExperience(Skills.WOODCUTTING, canoe.experience) - player.unlock() - return true - } - animate(player, CanoeUtils.getShapeAnimation(axe)) - return false - } - }) - return@on true - } - - onOpen(DESTINATION_INTERFACE){player, component -> - val varbit = player.getAttribute("canoe-varbit",VarbitDefinition.forObjectID(0)) - val canoe = CanoeUtils.getCanoeFromVarbit(player, varbit) - val stationIndex = CanoeUtils.getStationIndex(player.location) - val maxDistance = canoe.maxDist - player.packetDispatch.sendInterfaceConfig(DESTINATION_INTERFACE,boatChilds[stationIndex],true) - player.packetDispatch.sendInterfaceConfig(DESTINATION_INTERFACE,locationChilds[stationIndex],false) - if(canoe != Canoe.WAKA){ - player.packetDispatch.sendInterfaceConfig(DESTINATION_INTERFACE,49,true) - for(i in 0..3){ - if(i == stationIndex) continue - if(abs(i - stationIndex) > maxDistance){ - player.packetDispatch.sendInterfaceConfig(DESTINATION_INTERFACE,boatChilds[i],true) - player.packetDispatch.sendInterfaceConfig(DESTINATION_INTERFACE,locationChilds[i],true) - } - } - } - return@onOpen true - } - - on(DESTINATION_INTERFACE){player, _, _, buttonID, _, _ -> - val dest = CanoeUtils.getDestinationFromButtonID(buttonID) - val destIndex = CanoeUtils.getStationIndex(dest) - val arrivalMessage = CanoeUtils.getNameByIndex(destIndex) - val stationIndex = CanoeUtils.getStationIndex(player.location) - val interfaceAnimationId = CanoeUtils.getTravelAnimation(stationIndex, destIndex) - var travelAnimDur = 15 - val varbit = player.getAttribute("canoe-varbit",VarbitDefinition.forObjectID(0)) - - if (player.familiarManager.hasFamiliar()) { - player.sendMessage("You can't take a follower on a canoe.") - return@on true - } - if (interfaceAnimationId != 0) { - travelAnimDur = Animation(interfaceAnimationId).duration - } - - lock(player, travelAnimDur + 4) - player.interfaceManager.close() - player.pulseManager.run(object : Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 0 -> { - player.interfaceManager.openOverlay(Component(Components.FADE_TO_BLACK_120)) - player.interfaceManager.open(Component(Components.CANOE_TRAVEL_758)) - animateInterface(player, Components.CANOE_TRAVEL_758, 3, interfaceAnimationId) - } - 2 -> { - PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) - player.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12) - } - travelAnimDur+1 -> { - player.properties.teleportLocation = dest - player.interfaceManager.close(Component(Components.CANOE_TRAVEL_758)) - player.interfaceManager.closeOverlay() - player.interfaceManager.openOverlay(Component(Components.FADE_FROM_BLACK_170)) - } - travelAnimDur+3 -> { - player.unlock() - player.interfaceManager.restoreTabs() - PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) - player.sendMessage("You arrive at $arrivalMessage.") - player.sendMessage("Your canoe sinks from the long journey.") - if(destIndex == 4){ - player.sendMessage("There are no trees nearby to make a new canoe. Guess you're walking.") - } - setVarbit(player,varbit,0) - return true - } - } - return false - } - }) - return@on true - } - } -} diff --git a/Server/src/main/content/global/travel/canoe/CanoeListener.kt b/Server/src/main/content/global/travel/canoe/CanoeListener.kt new file mode 100644 index 000000000..57553ba37 --- /dev/null +++ b/Server/src/main/content/global/travel/canoe/CanoeListener.kt @@ -0,0 +1,372 @@ +package content.global.travel.canoe + +import content.data.skill.SkillingTool +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.scenery.SceneryBuilder +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.net.packet.PacketRepository +import core.net.packet.context.MinimapStateContext +import core.net.packet.out.MinimapState +import core.tools.RandomFunction +import org.rs09.consts.Components +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds +import kotlin.math.abs + +/** + * Interaction and interface listener for canoe along river Lum. + * This handles Components.CANOE_52, Components.CANOE_STATIONS_MAP_53 and Components.CANOE_TRAVEL_758 globally. + */ +class CanoeListener : InteractionListener, InterfaceListener { + companion object { + const val CANOE_STATION_VARBIT_ATTRIBUTE = "canoeStationVarbit" // Index of Canoe Stations (0-4) + const val CANOE_SELECTED_ATTRIBUTE = "canoeSelected" // Index of Canoes (0-3) + + const val CANOE_SHAPING_INTERFACE = Components.CANOE_52 + const val CANOE_DESTINATION_INTERFACE = Components.CANOE_STATIONS_MAP_53 + const val CANOE_TRAVEL_INTERFACE = Components.CANOE_TRAVEL_758 + + val CANOE_SHAPING_SILHOUETTE = arrayOf(0, 9, 10, 8) + val CANOE_SHAPING_TEXT = arrayOf(0, 3, 2, 5) + val CANOE_SHAPING_BUTTONS = arrayOf(30, 31, 32, 33) + + val CANOE_DESTINATION_BUTTONS = arrayOf(47, 48, 3, 6, 49) + val CANOE_DESTINATION_HIDE_ROW = arrayOf(10, 11, 12, 20, 18) + val CANOE_DESTINATION_YOU_ARE_HERE = arrayOf(25, 24, 23, 19, 0) + + val CANOE_TREE_FALLING_ANIMATION = Animation(3304) + val CANOE_PLAYER_PUSHING_ANIMATION = Animation(3301) + val CANOE_PUSHING_ANIMATION = Animation(3304) + val CANOE_SINKING_ANIMATION = Animation(3305) + /** Canoe travel interface animations. A 2D array mapping [ origin ][ destination ]. Index 0-4 for Lumbridge to Wilderness. */ + val CANOE_TRAVEL_ANIMATIONS = arrayOf( + arrayOf(0, 9890, 9889, 9888, 9887), + arrayOf(9906, 0, 9893, 9892, 9891), + arrayOf(9904, 9905, 0, 9895, 9894), + arrayOf(9901, 9902, 9903, 0, 9896), + arrayOf(9897, 9898, 9899, 9900, 0), + ) + + @JvmStatic + fun getAxeAnimation(axe: SkillingTool): Animation { + return when (axe) { + SkillingTool.BRONZE_AXE -> Animation(6744) + SkillingTool.IRON_AXE -> Animation(6743) + SkillingTool.STEEL_AXE -> Animation(6742) + SkillingTool.BLACK_AXE -> Animation(6741) + SkillingTool.MITHRIL_AXE -> Animation(6740) + SkillingTool.ADAMANT_AXE -> Animation(6739) + SkillingTool.RUNE_AXE -> Animation(6738) + SkillingTool.DRAGON_AXE -> Animation(6745) + else -> axe.animation + } + } + + /** To scale woodcutting success. Follows WoodcuttingNode values at 15,30,45,60 (3 levels 'harder'). */ + private fun checkSuccess(player: Player, resource: Canoes, tool: SkillingTool): Boolean { + val skill = Skills.WOODCUTTING + val level: Int = getDynLevel(player, skill) + getFamiliarBoost(player, skill) + val hostRatio = RandomFunction.randomDouble(100.0) + val lowMod: Double = if (tool == SkillingTool.BLACK_AXE) resource.tierModLow / 2 else resource.tierModLow + val low: Double = resource.baseLow + tool.ordinal * lowMod + val highMod: Double = if (tool == SkillingTool.BLACK_AXE) resource.tierModHigh / 2 else resource.tierModHigh + val high: Double = resource.baseHigh + tool.ordinal * highMod + val clientRatio = RandomFunction.getSkillSuccessChance(low, high, level) + return hostRatio < clientRatio + } + + /** Maps canoe stations stages to related properties. */ + enum class CanoeStationSceneries(val sceneryId: Int, val varbitValue: Int) { + // Stage 1 - Tree is standing, option to chop-down. + TREE_STANDING(Scenery.CANOE_STATION_12144, 0), + + // Stage 2 - The tree is falling via an animation, denies player of options. + TREE_FALLING(Scenery.CANOE_STATION_12145, 9), + + // Stage 3 - Tree is on the cradle after the chop, option to shape the boat via an interface. + TREE_FALLEN(Scenery.CANOE_STATION_12146, 10), + + // Stage 4 - Tree gets shaped into 1 of 4 canoes, option to push it into the water. + TREE_SHAPED_LOG(Scenery.CANOE_STATION_12147, 1), + TREE_SHAPED_DUGOUT(Scenery.CANOE_STATION_12148, 2), + TREE_SHAPED_STABLE_DUGOUT(Scenery.CANOE_STATION_12149, 3), + TREE_SHAPED_WAKA(Scenery.CANOE_STATION_12150, 4), + + // Stage 5 - Canoe is being pushed into the water via an animation, option to travel is available. + CANOE_PUSHING_LOG(Scenery.CANOE_STATION_12151, 5), + CANOE_PUSHING_DUGOUT(Scenery.CANOE_STATION_12152, 6), + CANOE_PUSHING_STABLE_DUGOUT(Scenery.CANOE_STATION_12153, 7), + CANOE_PUSHING_WAKA(Scenery.CANOE_STATION_12154, 8), + + // Stage 6 - Canoe is in the water, option to travel via an interface. + CANOE_FLOATING_LOG(Scenery.CANOE_STATION_12155, 11), + CANOE_FLOATING_DUGOUT(Scenery.CANOE_STATION_12156, 12), + CANOE_FLOATING_STABLE_DUGOUT(Scenery.CANOE_STATION_12157, 13), + CANOE_FLOATING_WAKA(Scenery.CANOE_STATION_12158, 14), + + // Stage 7 - Canoe is animated to be sunk at the end of the ride. NO VARBITS, NOT PART OF THE CANOE_STATION SCENERY. + CANOE_SINKING_LOG(Scenery.A_SINKING_CANOE_12159, 0), + CANOE_SINKING_DUGOUT(Scenery.A_SINKING_CANOE_12160, 0), + CANOE_SINKING_STABLE_DUGOUT(Scenery.A_SINKING_CANOE_12161, 0), + CANOE_SINKING_WAKA(Scenery.A_SINKING_CANOE_12162, 0); + + companion object { + @JvmField + val stationIdMap = values().associateBy { it.sceneryId } + val stationIdArray = stationIdMap.values.map { it.sceneryId }.toIntArray() + } + } + + /** Enums to map canoes to related properties. */ + enum class Canoes(val level: Int, val experience: Double, val maxDistance: Int, val baseLow: Double, val baseHigh: Double, val tierModLow: Double, val tierModHigh: Double, val treeShaped: CanoeStationSceneries, val canoePushing: CanoeStationSceneries, val canoeFloating: CanoeStationSceneries, val canoeSinking: CanoeStationSceneries) { + LOG (12, 30.0, 1, 32.0, 100.0, 16.0, 50.0, CanoeStationSceneries.TREE_SHAPED_LOG, CanoeStationSceneries.CANOE_PUSHING_LOG, CanoeStationSceneries.CANOE_FLOATING_LOG, CanoeStationSceneries.CANOE_SINKING_LOG), + DUGOUT (27, 60.0, 2, 16.0, 50.0, 8.0, 25.0, CanoeStationSceneries.TREE_SHAPED_DUGOUT, CanoeStationSceneries.CANOE_PUSHING_DUGOUT, CanoeStationSceneries.CANOE_FLOATING_DUGOUT, CanoeStationSceneries.CANOE_SINKING_DUGOUT), + STABLE_DUGOUT (42, 90.0, 3, 8.0, 25.0, 4.0, 12.5, CanoeStationSceneries.TREE_SHAPED_STABLE_DUGOUT, CanoeStationSceneries.CANOE_PUSHING_STABLE_DUGOUT, CanoeStationSceneries.CANOE_FLOATING_STABLE_DUGOUT, CanoeStationSceneries.CANOE_SINKING_STABLE_DUGOUT), + WAKA (57, 150.0, 4, 4.0, 12.5, 2.0, 6.25, CanoeStationSceneries.TREE_SHAPED_WAKA, CanoeStationSceneries.CANOE_PUSHING_WAKA, CanoeStationSceneries.CANOE_FLOATING_WAKA, CanoeStationSceneries.CANOE_SINKING_WAKA); + + companion object { + @JvmField + val indexMap = Canoes.values().associateBy { it.ordinal } + } + } + + /** Enums to map canoe stations locations to related properties. */ + enum class CanoeStationLocations(val stationRegion: Int, val stationVarbit: Int, val playerChopLocation: Location, val playerFloatLocation: Location, val playerFacingLocation: Location, val canoeSinkLocation: Location, val playerDestination: Location, val locationName: String) { + LUMBRIDGE(12850, 1839, Location(3243, 3235), Location(3243, 3237), Location(-1, 0), Location(3239, 3242), Location(3240, 3242), "Lumbridge"), + CHAMPIONS(12852, 1840, Location(3204, 3343), Location(3202, 3343), Location(0, -1), Location(3199, 3344), Location(3199, 3344), "the Champion's Guild"), + BARBARIAN(12341, 1841, Location(3112, 3409), Location(3112, 3411), Location(-1, 0), Location(3109, 3411), Location(3109, 3415), "Barbarian Village"), + EDGEVILLE(12342, 1842, Location(3132, 3508), Location(3132, 3510), Location(-1, 0), Location(3132, 3510), Location(3132, 3510), "Edgeville"), + WILDERNESS(12603, 0, Location(0, 0), Location(0, 0), Location(0, 0), Location(3142, 3795), Location(3139, 3796), "the Wilderness Pond"); + + companion object { + private val stationRegionMap = CanoeStationLocations.values().associateBy { it.stationRegion } + + @JvmStatic + fun getCanoeStationbyLocation(location: Location): CanoeStationLocations { + return stationRegionMap[location.regionId]!! + } + } + } + } + + override fun defineDestinationOverrides() { + // Set player's standing location when chopping down the tree. + setDest(IntType.SCENERY, CanoeStationSceneries.stationIdArray, "chop-down") { _, node -> + return@setDest CanoeStationLocations.getCanoeStationbyLocation(node.location).playerChopLocation + } + // Set player's standing location when shaping and floating the canoe. + setDest(IntType.SCENERY, CanoeStationSceneries.stationIdArray, "shape-canoe", "float canoe", "float log", "float waka") { _, node -> + return@setDest CanoeStationLocations.getCanoeStationbyLocation(node.location).playerFloatLocation + } + } + + override fun defineListeners() { + // Stage 1 - 2: Tree is standing, option to chop-down. + on(CanoeStationSceneries.stationIdArray, IntType.SCENERY, "chop-down") { player, node -> + val canoeStation = CanoeStationLocations.getCanoeStationbyLocation(node.location) + val axe: SkillingTool? = SkillingTool.getHatchet(player) + val stationVarbit = node.asScenery().definition.configFile + if (axe == null) { + sendMessage(player, "You do not have an axe which you have the woodcutting level to use.") + return@on true + } + if (getStatLevel(player, Skills.WOODCUTTING) < 12) { + sendMessage(player, "You need a woodcutting level of at least 12 to chop down this tree.") + return@on true + } + lock(player, axe.animation.duration + CANOE_TREE_FALLING_ANIMATION.duration) + face(player, canoeStation.playerChopLocation.transform(canoeStation.playerFacingLocation)) + animate(player, axe.animation) + queueScript(player, axe.animation.duration, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + player.animator.stop() + setVarbit(player, stationVarbit, CanoeStationSceneries.TREE_FALLING.varbitValue) + animateScenery(player, node.asScenery(), CANOE_TREE_FALLING_ANIMATION.id) + return@queueScript delayScript(player, CANOE_TREE_FALLING_ANIMATION.duration) + } + 1 -> { + setVarbit(player, stationVarbit, CanoeStationSceneries.TREE_FALLEN.varbitValue) + unlock(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + return@on true + } + + // Stage 3: Tree is on the cradle after the chop, option to shape-canoe via interface CANOE_52. + on(CanoeStationSceneries.stationIdArray, IntType.SCENERY, "shape-canoe") { player, node -> + val canoeStation = CanoeStationLocations.getCanoeStationbyLocation(node.location) + setAttribute(player, CANOE_STATION_VARBIT_ATTRIBUTE, canoeStation.stationVarbit) + face(player, canoeStation.playerFloatLocation.transform(canoeStation.playerFacingLocation)) + openInterface(player, CANOE_SHAPING_INTERFACE) + openOverlay(player, 333) // Black overlay + return@on true + } + + // Stage 4 - 5: Tree gets shaped into 1 of 4 canoes, option to float-canoe. + on(CanoeStationSceneries.stationIdArray, IntType.SCENERY, "float canoe", "float log", "float waka") { player, node -> + val canoeStation = CanoeStationLocations.getCanoeStationbyLocation(node.location) + val canoe = Canoes.indexMap[getAttribute(player, CANOE_SELECTED_ATTRIBUTE, 0)]!! + setVarbit(player, canoeStation.stationVarbit, canoe.canoePushing.varbitValue) + lock(player, CANOE_PLAYER_PUSHING_ANIMATION.duration) + playAudio(player, Sounds.CANOE_ROLL_2731) + face(player, canoeStation.playerFloatLocation.transform(canoeStation.playerFacingLocation)) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + animate(player, CANOE_PLAYER_PUSHING_ANIMATION) + animateScenery(player, node.asScenery(), CANOE_PUSHING_ANIMATION.id) + return@queueScript delayScript(player, CANOE_PLAYER_PUSHING_ANIMATION.duration) + } + 1 -> { + setVarbit(player, canoeStation.stationVarbit, canoe.canoeFloating.varbitValue) + unlock(player) + player.animator.stop() + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + return@on true + } + + // Stage 6: Canoe is in the water, option to paddle via interface CANOE_STATIONS_MAP_53. + on(CanoeStationSceneries.stationIdArray, IntType.SCENERY, "paddle log", "paddle canoe") { player, _ -> + closeInterface(player) + openInterface(player, Components.CANOE_STATIONS_MAP_53) + openOverlay(player, 333) // Black overlay + return@on true + } + } + + var temp = 14 + override fun defineInterfaceListeners() { + + // Stage 3: Opening interface 52 + onOpen(CANOE_SHAPING_INTERFACE) { player, _ -> + Canoes.values().forEach { + if (getStatLevel(player, Skills.WOODCUTTING) >= it.level && it != Canoes.LOG) { + setComponentVisibility(player, CANOE_SHAPING_INTERFACE, CANOE_SHAPING_SILHOUETTE[it.ordinal], true) + setComponentVisibility(player, CANOE_SHAPING_INTERFACE, CANOE_SHAPING_TEXT[it.ordinal], false) + } + } + return@onOpen true + } + + // Stage 3: On button click interface 52 + on(CANOE_SHAPING_INTERFACE) { player, _, _, buttonID, _, _ -> + closeInterface(player) + val canoe = Canoes.indexMap[CANOE_SHAPING_BUTTONS.indexOf(buttonID)]!! + val stationVarbit = getAttribute(player, CANOE_STATION_VARBIT_ATTRIBUTE, 1839) + val axe: SkillingTool? = SkillingTool.getHatchet(player) + if (axe == null) { + sendMessage(player, "You do not have an axe which you have the woodcutting level to use.") + return@on true + } + + lock(player, 4) + animate(player, getAxeAnimation(axe)) + submitIndividualPulse(player, object : Pulse(3) { + override fun pulse(): Boolean { + if (checkSuccess(player, canoe, axe)) { + setAttribute(player, CANOE_SELECTED_ATTRIBUTE, CANOE_SHAPING_BUTTONS.indexOf(buttonID)) + setVarbit(player, stationVarbit, canoe.treeShaped.varbitValue) + rewardXP(player, Skills.WOODCUTTING, canoe.experience) + unlock(player) + return true + } + animate(player, getAxeAnimation(axe)) + return false + } + }) + return@on true + } + + onOpen(CANOE_DESTINATION_INTERFACE) { player, component -> + val canoe = Canoes.indexMap[getAttribute(player, CANOE_SELECTED_ATTRIBUTE, 0)]!! + val origin = CanoeStationLocations.getCanoeStationbyLocation(player.location) + for (i in CanoeStationLocations.values()) { + setComponentVisibility(player, component.id, CANOE_DESTINATION_HIDE_ROW[i.ordinal], true) + if (i == CanoeStationLocations.WILDERNESS) { + if (canoe == Canoes.WAKA) { + // setComponentVisibility(player, component.id, 22, false) // Some red text on Wildy text. + setComponentVisibility(player, component.id, CANOE_DESTINATION_HIDE_ROW[i.ordinal], false) + } + } else if (i.ordinal == origin.ordinal) { + setComponentVisibility(player, component.id, CANOE_DESTINATION_YOU_ARE_HERE[i.ordinal], false) + }else if (abs(i.ordinal - origin.ordinal) <= canoe.maxDistance) { + setComponentVisibility(player, component.id, CANOE_DESTINATION_HIDE_ROW[i.ordinal], false) + } + } + return@onOpen true + } + + on(CANOE_DESTINATION_INTERFACE) { player, _, _, buttonID, _, _ -> + val origin = CanoeStationLocations.getCanoeStationbyLocation(player.location) + var destination: CanoeStationLocations = CanoeStationLocations.LUMBRIDGE + when (buttonID) { + CANOE_DESTINATION_BUTTONS[0] -> destination = CanoeStationLocations.LUMBRIDGE + CANOE_DESTINATION_BUTTONS[1] -> destination = CanoeStationLocations.CHAMPIONS + CANOE_DESTINATION_BUTTONS[2] -> destination = CanoeStationLocations.BARBARIAN + CANOE_DESTINATION_BUTTONS[3] -> destination = CanoeStationLocations.EDGEVILLE + CANOE_DESTINATION_BUTTONS[4] -> destination = CanoeStationLocations.WILDERNESS + } + val arrivalMessage = destination.locationName + val interfaceAnimationId = CANOE_TRAVEL_ANIMATIONS[origin.ordinal][destination.ordinal] + + if (player.familiarManager.hasFamiliar()) { + sendMessage(player, "You can't take a follower on a canoe.") + return@on true + } + lock(player, Animation(interfaceAnimationId).duration + 1 + Animation(Components.FADE_FROM_BLACK_170).duration) + // Tried using queueScript here, but it doesn't work as interfaces and overlays can't open. Maybe its because this is within an interface listener. + submitIndividualPulse(player, object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 0 -> { + openInterface(player, CANOE_TRAVEL_INTERFACE) + openOverlay(player, 333) // Only call openOverlay(player, 333) after openInterface + animateInterface(player, CANOE_TRAVEL_INTERFACE, 3, interfaceAnimationId) + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + player.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12) + } + Animation(interfaceAnimationId).duration + 1 -> { + teleport(player, destination.playerDestination) + closeInterface(player) + closeOverlay(player) + openOverlay(player, Components.FADE_FROM_BLACK_170) + } + Animation(interfaceAnimationId).duration + 1 + Animation(Components.FADE_FROM_BLACK_170).duration -> { + unlock(player) + player.interfaceManager.restoreTabs() + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) + val sinkingScenery = SceneryBuilder.add(core.game.node.scenery.Scenery(Scenery.A_SINKING_CANOE_12159, destination.canoeSinkLocation, 1), 3) as core.game.node.scenery.Scenery + animateScenery(sinkingScenery, CANOE_SINKING_ANIMATION.id) + sendMessage(player, "You arrive at $arrivalMessage.") + sendMessage(player, "Your canoe sinks from the long journey.") + if (destination == CanoeStationLocations.WILDERNESS) { + sendMessage(player, "There are no trees nearby to make a new canoe. Guess you're walking.") + } + setVarbit(player, origin.stationVarbit, 0) + return true + } + } + return false + } + }) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/travel/canoe/CanoeStationListener.kt b/Server/src/main/content/global/travel/canoe/CanoeStationListener.kt deleted file mode 100644 index cdb2f03d6..000000000 --- a/Server/src/main/content/global/travel/canoe/CanoeStationListener.kt +++ /dev/null @@ -1,101 +0,0 @@ -package content.global.travel.canoe - -import core.game.component.Component -import core.game.node.entity.skill.Skills -import content.data.skill.SkillingTool -import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Components -import core.game.interaction.InteractionListener -import core.game.interaction.IntType - -import core.api.* -import core.game.interaction.QueueStrength -import org.rs09.consts.Sounds - -class CanoeStationListener : InteractionListener { - - private val STATION_IDs = intArrayOf(12140, 12141, 12142, 12143, 12144, 12145, 12146, 12147, 12148, 12151, 12152, 12153, 12154, 12155, 12156, 12157, 12158, 12149, 12150) - private val STAGE_TREE_NONINTERACTABLE = 9 - private val STAGE_LOG_CHOPPED = 10 - private val SHAPING_INTERFACE = Components.CANOE_52 - private val PUSH = Animation(3301) - private val FALL = Animation(3303) - private val FLOAT = Animation(3304) - - override fun defineDestinationOverrides() { - setDest(IntType.SCENERY,STATION_IDs,"chop-down"){ _, node -> - return@setDest CanoeUtils.getChopLocation(node.location) - } - setDest(IntType.SCENERY,STATION_IDs,"shape-canoe","float canoe","float log","float waka"){ _, node -> - return@setDest CanoeUtils.getCraftFloatLocation(node.location) - } - } - - override fun defineListeners() { - on(STATION_IDs, IntType.SCENERY, "chop-down"){ player, node -> - val axe: SkillingTool? = SkillingTool.getHatchet(player) - val varbit = node.asScenery().definition.configFile - if(varbit.getValue(player) != 0){ - setVarbit(player,varbit,0) - } - - if (axe == null) { - player.packetDispatch.sendMessage("You do not have an axe which you have the woodcutting level to use.") - return@on true - } - if (player.skills.getLevel(Skills.WOODCUTTING) < 12) { - player.packetDispatch.sendMessage("You need a woodcutting level of at least 12 to chop down this tree.") - return@on true - } - lock(player, 5) - setVarp(player, varbit.varpId, 0) - player.faceLocation(CanoeUtils.getFaceLocation(player.location)) - player.animate(axe.animation) - setVarbit(player,varbit,STAGE_TREE_NONINTERACTABLE) - queueScript(player, 4, QueueStrength.SOFT) { - player.animator.stop() - setVarbit(player,varbit,STAGE_LOG_CHOPPED) - player.packetDispatch.sendSceneryAnimation(node.asScenery().getChild(player), FALL, false) - player.unlock() - return@queueScript stopExecuting(player) - } - return@on true - } - - on(STATION_IDs, IntType.SCENERY, "shape-canoe"){ player, node -> - val varbit = node.asScenery().definition.configFile - if(varbit.getValue(player) != STAGE_LOG_CHOPPED){ - setVarbit(player,varbit,0) - return@on true - } - player.faceLocation(CanoeUtils.getFaceLocation(player.location)) - player.interfaceManager.open(Component(SHAPING_INTERFACE)) - player.setAttribute("canoe-varbit",varbit) - return@on true - } - - on(STATION_IDs, IntType.SCENERY, "float canoe","float log","float waka"){ player, node -> - val varbit = node.asScenery().definition.configFile - val canoe = CanoeUtils.getCanoeFromVarbit(player, varbit) - player.animator.animate(PUSH) - lock(player, 2) - playAudio(player, Sounds.CANOE_ROLL_2731) - player.faceLocation(CanoeUtils.getFaceLocation(player.location)) - player.pulseManager.run(object : Pulse(){ - override fun pulse(): Boolean { - setVarbit(player,varbit, CanoeUtils.getCraftValue(canoe, true)) - player.packetDispatch.sendSceneryAnimation(node.asScenery(), FLOAT, false) - player.unlock() - return true - } - }) - return@on true - } - - on(STATION_IDs, IntType.SCENERY, "paddle log","paddle canoe"){ player, node -> - player.interfaceManager.open(Component(Components.CANOE_STATIONS_MAP_53)) - return@on true - } - } -} diff --git a/Server/src/main/content/global/travel/canoe/CanoeUtils.kt b/Server/src/main/content/global/travel/canoe/CanoeUtils.kt deleted file mode 100644 index 594fc272e..000000000 --- a/Server/src/main/content/global/travel/canoe/CanoeUtils.kt +++ /dev/null @@ -1,122 +0,0 @@ -package content.global.travel.canoe - -import core.cache.def.impl.VarbitDefinition -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import content.data.skill.SkillingTool -import core.game.world.map.Location -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Components - -object CanoeUtils { - private const val SHAPE_INTERFACE = Components.CANOE_52 - - private val FROM_LUMBRIDGE = mapOf(4 to 9887, 3 to 9888, 2 to 9889, 1 to 9890) - private val FROM_CHAMPIONS = mapOf(4 to 9891, 3 to 9892, 2 to 9893, 0 to 9906) - private val FROM_BARBARIAN = mapOf(4 to 9894, 3 to 9895, 1 to 9905, 0 to 9906) - private val FROM_EDGE = mapOf(4 to 9896, 2 to 9903, 1 to 9902, 0 to 9901) - private val FROM_WILDERNESS = mapOf(3 to 9900, 2 to 9899, 1 to 9898, 0 to 9897) - - fun checkCanoe(player: Player, canoe: Canoe){ - if(player.skills.getLevel(Skills.WOODCUTTING) < canoe.requiredLevel) return - player.packetDispatch.sendInterfaceConfig(SHAPE_INTERFACE,canoe.silhouetteChild,true) - player.packetDispatch.sendInterfaceConfig(SHAPE_INTERFACE,canoe.textChild,false) - } - - fun getCanoeFromVarbit(player: Player, varbit: VarbitDefinition): Canoe { - var bit = varbit.getValue(player) - if(bit > 10) bit -= 10 - return Canoe.values()[bit - 1] - } - - fun getCraftValue(canoe: Canoe, floating: Boolean): Int{ - return 1 + (canoe.ordinal + if (floating) 10 else 0) - } - - fun getStationIndex(location: Location): Int{ - return when(location.regionId){ - 12850 -> 0 - 12852,12596 -> 1 - 12341 -> 2 - 12342 -> 3 - 12603 -> 4 - else -> 0 - } - } - - fun getTravelAnimation(stationId: Int, destId: Int): Int { - return when(stationId){ - 0 -> FROM_LUMBRIDGE.getOrDefault(destId,0) - 1 -> FROM_CHAMPIONS.getOrDefault(destId,0) - 2 -> FROM_BARBARIAN.getOrDefault(destId,0) - 3 -> FROM_EDGE.getOrDefault(destId,0) - 4 -> FROM_WILDERNESS.getOrDefault(destId,0) - else -> 0 - } - } - - fun getShapeAnimation(axe: SkillingTool): Animation{ - return when(axe){ - SkillingTool.BRONZE_AXE -> Animation(6744) - SkillingTool.IRON_AXE -> Animation(6743) - SkillingTool.STEEL_AXE -> Animation(6742) - SkillingTool.BLACK_AXE -> Animation(6741) - SkillingTool.MITHRIL_AXE -> Animation(6740) - SkillingTool.ADAMANT_AXE -> Animation(6739) - SkillingTool.RUNE_AXE -> Animation(6738) - SkillingTool.DRAGON_AXE -> Animation(6745) - else -> axe.animation - } - } - - fun getDestinationFromButtonID(buttonID: Int): Location { - return when(buttonID){ - 47 -> Location.create(3240, 3242, 0) - 48 -> Location.create(3199, 3344, 0) - 3 -> Location.create(3109, 3415, 0) - 6 -> Location.create(3132, 3510, 0) - 49 -> Location.create(3139, 3796, 0) - else -> Location.create(3240, 3242, 0) - } - } - - fun getNameByIndex(index: Int): String{ - return when(index){ - 0 -> "Lumbridge" - 1 -> "the Champion's Guild." - 2 -> "Barbarian Village" - 3 -> "Edgeville" - 4 -> "the Wilderness Pond" - else -> "Uhhh report this." - } - } - - fun getFaceLocation(location: Location): Location{ - return when(getStationIndex(location)){ - 1 -> location.transform(0,-1,0) - 0,2,3 -> location.transform(-1,0,0) - else -> location - } - } - - fun getChopLocation(location: Location): Location{ - return when(getStationIndex(location)){ - 0 -> Location.create(3243, 3235, 0) - 1 -> Location.create(3204, 3343, 0) - 2 -> Location.create(3112, 3409, 0) - 3 -> Location.create(3132, 3508, 0) - else -> Location.create(0,0) - } - } - - fun getCraftFloatLocation(location: Location): Location{ - return when(getStationIndex(location)){ - 0 -> Location.create(3243, 3237, 0) - 1 -> Location.create(3202, 3343, 0) - 2 -> Location.create(3112, 3411, 0) - 3 -> Location.create(3132, 3510, 0) - else -> Location.create(0,0) - } - } - -} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt b/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt index 79df3f710..19a1f06aa 100644 --- a/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt +++ b/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt @@ -1,7 +1,6 @@ package content.region.misthalin.varrock.diary import content.global.skill.prayer.Bones -import content.global.travel.canoe.Canoe import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType @@ -13,6 +12,7 @@ import org.rs09.consts.Scenery import content.region.misthalin.varrock.dialogue.ElsieDialogue import content.global.handlers.iface.FairyRing import content.global.skill.magic.TeleportMethod +import content.global.travel.canoe.CanoeListener import core.api.getStatLevel import core.api.inBorders import core.game.diary.AreaDiaryTask @@ -219,8 +219,8 @@ class VarrockAchivementDiary : DiaryEventHookBase(DiaryType.VARROCK) { player.viewport.region?.let { when (it.id) { 12342 -> { - if (event.iface == Components.CANOE_52 - && Canoe.getCanoeFromChild(event.buttonId) == Canoe.WAKA) { + if (event.iface == CanoeListener.CANOE_SHAPING_INTERFACE + && event.buttonId == CanoeListener.CANOE_SHAPING_BUTTONS[CanoeListener.Companion.Canoes.WAKA.ordinal]) { finishTask( player, DiaryLevel.HARD, diff --git a/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java b/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java index dcf8fb7db..3b917dd84 100644 --- a/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java +++ b/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java @@ -206,6 +206,10 @@ public final class InterfaceManager { player.removeAttribute("runscript"); player.getPacketDispatch().sendRunScript(101, ""); } + // Component 333 is an immediate(no-fading) full-screen HD-mode black screen which auto-clears when interrupted. + if (overlay != null && overlay.getId() == 333) { + closeOverlay(); + } if (opened != null && opened.close(player)) { if (opened != null && (!opened.getDefinition().isWalkable() || opened.getId() == 14)) { PacketRepository.send(CloseInterface.class, new InterfaceContext(player, opened.getDefinition().getWindowPaneId(isResizable()), opened.getDefinition().getChildId(isResizable()), opened.getId(), opened.getDefinition().isWalkable()));