Rewrote grappling, fixes Yanille south shortcut, Catherby skill check and requirements text getting cut off

This commit is contained in:
GregF 2024-04-15 00:27:30 +00:00 committed by Ryan
parent 24b314426e
commit 50eb295fda
15 changed files with 788 additions and 651 deletions

View file

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

View file

@ -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<Integer, Integer> 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<Object> 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<Integer, Integer> 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;
}
}

View file

@ -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<Integer, Integer> 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<Object> 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<Integer, Integer> 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);
}
}
}

View file

@ -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<Integer, Integer> 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<Object> 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<Integer, Integer> 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);
}
}

View file

@ -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<Integer, Integer> 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<Object> 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<Integer, Integer> 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;
}
}

View file

@ -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<Int, Int>
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<Scenery?>
// 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<String> {
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
}
}

View file

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

View file

@ -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<core.game.node.scenery.Scenery?>
}

View file

@ -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<Int, Int> = 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<core.game.node.scenery.Scenery?> = 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<core.game.node.scenery.Scenery?> {
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)
}
}

View file

@ -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<Int, Int> = 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<core.game.node.scenery.Scenery?> = 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
}
}
}

View file

@ -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<core.game.node.scenery.Scenery?> = 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<Int, Int> = 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<Int, Int> = 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
}
}

View file

@ -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<Int, Int> = 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<core.game.node.scenery.Scenery?> = 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<core.game.node.scenery.Scenery?> {
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
}
}

View file

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

View file

@ -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<Int, Int> = 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<core.game.node.scenery.Scenery?> = 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
}
}

View file

@ -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<Int, Int> = 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<core.game.node.scenery.Scenery?> {
return emptyList()
}
override var grappleScenery: List<core.game.node.scenery.Scenery?> = 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)
}
}