From 50eb295fda0ebc44643142bad38e2f3961dd0fec Mon Sep 17 00:00:00 2001 From: GregF Date: Mon, 15 Apr 2024 00:27:30 +0000 Subject: [PATCH] Rewrote grappling, fixes Yanille south shortcut, Catherby skill check and requirements text getting cut off --- .../shortcuts/CatherbyGrappleShortcut.kt | 114 -------------- .../shortcuts/FaladorGrapplePlugin.java | 145 ------------------ .../agility/shortcuts/KaramjaGrapple.java | 145 ------------------ .../agility/shortcuts/WaterOrbGrapple.java | 126 --------------- .../agility/shortcuts/YanilleGrapple.java | 121 --------------- .../grapple/AbstractGrappleShortcut.kt | 136 ++++++++++++++++ .../grapple/AbstractOneWayGrapple.kt | 39 +++++ .../grapple/AbstractTwoWayGrapple.kt | 16 ++ .../shortcuts/grapple/AlKharidGrapple.kt | 108 +++++++++++++ .../grapple/CatherbyGrappleShortcut.kt | 43 ++++++ .../shortcuts/grapple/FaladorGrapple.kt | 123 +++++++++++++++ .../shortcuts/grapple/KaramjaGrapple.kt | 108 +++++++++++++ .../shortcuts/grapple/WallGrappleInterface.kt | 68 ++++++++ .../shortcuts/grapple/WaterOrbGrapple.kt | 48 ++++++ .../shortcuts/grapple/YanilleGrapple.kt | 99 ++++++++++++ 15 files changed, 788 insertions(+), 651 deletions(-) delete mode 100644 Server/src/main/content/global/skill/agility/shortcuts/CatherbyGrappleShortcut.kt delete mode 100644 Server/src/main/content/global/skill/agility/shortcuts/FaladorGrapplePlugin.java delete mode 100644 Server/src/main/content/global/skill/agility/shortcuts/KaramjaGrapple.java delete mode 100644 Server/src/main/content/global/skill/agility/shortcuts/WaterOrbGrapple.java delete mode 100644 Server/src/main/content/global/skill/agility/shortcuts/YanilleGrapple.java create mode 100644 Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractGrappleShortcut.kt create mode 100644 Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractOneWayGrapple.kt create mode 100644 Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractTwoWayGrapple.kt create mode 100644 Server/src/main/content/global/skill/agility/shortcuts/grapple/AlKharidGrapple.kt create mode 100644 Server/src/main/content/global/skill/agility/shortcuts/grapple/CatherbyGrappleShortcut.kt create mode 100644 Server/src/main/content/global/skill/agility/shortcuts/grapple/FaladorGrapple.kt create mode 100644 Server/src/main/content/global/skill/agility/shortcuts/grapple/KaramjaGrapple.kt create mode 100644 Server/src/main/content/global/skill/agility/shortcuts/grapple/WallGrappleInterface.kt create mode 100644 Server/src/main/content/global/skill/agility/shortcuts/grapple/WaterOrbGrapple.kt create mode 100644 Server/src/main/content/global/skill/agility/shortcuts/grapple/YanilleGrapple.kt diff --git a/Server/src/main/content/global/skill/agility/shortcuts/CatherbyGrappleShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/CatherbyGrappleShortcut.kt deleted file mode 100644 index 2cefab3da..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/CatherbyGrappleShortcut.kt +++ /dev/null @@ -1,114 +0,0 @@ -package content.global.skill.agility.shortcuts - -import core.api.* -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Items -import org.rs09.consts.Scenery -import core.game.interaction.IntType -import core.game.interaction.InteractionListener - -/** - * Handles the Catherby to Taverley grapple shortcut - * @author Byte - */ -class CatherbyGrappleShortcut : InteractionListener { - - companion object { - private val START_LOCATION: Location = Location.create(2866, 3429, 0) - private val END_LOCATION: Location = Location.create(2869,3430,0) - - private val REQUIREMENTS = hashMapOf( - Skills.AGILITY to 32, - Skills.RANGE to 35, - Skills.STRENGTH to 35 - ) - - private val VALID_CROSSBOWS = intArrayOf( - Items.MITH_CROSSBOW_9181, - Items.ADAMANT_CROSSBOW_9183, - Items.RUNE_CROSSBOW_9185, - Items.DORGESHUUN_CBOW_8880 - ) - } - - private var rocks = getScenery(Location.create(2869,3429, 0)) - - override fun defineListeners() { - flagInstant() // execute listeners instantly without determining path - - on(Scenery.ROCKS_17042, IntType.SCENERY, "grapple") { player, _ -> - if (isPlayerInRangeToGrapple(player)) { - forceWalk(player, START_LOCATION, "smart") - } else { - sendMessage(player, "Nothing interesting happens.") - return@on true - } - - if (!doesPlayerHaveRequiredItemsEquipped(player)) { - sendDialogue(player, "You need a Mithril crossbow and a Mithril grapple in order to do this.") - return@on true - } - - if (!doesPlayerHaveRequiredLevels(player)) { - sendDialogueLines(player, - "You need at least " + - REQUIREMENTS[Skills.AGILITY] + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " + - REQUIREMENTS[Skills.RANGE] + " " + Skills.SKILL_NAME[Skills.RANGE] + ", ", - "and " + - REQUIREMENTS[Skills.STRENGTH] + " " + Skills.SKILL_NAME[Skills.STRENGTH] + " to use this shortcut." - ) - return@on true - } - - lock(player, 15) - submitWorldPulse(object : Pulse(2) { - var counter = 0 - override fun pulse() : Boolean { - when (counter++) { - 1 -> { - face(player, END_LOCATION) - // Audit: shows player climbing (probably a wall), need a cliff climb animation - animate(player, Animation(4455)) - } - 3 -> { - // Audit: shows grapple on rocks, but there is no rope - replaceScenery(rocks!!, rocks!!.id + 1, 10) - } - 8 -> { - teleport(player, END_LOCATION) - } - 9 -> { - sendMessage(player, "You successfully grapple the rock and climb the cliffside.") - unlock(player) - return true - } - } - return false - } - }) - - return@on true - } - } - - private fun doesPlayerHaveRequiredItemsEquipped(player: Player): Boolean { - return inEquipment(player, Items.MITH_GRAPPLE_9419) && anyInEquipment(player, *VALID_CROSSBOWS) - } - - private fun doesPlayerHaveRequiredLevels(player: Player): Boolean { - for ((skill, requiredLevel) in REQUIREMENTS) { - if (!hasLevelDyn(player, skill, requiredLevel)) { - return false - } - } - return true - } - - private fun isPlayerInRangeToGrapple(player: Player): Boolean { - return inBorders(player, START_LOCATION.x - 2, START_LOCATION.y - 2, START_LOCATION.x, START_LOCATION.y) - } -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/FaladorGrapplePlugin.java b/Server/src/main/content/global/skill/agility/shortcuts/FaladorGrapplePlugin.java deleted file mode 100644 index f9bf6f6a9..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/FaladorGrapplePlugin.java +++ /dev/null @@ -1,145 +0,0 @@ -package content.global.skill.agility.shortcuts; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import org.rs09.consts.Items; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.impl.ForceMovement; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.net.packet.PacketRepository; -import core.net.packet.context.MinimapStateContext; -import core.net.packet.out.MinimapState; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.node.entity.skill.Skills; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents the plugin used to handle the grappling of the falador wall. - * - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FaladorGrapplePlugin extends OptionHandler { - private static final HashMap REQUIREMENTS = new HashMap<>(); - private static String requirementsString; - - static { - REQUIREMENTS.putIfAbsent(Skills.AGILITY, 11); - REQUIREMENTS.putIfAbsent(Skills.RANGE, 19); - REQUIREMENTS.putIfAbsent(Skills.STRENGTH, 37); - - requirementsString = "You need at least " - + REQUIREMENTS.get(Skills.AGILITY) + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " - + REQUIREMENTS.get(Skills.RANGE) + " " + Skills.SKILL_NAME[Skills.RANGE] + ", and " - + REQUIREMENTS.get(Skills.STRENGTH) + " " + Skills.SKILL_NAME[Skills.STRENGTH] - + " to use this shortcut."; - } - - private static final int[] CBOWS = new int[]{ - Items.MITH_CROSSBOW_9181, - Items.ADAMANT_CROSSBOW_9183, - Items.RUNE_CROSSBOW_9185, - Items.DORGESHUUN_CBOW_8880 - }; - private static final Item MITH_GRAPPLE = new Item(9419); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(17049).getHandlers().put("option:grapple", this); - SceneryDefinition.forId(17050).getHandlers().put("option:grapple", this); - SceneryDefinition.forId(17051).getHandlers().put("option:jump", this); - SceneryDefinition.forId(17052).getHandlers().put("option:jump", this); - return this; - } - - @Override - public boolean handle(final Player player, final Node node, String option) { - Location destination; - Location current = player.getLocation(); - - switch (option) { - case "jump": - ForceMovement.run(player, - current, - node.asScenery().getId() == 17051 - ? Location.create(3033, 3390, 0) - : Location.create(3032, 3388, 0), - new Animation(7268), - 10); - break; - case "grapple": - destination = node.asScenery().getId() == 17049 - ? Location.create(3033, 3389, 1) - : Location.create(3032, 3391, 1); - - for (Map.Entry e : REQUIREMENTS.entrySet()) { - if (player.getSkills().getLevel(e.getKey()) < e.getValue()) { - player.getDialogueInterpreter().sendDialogue(requirementsString); - return true; - } - } - - if (!player.getEquipment().containsAtLeastOneItem(CBOWS) || !player.getEquipment().containsItem(MITH_GRAPPLE)) { - player.getDialogueInterpreter().sendDialogue("You need a Mithril crossbow and a Mithril grapple in order to do this."); - return true; - } - - player.lock(); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter = 1; - Component tab; - - @Override - public boolean pulse() { - switch (counter++) { - case 1: - player.faceLocation(destination); - player.visualize(new Animation(4455), new Graphics(760, 100)); - break; - case 8: - tab = player.getInterfaceManager().getSingleTab(); - player.getInterfaceManager().openOverlay(new Component(115)); - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); - player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12); - break; - case 13: - player.getProperties().setTeleportLocation(destination); - break; - case 14: - player.getInterfaceManager().restoreTabs(); - if (tab != null) { - player.getInterfaceManager().openTab(tab); - } - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); - player.getInterfaceManager().closeOverlay(); - player.getInterfaceManager().close(); - player.unlock(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 1, 2); - return true; - } - return false; - } - }); - break; - } - return true; - } - - @Override - public Location getDestination(final Node moving, final Node destination) { - return destination.asScenery().getId() == 17050 ? Location.create(3032, 3388, 0) : null; - } - -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/KaramjaGrapple.java b/Server/src/main/content/global/skill/agility/shortcuts/KaramjaGrapple.java deleted file mode 100644 index b623cf8f9..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/KaramjaGrapple.java +++ /dev/null @@ -1,145 +0,0 @@ -package content.global.skill.agility.shortcuts; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import org.rs09.consts.Items; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Direction; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.node.entity.skill.Skills; - -import java.util.HashMap; -import java.util.Map; - -@Initializable -public class KaramjaGrapple extends OptionHandler { - private static final HashMap REQUIREMENTS = new HashMap<>(); - private static final String requirementsString; - - static { - REQUIREMENTS.putIfAbsent(Skills.AGILITY, 53); - REQUIREMENTS.putIfAbsent(Skills.RANGE, 42); - REQUIREMENTS.putIfAbsent(Skills.STRENGTH, 21); - - requirementsString = "You need at least " - + REQUIREMENTS.get(Skills.AGILITY) + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " - + REQUIREMENTS.get(Skills.RANGE) + " " + Skills.SKILL_NAME[Skills.RANGE] + ", and " - + REQUIREMENTS.get(Skills.STRENGTH) + " " + Skills.SKILL_NAME[Skills.STRENGTH] - + " to use this shortcut."; - } - - private static final int[] CBOWS = new int[]{ - Items.MITH_CROSSBOW_9181, - Items.ADAMANT_CROSSBOW_9183, - Items.RUNE_CROSSBOW_9185, - Items.DORGESHUUN_CBOW_8880 - }; - private static final Item MITH_GRAPPLE = new Item(9419); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(17074).getHandlers().put("option:grapple", this); - // island tree 17074 +1 rope loop, +2 grappled one way, +3 grappled other way - // north tree 17056 +1 rope loop, +2 grappled - // south tree 17059 +1 rope loop, +2 grappled - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Location destination; - Location current = player.getLocation(); - Scenery startTree, endTree; - Direction direction; - if (current.getY() > 3134) { // starting at north side - startTree = RegionManager.getObject(Location.create(2874, 3144, 0)); - endTree = RegionManager.getObject(Location.create(2873, 3125, 0)); - destination = Location.create(2874, 3127, 0); - direction = Direction.SOUTH; - } else { - startTree = RegionManager.getObject(Location.create(2873, 3125, 0)); - endTree = RegionManager.getObject(Location.create(2874, 3144, 0)); - destination = Location.create(2874, 3142, 0); - direction = Direction.NORTH; - } - Scenery islandTree = RegionManager.getObject(Location.create(2873, 3134, 0)); - - - switch (option) { - case "grapple": - - for (Map.Entry e : REQUIREMENTS.entrySet()) { - if (player.getSkills().getLevel(e.getKey()) < e.getValue()) { - player.getDialogueInterpreter().sendDialogue(requirementsString); - return true; - } - } - - if (!player.getEquipment().containsAtLeastOneItem(CBOWS) || !player.getEquipment().containsItem(MITH_GRAPPLE)) { - player.getDialogueInterpreter().sendDialogue("You need a Mithril crossbow and a Mithril grapple in order to do this."); - return true; - } - - player.lock(); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter = 1; - Component tab; - - @Override - public boolean pulse() { - switch (counter++) { - // TODO animations not implemented. - // See ~11min in https://www.youtube.com/watch?v=qpB53rzYqrA - // don't know how to get ropes to show up. The tree objects have grapples and stuff but don't look like the video and aren't the right directions - // splash gfx are 68 and 69, not sure why there are two - // not sure what swimming animation is, could be 4464 thru 4468 - case 1: - player.faceLocation(player.getLocation().transform(direction)); - player.animate(new Animation(4230)); - break; - case 3: - //player.getPacketDispatch().sendPositionedGraphic(67, 10, 0, player.getLocation().transform(direction, 5)); // - break; - case 4: - //ObjectBuilder.replace(startTree, startTree.transform(startTree.getId() + 1), 10); - //ObjectBuilder.replace(islandTree, islandTree.transform(islandTree.getId() + 1), 10); - break; - case 5: - break; - case 13: - player.getProperties().setTeleportLocation(destination); - break; - case 14: - player.unlock(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 6); - return true; - } - return false; - } - }); - break; - } - return true; - } - - @Override - public Location getDestination(final Node moving, final Node destination) { - // Run between tree and water before firing grapple - if (moving.getLocation().getY() > 3134) { // starting at north side - return Location.create(2874, 3142, 0); - } else { - return Location.create(2874, 3127, 0); - } - } -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/WaterOrbGrapple.java b/Server/src/main/content/global/skill/agility/shortcuts/WaterOrbGrapple.java deleted file mode 100644 index ab59e66d7..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/WaterOrbGrapple.java +++ /dev/null @@ -1,126 +0,0 @@ -package content.global.skill.agility.shortcuts; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import org.rs09.consts.Items; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.item.Item; -import core.game.node.scenery.Scenery; -import core.game.node.scenery.SceneryBuilder; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.node.entity.skill.Skills; - -import java.util.HashMap; -import java.util.Map; - -@Initializable -public class WaterOrbGrapple extends OptionHandler { - private static final HashMap REQUIREMENTS = new HashMap<>(); - private static final String requirementsString; - - static { - REQUIREMENTS.putIfAbsent(Skills.AGILITY, 36); - REQUIREMENTS.putIfAbsent(Skills.RANGE, 39); - REQUIREMENTS.putIfAbsent(Skills.STRENGTH, 22); - - requirementsString = "You need at least " - + REQUIREMENTS.get(Skills.AGILITY) + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " - + REQUIREMENTS.get(Skills.RANGE) + " " + Skills.SKILL_NAME[Skills.RANGE] + ", and " - + REQUIREMENTS.get(Skills.STRENGTH) + " " + Skills.SKILL_NAME[Skills.STRENGTH] - + " to use this shortcut."; - } - - private static final int[] CBOWS = new int[]{ - Items.MITH_CROSSBOW_9181, - Items.ADAMANT_CROSSBOW_9183, - Items.RUNE_CROSSBOW_9185, - Items.DORGESHUUN_CBOW_8880 - }; - private static final Item MITH_GRAPPLE = new Item(9419); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(17062).getHandlers().put("option:grapple", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Location destination; - Location current = player.getLocation(); - Scenery rock = RegionManager.getObject(Location.create(2841, 3426, 0)); - Scenery tree = RegionManager.getObject(Location.create(2841, 3434, 0)); - - switch (option) { - case "grapple": - destination = Location.create(2841, 3433, 0); - - for (Map.Entry e : REQUIREMENTS.entrySet()) { - if (player.getSkills().getLevel(e.getKey()) < e.getValue()) { - player.getDialogueInterpreter().sendDialogue(requirementsString); - return true; - } - } - - if (!player.getEquipment().containsAtLeastOneItem(CBOWS) || !player.getEquipment().containsItem(MITH_GRAPPLE)) { - player.getDialogueInterpreter().sendDialogue("You need a Mithril crossbow and a Mithril grapple in order to do this."); - return true; - } - - player.lock(); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter = 1; - Component tab; - - @Override - public boolean pulse() { - switch (counter++) { - // TODO this animation sequence is wrong. sendPositionedGraphic doesn't work correctly, and rest of water crossing not well implemented - // See 4:24 in https://www.youtube.com/watch?v=O90y-N_vwTc - // rope gfx is 67 - // splash gfx are 68 and 69, not sure why there are two - // not sure what swimming animation is, could be 4464 thru 4468 - case 1: - player.faceLocation(destination); - player.animate(new Animation(4230)); - break; - case 3: - player.getPacketDispatch().sendPositionedGraphic(67, 10, 0, Location.create(2840,3427,0)); // - break; - case 4: - SceneryBuilder.replace(rock, rock.transform(rock.getId() + 1), 10); - SceneryBuilder.replace(tree, tree.transform(tree.getId() + 1), 10); - break; - case 5: - break; - case 13: - player.getProperties().setTeleportLocation(destination); - break; - case 14: - player.unlock(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 10); - return true; - } - return false; - } - }); - break; - } - return true; - } - - @Override - public Location getDestination(final Node moving, final Node destination) { - // Run between rock and stream before firing grapple - return Location.create(2841, 3427, 0); - } -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/YanilleGrapple.java b/Server/src/main/content/global/skill/agility/shortcuts/YanilleGrapple.java deleted file mode 100644 index d37e1964a..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/YanilleGrapple.java +++ /dev/null @@ -1,121 +0,0 @@ -package content.global.skill.agility.shortcuts; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.impl.ForceMovement; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.net.packet.PacketRepository; -import core.net.packet.context.MinimapStateContext; -import core.net.packet.out.MinimapState; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.game.node.entity.skill.Skills; - -import java.util.HashMap; -import java.util.Map; - -@Initializable -public class YanilleGrapple extends OptionHandler { - private static final HashMap REQUIREMENTS = new HashMap<>(); - private static String requirementsString; - - static { - REQUIREMENTS.putIfAbsent(Skills.AGILITY, 39); - REQUIREMENTS.putIfAbsent(Skills.RANGE, 21); - REQUIREMENTS.putIfAbsent(Skills.STRENGTH, 38); - - requirementsString = "You need at least " - + REQUIREMENTS.get(Skills.AGILITY) + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " - + REQUIREMENTS.get(Skills.RANGE) + " " + Skills.SKILL_NAME[Skills.RANGE] + ", and " - + REQUIREMENTS.get(Skills.STRENGTH) + " " + Skills.SKILL_NAME[Skills.STRENGTH] - + " to use this shortcut."; - } - - private static final Item MITH_CBOW = new Item(9181); - private static final Item MITH_GRAPPLE = new Item(9419); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(17047).getHandlers().put("option:grapple", this); - SceneryDefinition.forId(17048).getHandlers().put("option:jump", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Location destination; - Location current = player.getLocation(); - - switch (option) { - case "jump": - ForceMovement.run(player, - current, - current.getY() < 3074 ? Location.create(2556,3072,0) : Location.create(2556,3075,0), - new Animation(7268), - 10); - break; - case "grapple": - destination = current.getY() < 3073 - ? Location.create(2556, 3073, 1) - : Location.create(2556, 3074, 1); - - for (Map.Entry e : REQUIREMENTS.entrySet()) { - if (player.getSkills().getLevel(e.getKey()) < e.getValue()) { - player.getDialogueInterpreter().sendDialogue(requirementsString); - return true; - } - } - - if (!player.getEquipment().containsItem(MITH_CBOW) || !player.getEquipment().containsItem(MITH_GRAPPLE)) { - player.getDialogueInterpreter().sendDialogue("You need a Mithril crossbow and a Mithril grapple in order to do this."); - return true; - } - - player.lock(); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter = 1; - Component tab; - - @Override - public boolean pulse() { - switch (counter++) { - case 1: - player.faceLocation(destination); - player.visualize(new Animation(4455), new Graphics(760, 100)); - break; - case 8: - tab = player.getInterfaceManager().getSingleTab(); - player.getInterfaceManager().openOverlay(new Component(115)); - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); - player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12); - break; - case 13: - player.getProperties().setTeleportLocation(destination); - break; - case 14: - player.getInterfaceManager().restoreTabs(); - if (tab != null) { - player.getInterfaceManager().openTab(tab); - } - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); - player.getInterfaceManager().closeOverlay(); - player.getInterfaceManager().close(); - player.unlock(); - return true; - } - return false; - } - }); - break; - } - return true; - } -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractGrappleShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractGrappleShortcut.kt new file mode 100644 index 000000000..4d868405f --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractGrappleShortcut.kt @@ -0,0 +1,136 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.scenery.Scenery +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items + +abstract class AbstractGrappleShortcut : InteractionListener { + /** + * Make sure that you have flagInstant in your listeners. + * If you do not the player will run and try to touch the grapple point + */ + private val VALID_CROSSBOWS = intArrayOf( + Items.MITH_CROSSBOW_9181, + Items.ADAMANT_CROSSBOW_9183, + Items.RUNE_CROSSBOW_9185, + Items.DORGESHUUN_CBOW_8880 + ) + + protected abstract val REQUIREMENTS: HashMap + + protected abstract val grappleStartLocation: Location + protected abstract val grappleEndLocation: Location + + // use lazy so that the skill requirements can be populated after the child builds them + private val requirementString1: String by lazy { + "You need at least " + + REQUIREMENTS[Skills.AGILITY] + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " + + REQUIREMENTS[Skills.RANGE] + " " + Skills.SKILL_NAME[Skills.RANGE] + "," + } + private val requirementString2: String by lazy { + "and " + + REQUIREMENTS[Skills.STRENGTH] + " " + Skills.SKILL_NAME[Skills.STRENGTH] + " to use this shortcut." + } + + + // What needs to have its model changed during a grapple + protected abstract val grappleScenery: List + + // What animation to use when getting the player across + protected abstract val animation: Animation + // How long should the animation last (in ticks) + protected abstract val animationDuration: Int + + protected abstract fun animation(animationStage: Int, player: Player): Boolean + + + // The message that can appear if there should be one after grappling + protected val message: String? = null + + private fun getRequirementString(): Array { + val requirementString = arrayOf(requirementString1, requirementString2) + return requirementString + } + + private fun doesPlayerHaveRequiredItemsEquipped(player: Player): Boolean { + return inEquipment(player, Items.MITH_GRAPPLE_9419) && anyInEquipment(player, *VALID_CROSSBOWS) + } + + private fun doesPlayerHaveRequiredLevels(player: Player): Boolean { + for ((skill, requiredLevel) in REQUIREMENTS) { + if (!hasLevelDyn(player, skill, requiredLevel)) { + return false + } + } + return true + } + + protected open fun isPlayerInRangeToGrapple(player: Player, startLoc: Location, range: Int): Boolean { + return inBorders(player, startLoc.x - range, startLoc.y - range, + startLoc.x + range, startLoc.y + range) + } + + /** + * See if the [player] is close enough to the [startLoc] (based on [range] to + * try and grapple. This will return false if the [player] is too far away, + * if the player does not have the right levels or if the player does not have + * the correct gear equipped. + */ + protected fun canGrapple(player: Player, startLoc: Location, range: Int): Boolean { + if (isPlayerInRangeToGrapple(player, startLoc, range)) { + forceWalk(player, startLoc, "smart") + } else { + // todo should this be "you are too far away" or something like that? + sendMessage(player, "Nothing interesting happens.") + return false + } + + if (!doesPlayerHaveRequiredItemsEquipped(player)) { + sendDialogue(player, "You need a Mithril crossbow and a Mithril grapple in order to do this.") + return false + } + + if (!doesPlayerHaveRequiredLevels(player)) { + sendDialogueLines(player, + *getRequirementString() + ) + return false + } + return true + } + + protected fun grapple(player: Player, message: String?): Boolean { + + lock(player, animationDuration) + // TODO is this right? should we force the player to cross? + queueScript(player, strength = QueueStrength.SOFT) { stage: Int -> + if (animation(stage, player)){ + // We're done with the animation + return@queueScript stopExecuting(player) + } + else{ + return@queueScript delayScript(player, 1) + } + } + + message?.let{ + player.sendMessage(message) + } + return true + } + + + /** + * If an achievement diary can be updated it should be done here + */ + protected open fun updateDiary(player: Player): Boolean{ + return false + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractOneWayGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractOneWayGrapple.kt new file mode 100644 index 000000000..7645b0315 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractOneWayGrapple.kt @@ -0,0 +1,39 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.node.entity.player.Player + +abstract class AbstractOneWayGrapple : AbstractGrappleShortcut() { + + + override fun animation(animationStage: Int, player: Player): Boolean { + when (animationStage) { + 1 -> { + // Point towards the grapple landing zone + face(player, grappleEndLocation) + // Start the grapple animation + animate(player, animation) + } + + 5 -> { + for (tgt in grappleScenery) { + // Add grapple effects to all scenery (for 10 ticks) + replaceScenery(tgt!!, tgt.id + 1, 10) + } + } + + 5 + animationDuration -> { + // After the animation is done teleport to the landing zone + teleport(player, grappleEndLocation) + } + + 5 + animationDuration + 1 -> { + // free the player + unlock(player) + updateDiary(player) + return true + } + } + return false + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractTwoWayGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractTwoWayGrapple.kt new file mode 100644 index 000000000..4cbb3aa1c --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AbstractTwoWayGrapple.kt @@ -0,0 +1,16 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location + +abstract class AbstractTwoWayGrapple : AbstractGrappleShortcut(){ + + abstract var direction: Direction? + + abstract var startLoc: Location? + abstract var endLoc: Location? + + protected abstract fun setStartEndSide(player: Player, margin: Int = 5) + protected abstract fun getGrappleScenery(direction: Direction): List +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/AlKharidGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AlKharidGrapple.kt new file mode 100644 index 000000000..5f98b03e4 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/AlKharidGrapple.kt @@ -0,0 +1,108 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Scenery + +class AlKharidGrapple : AbstractTwoWayGrapple(){ + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 8, Skills.RANGE to 37, Skills.STRENGTH to 19) + + // Lumbridge + override val grappleStartLocation: Location = Location(3246, 3179, 0) + + // Al Kharid + override val grappleEndLocation: Location = Location(3259, 3179, 0) + + override var direction: Direction? = null + override var startLoc: Location? = null + override var endLoc: Location? = null + + override var grappleScenery: List = listOf() + + override val animation: Animation = Animation(4230) + override val animationDuration: Int = 9 + override fun animation(animationStage: Int, player: Player): Boolean { + when (animationStage) { + 1 -> { + face(player, endLoc!!) + animate(player, animation) + } + + 5 -> { + for (tgt in grappleScenery) { + if ((tgt!!.id == 17068)) { + // This is the raft + continue + } + replaceScenery(tgt, tgt.id + 1, 10) + } + } + + 5 + animationDuration -> { + teleport(player, endLoc!!) + } + 5 + animationDuration + 1 -> { + unlock(player) + updateDiary(player) + return true + } + } + return false + } + + override fun defineListeners() { + flagInstant() // execute listeners instantly without determining path + + on(Scenery.BROKEN_RAFT_17068, IntType.SCENERY, "grapple") { player, target -> + // Check if we are on the east or the west of the broken raft + // East = Lum + setStartEndSide(player) + if (!canGrapple(player, startLoc!!, 2)){ + return@on true + } + grapple(player,message) + return@on true + } + } + + + override fun setStartEndSide(player: Player, margin: Int) { + if (player.location.x < grappleStartLocation.x + margin){ + // We're on the west side + direction = Direction.EAST // got to jump east + startLoc = grappleStartLocation + endLoc = grappleEndLocation + } + else { + // we're on the east side + direction = Direction.WEST // got to jump west + startLoc = grappleEndLocation + endLoc = grappleStartLocation + } + + grappleScenery = getGrappleScenery(direction!!) + } + + override fun getGrappleScenery(direction: Direction): List { + val lumbridgeTree = getScenery(Location(3244, 3179, 0)) + val alKharidTree = getScenery(Location(3260, 3178, 0)) + val startTree : core.game.node.scenery.Scenery? + val endTree : core.game.node.scenery.Scenery? + val raft = getScenery(Location(3252, 3179, 0)) + if (direction == Direction.EAST){ + startTree = lumbridgeTree + endTree = alKharidTree + } + else{ + startTree = alKharidTree + endTree = lumbridgeTree + } + return listOf(startTree,endTree, raft) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/CatherbyGrappleShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/CatherbyGrappleShortcut.kt new file mode 100644 index 000000000..e5bcd2a8f --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/CatherbyGrappleShortcut.kt @@ -0,0 +1,43 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Scenery +import core.game.interaction.IntType +import core.plugin.Initializable +import kotlin.collections.HashMap + + +@Initializable +class CatherbyGrappleShortcut : AbstractOneWayGrapple(){ + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 32, Skills.RANGE to 35, Skills.STRENGTH to 35) + + override val grappleStartLocation: Location = Location.create(2866, 3429, 0) + + override val grappleEndLocation: Location = Location.create(2869,3430,0) + + // todo this is the wrong animation + override val animation: Animation = Animation(4455) + + override val animationDuration: Int = 9 + + override val grappleScenery: List = listOf( + getScenery(Location.create(2869,3429, 0)) // rocks + ) + + override fun defineListeners() { + flagInstant() + + on(Scenery.ROCKS_17042, IntType.SCENERY, "grapple"){ player, _ -> + if (!canGrapple(player, grappleStartLocation, 1)) { + return@on true + } + grapple(player, message) + return@on true + } + } + +} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/FaladorGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/FaladorGrapple.kt new file mode 100644 index 000000000..58e8e2fe2 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/FaladorGrapple.kt @@ -0,0 +1,123 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.plugin.Initializable +import org.rs09.consts.Scenery + +abstract class AbstractFaladorGrapple(private var wallGrappleInterface: WallGrappleInterface = WallGrappleInterfaceImpl()): AbstractOneWayGrapple() { + + override val animation: Animation = Animation(4455) + + override val animationDuration: Int = 14 + + // There are no scenery items to hook so don't let children override this + final override val grappleScenery: List = listOf() + + protected fun jump(player: Player, destination: Location): Boolean { + return wallGrappleInterface.jump(player, destination) + } + + override fun animation(animationStage: Int, player: Player): Boolean { + when (animationStage) { + 1 -> { + player.faceLocation(grappleEndLocation) + visualize(player, animation, Graphics(760, 100)) + } + + 8 -> { + wallGrappleInterface.fadeToBlack(player) + } + + 13 -> teleport(player, grappleEndLocation) + 14 -> { + wallGrappleInterface.showGame(player) + unlock(player) + updateDiary(player) + return true + } + } + return false + } + +} +@Initializable +class FaladorGrappleNorth : AbstractFaladorGrapple() { + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 11, Skills.RANGE to 19, Skills.STRENGTH to 37) + + override val grappleStartLocation: Location = Location.create(3033, 3390, 0) + + + override val grappleEndLocation: Location = Location.create(3033, 3389, 1) + + override fun defineListeners() { + flagInstant() + on(Scenery.WALL_17049, IntType.SCENERY, "grapple") { player, _ -> + if(!canGrapple(player, grappleStartLocation, 4)) { + return@on true + } + grapple(player, message) + return@on true + } + + on(Scenery.WALL_17051, IntType.SCENERY, "jump"){ player, _ -> + jump(player, grappleStartLocation) + return@on true + } + } + + override fun isPlayerInRangeToGrapple(player: Player, startLoc: Location, range: Int): Boolean { + // Do not let the player grapple from the other side of the wall + return inBorders(player, startLoc.x - range, startLoc.y, + startLoc.x + range, startLoc.y + 2) + } + + override fun updateDiary(player: Player): Boolean { + player.achievementDiaryManager.finishTask(player, DiaryType.FALADOR, 1, 2) + return true + } +} + +@Initializable +class FaladorGrappleSouth : AbstractFaladorGrapple() { + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 11, Skills.RANGE to 19, Skills.STRENGTH to 37) + + override val grappleStartLocation: Location = Location.create(3032, 3388, 0) + + override val grappleEndLocation: Location = Location.create(3032, 3389, 1) + + override fun defineListeners() { + flagInstant() + on(Scenery.WALL_17050, IntType.SCENERY, "grapple") { player, _ -> + if(!canGrapple(player, grappleStartLocation, 4)) { + return@on true + } + grapple(player, message) + return@on true + } + + on(Scenery.WALL_17052, IntType.SCENERY, "jump"){ player, _ -> + jump(player, grappleStartLocation) + return@on true + } + } + + override fun isPlayerInRangeToGrapple(player: Player, startLoc: Location, range: Int): Boolean { + // Do not let the player grapple from the other side of the wall + return inBorders(player, startLoc.x - range, startLoc.y, + startLoc.x + range, startLoc.y - 2) + } + + override fun updateDiary(player: Player): Boolean { + player.achievementDiaryManager.finishTask(player, DiaryType.FALADOR, 1, 2) + return true + } +} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/KaramjaGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/KaramjaGrapple.kt new file mode 100644 index 000000000..96337db8e --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/KaramjaGrapple.kt @@ -0,0 +1,108 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import org.rs09.consts.Scenery + +@Initializable +class KaramjaGrapple : AbstractTwoWayGrapple(){ + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 53, Skills.RANGE to 42, Skills.STRENGTH to 21) + + // South + override val grappleStartLocation: Location = Location.create(2874, 3127, 0) + + + // North + override val grappleEndLocation: Location = Location.create(2874,3142,0) + + override var direction: Direction? = null + override var startLoc: Location? = null + override var endLoc: Location? = null + + override var grappleScenery: List = listOf() + + override val animation: Animation = Animation(4230) + override val animationDuration: Int = 9 + + override fun animation(animationStage: Int, player: Player): Boolean { + when (animationStage) { + 1 -> { + face(player, endLoc!!) + animate(player, animation) + } + + 5 -> { + for (tgt in grappleScenery) { + replaceScenery(tgt!!, tgt.id + 1, 10) + } + } + + 5 + animationDuration -> { + teleport(player, endLoc!!) + } + 5 + animationDuration + 1 -> { + unlock(player) + updateDiary(player) + return true + } + } + return false + } + + override fun defineListeners() { + flagInstant() + + on(Scenery.STRONG_TREE_17074, IntType.SCENERY, "grapple"){ player, _ -> + setStartEndSide(player) + if(!canGrapple(player, startLoc!!, 1)){ + return@on true + } + grapple(player, message) + return@on true + } + + } + + override fun setStartEndSide(player: Player, margin: Int) { + if (player.location.y > grappleEndLocation.y - margin){ // we're on the north side + direction = Direction.SOUTH // got to jump south + startLoc = grappleEndLocation + endLoc = grappleStartLocation + } + else { + direction = Direction.NORTH // got to jump north + startLoc = grappleStartLocation + endLoc = grappleEndLocation + } + + grappleScenery = getGrappleScenery(direction!!) + } + + override fun getGrappleScenery(direction: Direction): List { + val startTree : core.game.node.scenery.Scenery? + val endTree : core.game.node.scenery.Scenery? + val islandTree = getScenery(Location(2873, 3134, 0)) + if (direction == Direction.NORTH){ + startTree = getScenery(Location(2874, 3144, 0)) + endTree = getScenery(Location(2873, 3125, 0)) + } + else{ + startTree = getScenery(Location(2874, 3144, 0)) + endTree = getScenery(Location(2873, 3125, 0)) + } + return listOf(startTree,endTree, islandTree) + } + + override fun updateDiary(player: Player): Boolean { + player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 2, 6) + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/WallGrappleInterface.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/WallGrappleInterface.kt new file mode 100644 index 000000000..ad8444b13 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/WallGrappleInterface.kt @@ -0,0 +1,68 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.* +import core.game.component.Component +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +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 + +interface WallGrappleInterface { + var tab: Component? + fun jump(player: Player, destination: Location): Boolean + fun fadeToBlack(player: Player): Component + fun showGame(player: Player): Boolean +} + + +class WallGrappleInterfaceImpl: WallGrappleInterface{ + override var tab: Component? = null + + override fun jump(player: Player, destination: Location): Boolean { + // todo this doesn't look great compared to what it used to look like + forceWalk(player, destination,"smart" ) + face(player, destination) + // We're teleporting if we are animating so make the strength SOFT + queueScript(player, strength = QueueStrength.SOFT){ stage: Int -> + when (stage){ + 1 -> animate(player, Animation(7268)) + 2 ->{ + teleport(player, destination) + return@queueScript stopExecuting(player) + } + } + return@queueScript delayScript(player, 1) + + } + return true + } + + + override fun fadeToBlack(player: Player): Component { + // todo make this work. Right now the tab is always null + tab = player.interfaceManager.singleTab + player.interfaceManager.openOverlay(Component(115)) + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + player.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12) + if (tab == null){ + println("Panic") + return Component(1) + } + return tab!! + } + + override fun showGame(player: Player): Boolean { + player.interfaceManager.restoreTabs() + if (tab != null) { + player.interfaceManager.openTab(tab) + } + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) + closeOverlay(player) + closeInterface(player) + return true + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/WaterOrbGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/WaterOrbGrapple.kt new file mode 100644 index 000000000..8fc2a2167 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/WaterOrbGrapple.kt @@ -0,0 +1,48 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.getScenery +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import org.rs09.consts.Scenery +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable + +@Initializable +class WaterOrbGrapple : AbstractOneWayGrapple(){ + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 36, Skills.RANGE to 39, Skills.STRENGTH to 22) + + override val grappleStartLocation: Location = Location.create(2841, 3427, 0) + + override val grappleEndLocation: Location = Location.create(2841, 3433, 0) + + override val animation: Animation = Animation(4230) + + override val animationDuration: Int = 9 + + override val grappleScenery: List = listOf( + getScenery(Location.create(2841, 3426, 0)), // rock + getScenery(Location.create(2841, 3434, 0)) // tree + ) + + override fun defineListeners() { + flagInstant() + + on(Scenery.CROSSBOW_TREE_17062, IntType.SCENERY, "grapple"){ player, _ -> + if (!canGrapple(player, grappleStartLocation, 1)) { + return@on true + } + grapple(player, message) + return@on true + } + } + + override fun updateDiary(player: Player): Boolean { + player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 10) + return true + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/agility/shortcuts/grapple/YanilleGrapple.kt b/Server/src/main/content/global/skill/agility/shortcuts/grapple/YanilleGrapple.kt new file mode 100644 index 000000000..dc5351275 --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/grapple/YanilleGrapple.kt @@ -0,0 +1,99 @@ +package content.global.skill.agility.shortcuts.grapple + +import core.api.inBorders +import core.api.teleport +import core.api.unlock +import core.api.visualize +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.plugin.Initializable +import org.rs09.consts.Scenery + +@Initializable +class YanilleGrapple(private var wallGrappleInterface: WallGrappleInterface = WallGrappleInterfaceImpl()): AbstractTwoWayGrapple() { + + override val REQUIREMENTS: HashMap = hashMapOf(Skills.AGILITY to 11, Skills.RANGE to 19, Skills.STRENGTH to 37) + + override val grappleStartLocation: Location = Location.create(2556, 3072, 0) + + override val grappleEndLocation: Location = Location.create(2556, 3073, 1) + + override var direction: Direction? = null + override var startLoc: Location? = null + override var endLoc: Location? = null + override fun setStartEndSide(player: Player, margin: Int) { + // Start location is where you end after jumping in the opposite way + if (player.location.y > 3073 ){ + // We are north of the middle of the wall + startLoc = Location.create(2556, 3075, 0) // This is where you grapple from/land after jumping + endLoc = Location.create(2556, 3074, 1) // + } + else { + // We are south of the middle of the wall + startLoc = Location.create(2556, 3072, 0) + endLoc = Location.create(2556, 3073, 1) + } + } + + override fun getGrappleScenery(direction: Direction): List { + return emptyList() + } + + override var grappleScenery: List = listOf() + + override val animation: Animation = Animation(4455) + override val animationDuration: Int = 9 + override fun animation(animationStage: Int, player: Player): Boolean { + when (animationStage) { + 1 -> { + player.faceLocation(endLoc) + visualize(player, animation, Graphics(760, 100)) + } + + 8 -> { + wallGrappleInterface.fadeToBlack(player) + } + + 13 -> teleport(player, endLoc!!) + 14 -> { + wallGrappleInterface.showGame(player) + unlock(player) + updateDiary(player) + return true + } + } + return false + } + + + override fun defineListeners() { + // Do not use flagListeners here + // The player needs to be able to touch the target + on(Scenery.WALL_17047, IntType.SCENERY, "grapple") { player, _ -> + setStartEndSide(player, 0) + if(!canGrapple(player, startLoc!!, 4)){ + return@on true + } + grapple(player, message) + return@on true + } + + on(Scenery.WALL_17048, IntType.SCENERY, "jump") { player, _ -> + setStartEndSide(player, 0) + wallGrappleInterface.jump(player, startLoc!!) + return@on true + } + } + + override fun isPlayerInRangeToGrapple(player: Player, startLoc: Location, range: Int): Boolean { + // Do not let the player grapple from the other side of the wall + return inBorders( + player, startLoc.x - range, startLoc.y, + startLoc.x + range, startLoc.y - 2) + } +}