diff --git a/.gitignore b/.gitignore index f99bd4ca5..e7436de2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ builddir logs +out/ Management-Server/target/* Management-Server/.gradle Management-Server/build/ @@ -11,6 +12,9 @@ Server/**/*.class Server/build/ Server/.gradle/ Server/data/eco/grand_exchange_db.emp +Server/data/eco/grandexchange.db +Server/data/serverstore/ +Server/data/global_kill_stats.json Server/ge_test.db Management-Server/managementprops/ Server/worldprops/ @@ -25,3 +29,4 @@ gradle **/*.swp **/*.swo build/kotlin/sessions/ +**/*.iml diff --git a/Server/src/main/java/core/game/container/impl/BankContainer.java b/Server/src/main/java/core/game/container/impl/BankContainer.java index 0064651bb..2dbacef83 100644 --- a/Server/src/main/java/core/game/container/impl/BankContainer.java +++ b/Server/src/main/java/core/game/container/impl/BankContainer.java @@ -91,7 +91,27 @@ public final class BankContainer extends Container { return true; }); player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6); - InterfaceContainer.generateItems(player, player.getInventory().toArray(), new String[]{"Examine", "Deposit-X", "Deposit-All", "Deposit-10", "Deposit-5", "Deposit-1"}, 11, 15, 5, 7); + refreshDepositBoxInterface(); + } + + /** + * Invalidates the visual state of deposit box interface + * forcing the client to re-draw the items + */ + public void refreshDepositBoxInterface() + { + InterfaceContainer.generateItems( + player, + player.getInventory().toArray(), + new String[] { + "Examine", + "Deposit-X", + "Deposit-All", + "Deposit-10", + "Deposit-5", + "Deposit-1" + }, 11, 15, 5, 7 + ); } /** @@ -498,6 +518,13 @@ public final class BankContainer extends Container { */ public void setTabIndex(int tabIndex) { this.tabIndex = tabIndex; + + /* + * Kludge to update the interface + * after dumping all to prevent + * "invisible" items in slots. + */ + update(true); } /** diff --git a/Server/src/main/java/core/game/content/zone/phasmatys/PhasmatysZone.java b/Server/src/main/java/core/game/content/zone/phasmatys/PhasmatysZone.java index 07b0a916b..132b5c98c 100644 --- a/Server/src/main/java/core/game/content/zone/phasmatys/PhasmatysZone.java +++ b/Server/src/main/java/core/game/content/zone/phasmatys/PhasmatysZone.java @@ -1,6 +1,7 @@ package core.game.content.zone.phasmatys; import static api.ContentAPIKt.*; + import core.game.content.global.Bones; import core.game.content.global.action.ClimbActionHandler; import core.game.interaction.Option; @@ -22,6 +23,7 @@ import core.game.world.map.zone.ZoneBuilder; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.NPCs; import rs09.plugin.ClassScanner; /** @@ -65,6 +67,13 @@ public final class PhasmatysZone extends MapZone implements Plugin { player = e.asPlayer(); if (target instanceof NPC) { NPC npc = (NPC) target; + + if (npc.getId() == NPCs.GHOST_BANKER_1702) { + // Kludge to prevent this module from overriding NPC + // handling until I rip this piece of Vexia shit out. + return false; + } + if ((npc.getName().toLowerCase().contains("ghost") || npc.getName().equalsIgnoreCase("velorina") || npc.getName().contains("husband")) && !hasAmulet(player)) { player.getDialogueInterpreter().open("ghosty dialogue", target); return true; @@ -76,13 +85,13 @@ public final class PhasmatysZone extends MapZone implements Plugin { return true; case 5267: player.animate(Animation.create(536)); - sendMessage(player, "The trapdoor opens..."); + sendMessage(player, "The trapdoor opens..."); SceneryBuilder.replace((Scenery) target, ((Scenery) target).transform(5268)); return true; case 5268: if (option.getName().equals("Close")) { player.animate(Animation.create(535)); - sendMessage(player, "You close the trapdoor."); + sendMessage(player, "You close the trapdoor."); SceneryBuilder.replace((Scenery) target, ((Scenery) target).transform(5267)); } else { sendMessage(player, "You climb down through the trapdoor..."); @@ -94,11 +103,11 @@ public final class PhasmatysZone extends MapZone implements Plugin { SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(7435)); } break; - case 7435: // open trapdoor in bar - if (option.getName().equalsIgnoreCase("close")) { - SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(7434)); - } - break; + case 7435: // open trapdoor in bar + if (option.getName().equalsIgnoreCase("close")) { + SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(7434)); + } + break; case 9308: if (player.getSkills().getStaticLevel(Skills.AGILITY) < 58) { player.sendMessage("You need an agility level of at least 58 to climb down this wall."); @@ -255,9 +264,9 @@ public final class PhasmatysZone extends MapZone implements Plugin { /** * Checks if bones are in the hopper. * - * @param player the player. + * @param player the player. * @param inHopper in the hopper or in the bin. - * @param object the object. + * @param object the object. * @return {@code True} if so. */ public static boolean hasBones(Player player, Scenery object, boolean inHopper) { diff --git a/Server/src/main/java/core/game/interaction/object/BankingPlugin.java b/Server/src/main/java/core/game/interaction/object/BankingPlugin.java index d955774a7..75cbcf674 100644 --- a/Server/src/main/java/core/game/interaction/object/BankingPlugin.java +++ b/Server/src/main/java/core/game/interaction/object/BankingPlugin.java @@ -1,44 +1,21 @@ package core.game.interaction.object; -import static api.ContentAPIKt.*; - -import api.IfaceSettingsBuilder; -import api.events.InteractionEvent; -import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.game.component.CloseEvent; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.container.access.InterfaceContainer; -import core.game.content.dialogue.DialogueAction; import core.game.content.dialogue.DialoguePlugin; import core.game.content.dialogue.FacialExpression; import core.game.interaction.OptionHandler; import core.game.node.Node; -import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.IronmanMode; import core.game.node.entity.player.link.appearance.Gender; 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.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 kotlin.Unit; -import rs09.game.content.dialogue.DepositAllDialogue; -import rs09.game.ge.GrandExchangeRecords; import rs09.game.ge.GrandExchangeOffer; +import rs09.game.ge.GrandExchangeRecords; import rs09.game.world.GameWorld; -import java.text.NumberFormat; - /** * Represents the plugin used for anything related to banking. * @@ -50,78 +27,16 @@ public final class BankingPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.setOptionHandler("use-quickly", this); - SceneryDefinition.setOptionHandler("use", this); - SceneryDefinition.setOptionHandler("bank", this); - SceneryDefinition.setOptionHandler("collect", this); - SceneryDefinition.setOptionHandler("deposit", this); - new BankingInterface().newInstance(arg); - new BankDepositInterface().newInstance(arg); - new BankNPCPlugin().newInstance(arg); - new BankNPC().newInstance(arg); new BankerDialogue().init(); return this; } @Override public boolean handle(Player player, Node node, String option) { - final Scenery object = (Scenery) node; - if (player.getIronmanManager().checkRestriction(IronmanMode.ULTIMATE)) { - return true; - } - if (object.getName().contains("Bank") || object.getName().contains("Deposit")) { - //TODO: REPLACE THIS ALL WITH A LISTENER. I DONT FEEL LIKE IT RIGHT NOW. - player.dispatch(new InteractionEvent(object, option)); - switch (option) { - case "use": -// final Location l = object.getLocation(); -// final Location p = player.getLocation(); -// final NPC npc = Repository.findNPC(l.transform(l.getX() - p.getX(), l.getY() - p.getY(), 0)); -// if (node.getId() == 4483) { -// player.getBank().open(); -// return true; -// } -// if (npc != null && DialogueInterpreter.contains(npc.getId())) { -// npc.faceLocation(node.getLocation()); -// player.getDialogueInterpreter().open(npc.getId(), npc.getId()); -// } else { -// player.getDialogueInterpreter().open(494); -// } -// return true; - case "use-quickly": - case "bank": - player.getBankPinManager().openType(1); - checkAchievements(player); - return true; - case "collect": - GrandExchangeRecords.getInstance(player).openCollectionBox(); - return true; - case "deposit": - openDepositBox(player); - return true; - } - } + player.sendChat(":crab: :crab: :crab: RETARDED PLUGIN IS GONE :crab: :crab: :crab:"); return true; } - /** - * Method used to open the deposit box. - * - * @param player the player. - */ - private void openDepositBox(final Player player) { - player.getInterfaceManager().open(new Component(11)).setCloseEvent(new CloseEvent() { - @Override - public boolean close(Player player, Component c) { - player.getInterfaceManager().openDefaultTabs(); - return true; - } - }); - //player.getInterfaceManager().closeDefaultTabs(); - player.getInterfaceManager().removeTabs(0, 1, 2, 3, 4, 5, 6); - InterfaceContainer.generateItems(player, player.getInventory().toArray(), new String[]{"Examine", "Deposit-X", "Deposit-All", "Deposit-10", "Deposit-5", "Deposit-1",}, 11, 15, 5, 7); - } - /** * Represents the dialogue plugin used for all bankers. * @@ -317,7 +232,6 @@ public final class BankingPlugin extends OptionHandler { case 2: case 3: player.getBankPinManager().openType(buttonId); - checkAchievements(player); end(); break; case 4: @@ -340,7 +254,6 @@ public final class BankingPlugin extends OptionHandler { case 2: case 3: player.getBankPinManager().openType(buttonId); - checkAchievements(player); end(); break; case 4: @@ -381,382 +294,4 @@ public final class BankingPlugin extends OptionHandler { } } - - /** - * Represents the component plugin used to handle banking interfaces. - * - * @author Emperor - * @author 'Vexia - * @version 1.0 - */ - public static final class BankingInterface extends ComponentPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(763, this); - ComponentDefinition.put(762, this); - ComponentDefinition.put(767, this); - return this; - } - - @Override - public void open(Player player, Component component) { - super.open(player, component); - player.getBank().sendBankSpace(); - int settings = new IfaceSettingsBuilder().enableAllOptions().enableSlotSwitch().setInterfaceEventsDepth(2).build(); - player.getPacketDispatch().sendIfaceSettings(settings, 73, 762, 0, 496); - } - - @Override - public boolean handle(final Player p, Component component, int opcode, int button, final int slot, int itemId) { - final Item item = component.getId() == 762 ? p.getBank().get(slot) : p.getInventory().get(slot); - switch (component.getId()) { - case 767: - switch (button) { - case 10: - p.getBank().open(); - break; - } - break; - case 762: - switch (button) { - case 18: - p.getDialogueInterpreter().open(new DepositAllDialogue().getID()); - return true; - case 23: - p.getDialogueInterpreter().sendOptions("Select an Option", "Check bank value", "Banking assistance", "Close"); - p.getDialogueInterpreter().addAction(new DialogueAction() { - - @Override - public void handle(Player player, int buttonId) { - switch (buttonId) { - case 2: - p.getDialogueInterpreter().sendItemMessage(new Item(995, 50000), "
Your bank is worth approximately " + NumberFormat.getInstance().format(p.getBank().getWealth()) + " coins."); - break; - case 3: - p.getBank().close(); - p.getInterfaceManager().open(new Component(767)); - break; - case 4: - p.getDialogueInterpreter().close(); - break; - } - } - - }); - break; - case 14: - p.getBank().setInsertItems(!p.getBank().isInsertItems()); - return true; - case 16: - p.getBank().setNoteItems(!p.getBank().isNoteItems()); - return true; - case 73: - int amount = 0; - switch (opcode) { - case 155: - amount = 1; - break; - case 196: - amount = 5; - break; - case 124: - amount = 10; - break; - case 199: - amount = p.getBank().getLastAmountX(); - break; - case 234: - sendInputDialogue(p, false, "Enter the amount:", (value) -> { - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - s = s.replace("m","000000"); - s = s.replace("M","000000"); - int val = Integer.parseInt(s); - p.getBank().takeItem(slot, val); - p.getBank().updateLastAmountX(val); - return Unit.INSTANCE; - }); - break; - case 9: - p.sendMessage(p.getBank().get(slot).getDefinition().getExamine()); - break; - case 168: - amount = p.getBank().getAmount(item); - break; - case 166: - amount = p.getBank().getAmount(item) - 1; - break; - default: - return false; - } - if (amount > 0) { - final int withdraw = amount; - GameWorld.getPulser().submit(new Pulse(1, p) { - @Override - public boolean pulse() { - if (item == null) { - return true; - } - p.getBank().takeItem(slot, withdraw); - return true; - } - }); - } - return true; - case 20://search - p.setAttribute("search", true); - break; - case 41: - case 39: - case 37: - case 35: - case 33: - case 31: - case 29: - case 27: - case 25: - if (p.getAttribute("search", false)) { - p.getBank().reopen(); - } - int tabIndex = -((button - 41) / 2); - switch (opcode) { - case 155: - p.getBank().setTabIndex(tabIndex); - return true; - case 196: - p.getBank().collapseTab(tabIndex); - return true; - } - return false; - } - break; - case 763:// overlay. - switch (opcode) { - case 155: - p.getBank().addItem(slot, 1); - break; - case 196: - p.getBank().addItem(slot, 5); - break; - case 124: - p.getBank().addItem(slot, 10); - break; - case 199: - p.getBank().addItem(slot, p.getBank().getLastAmountX()); - break; - case 234: - sendInputDialogue(p, false, "Enter the amount:", (value) -> { - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - int val = Integer.parseInt(s); - p.getBank().addItem(slot, val); - p.getBank().updateLastAmountX(val); - return Unit.INSTANCE; - }); - break; - case 168: - p.getBank().addItem(slot, p.getInventory().getAmount(item)); - break; - } - break; - } - return true; - } - - } - - /** - * Represents the bank deposit interface handler. - * - * @author 'Vexia - * @version 1.0 - */ - public static final class BankDepositInterface extends ComponentPlugin { - - /** - * Represents the deposit animation. - */ - private static final Animation DEPOSIT_ANIMATION = new Animation(834); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(11, this); - return this; - } - - @Override - public boolean handle(final Player p, Component component, int opcode, int button, final int slot, int itemId) { - final Item item = p.getInventory().get(slot); - if (item == null && button != 15 && button != 13) { - return true; - } - switch (component.getId()) { - case 11: - p.animate(DEPOSIT_ANIMATION); - switch (opcode) { - case 155: // Deposit items - p.getBank().addItem(slot, 1); - break; - case 196: - p.getBank().addItem(slot, 5); - break; - case 124: - p.getBank().addItem(slot, 10); - break; - case 199: - p.getPulseManager().run(new Pulse(1, p) { - @Override - public boolean pulse() { - p.getBank().addItem(slot, p.getInventory().getAmount(item)); - InterfaceContainer.generateItems(p, p.getInventory().toArray(), new String[]{"Examine", "Deposit-X", "Deposit-All", "Deposit-10", "Deposit-5", "Deposit-1",}, 11, 15, 5, 7); - return true; - } - }); - return true; - case 234: - sendInputDialogue(p, false, "Enter the amount:", (value) -> { - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - int val = Integer.parseInt(s); - p.getBank().addItem(slot, val); - InterfaceContainer.generateItems(p, p.getInventory().toArray(), new String[]{"Examine", "Deposit-X", "Deposit-All", "Deposit-10", "Deposit-5", "Deposit-1",}, 11, 15, 5, 7); - return Unit.INSTANCE; - }); - break; - case 168: - p.sendMessage(item.getDefinition().getExamine()); - break; - } - switch (button) { - case 13: - p.getFamiliarManager().dumpBob(); - break; - } - break; - } - InterfaceContainer.generateItems(p, p.getInventory().toArray(), new String[]{"Examine", "Deposit-X", "Deposit-All", "Deposit-10", "Deposit-5", "Deposit-1",}, 11, 15, 5, 7); - return true; - } - - } - - /** - * Represents the plugin used to handle the banker npc. - * - * @author 'Vexia - * @author Emperor - * @version 1.01 - */ - public static final class BankNPCPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.setOptionHandler("bank", this); - NPCDefinition.setOptionHandler("collect", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final NPC npc = ((NPC) node); - npc.faceLocation(node.getLocation()); - if (option.equals("bank")) { - player.getBank().open(); - checkAchievements(player); - } else { - GrandExchangeRecords.getInstance(player).openCollectionBox(); - } - return true; - } - - @Override - public Location getDestination(Node n, Node node) { - NPC npc = (NPC) node; - if (npc.getAttribute("facing_booth", false)) { - Direction dir = npc.getDirection(); - return npc.getLocation().transform(dir.getStepX() << 1, dir.getStepY() << 1, 0); - } - if (npc.getId() == 6533) { - return Location.create(3167, 3489, 0);// ge bankers. - } else if (npc.getId() == 6534) { - return Location.create(3167, 3490, 0);// ge bankers. - } else if (npc.getId() == 6535) { - return Location.create(3162, 3489, 0); - } else if (npc.getId() == 6532) { - return Location.create(3162, 3489, 0); - } else if (npc.getId() == 4907) { - return npc.getLocation().transform(0, -2, 0); - } - return super.getDestination(npc, node); - } - } - - /** - * Represents the abstract npc of a banker. - * - * @author 'Vexia - * @author Emperor - * @version 1.2 - */ - public static final class BankNPC extends AbstractNPC { - - /** - * Represents the ids of this class. - */ - private final int[] IDS = new int[]{4907, 44, 45, 166, 494, 495, 496, 497, 498, 499, 902, 1036, 1360, 1702, 2163, 2164, 2354, 2355, 2568, 2569, 2570, 2619, 3046, 3198, 3199, 4296, 4519, 5257, 5258, 5259, 5260, 5383, 5488, 5776, 5777, 5898, 5912, 5913, 6200, 6362, 6532, 6533, 6534, 6535, 6538, 7049, 7050, 7445, 7446, 7605}; - - /** - * Constructs a new {@code BankNPC} {@code Object}. - */ - public BankNPC() { - super(0, null); - } - - @Override - public void init() { - super.init(); - for (int i = 0; i < 4; i++) { - Direction dir = Direction.get(i); - Location loc = getLocation().transform(dir.getStepX(), dir.getStepY(), 0); - Scenery bank = RegionManager.getObject(loc); - if (bank != null && bank.getName().equals("Bank booth")) { - setDirection(dir); - setAttribute("facing_booth", true); - super.setWalks(false); - break; - } - } - } - - /** - * Constructs a new {@code BankNPC} {@code Object}. - * - * @param id The NPC id. - * @param location The location. - */ - private BankNPC(int id, Location location) { - super(id, location); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new BankNPC(id, location); - } - - @Override - public int[] getIds() { - return IDS; - } - } - - private static void checkAchievements(Player player) { - // Access the bank in Draynor Village - if (player.getLocation().withinDistance(Location.create(3092, 3243, 0))) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 15); - } - } } diff --git a/Server/src/main/java/core/game/node/entity/impl/WalkingQueue.java b/Server/src/main/java/core/game/node/entity/impl/WalkingQueue.java index e6b0d6fda..fb8af9fe3 100644 --- a/Server/src/main/java/core/game/node/entity/impl/WalkingQueue.java +++ b/Server/src/main/java/core/game/node/entity/impl/WalkingQueue.java @@ -375,8 +375,17 @@ public final class WalkingQueue { * @param running The running flag (ctrl + click reward). */ public void reset(boolean running) { + Location loc = entity.getLocation(); + + if (loc == null) { + throw new IllegalStateException( + "The entity location provided was null." + + "Are you sure anything down the stack trace isn't providing an NPC with a null location?" + ); + } + walkingQueue.clear(); - walkingQueue.add(new Point(entity.getLocation().getX(), entity.getLocation().getY())); + walkingQueue.add(new Point(loc.getX(), loc.getY())); this.running = running; } diff --git a/Server/src/main/java/core/game/node/entity/player/link/InterfaceManager.java b/Server/src/main/java/core/game/node/entity/player/link/InterfaceManager.java index e979a2846..6e3a7cde2 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/InterfaceManager.java +++ b/Server/src/main/java/core/game/node/entity/player/link/InterfaceManager.java @@ -1,5 +1,7 @@ package core.game.node.entity.player.link; +import api.events.InterfaceCloseEvent; +import api.events.InterfaceOpenEvent; import core.game.component.Component; import core.game.component.InterfaceType; import core.game.node.entity.combat.equipment.WeaponInterface; @@ -125,8 +127,13 @@ public final class InterfaceManager { SystemLogger.logErr("Set interface type to WINDOW_PANE for component " + windowsPane.getId() + ", definition requires updating!"); windowsPane.getDefinition().setType(InterfaceType.WINDOW_PANE); } + PacketRepository.send(WindowsPane.class, new WindowsPaneContext(player, windowsPane.getId(), overlap ? 1 : 0)); windowsPane.open(player); + + if (opened != null) + player.dispatch(new InterfaceOpenEvent(opened)); + return windowsPane; } @@ -136,8 +143,12 @@ public final class InterfaceManager { SystemLogger.logErr("Set interface type to WINDOW_PANE for component " + windowsPane.getId() + ", definition requires updating!"); windowsPane.getDefinition().setType(InterfaceType.SINGLE_TAB); } + PacketRepository.send(WindowsPane.class, new WindowsPaneContext(player, windowsPane.getId(), type)); windowsPane.open(player); + + if (opened != null) + player.dispatch(new InterfaceOpenEvent(opened)); } /** @@ -161,7 +172,11 @@ public final class InterfaceManager { return null; } component.open(player); - return opened = component; + + opened = component; + player.dispatch(new InterfaceOpenEvent(opened)); + + return opened; } /** @@ -191,6 +206,7 @@ public final class InterfaceManager { if (opened != null && opened.close(player)) { if (opened != null && (!opened.getDefinition().isWalkable() || opened.getId() == 14)) { PacketRepository.send(CloseInterface.class, new InterfaceContext(player, opened.getDefinition().getWindowPaneId(isResizable()), opened.getDefinition().getChildId(isResizable()), opened.getId(), opened.getDefinition().isWalkable())); + player.dispatch(new InterfaceCloseEvent(opened)); } opened = null; } diff --git a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarManager.java b/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarManager.java index c22e7b7b3..1b5ab8791 100644 --- a/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarManager.java +++ b/Server/src/main/java/core/game/node/entity/skill/summoning/familiar/FamiliarManager.java @@ -281,28 +281,6 @@ public final class FamiliarManager { player.getInterfaceManager().setViewedTab(7); } - /** - * Dumps the bob. - */ - public void dumpBob() { - if (!hasFamiliar()) { - player.getPacketDispatch().sendMessage("You don't have a familiar."); - return; - } - Familiar familiar = getFamiliar(); - if (!familiar.isBurdenBeast()) { - player.getPacketDispatch().sendMessage("Your familiar is not a beast of burden."); - return; - } - BurdenBeast beast = ((BurdenBeast) familiar); - if (!player.getBank().hasSpaceFor(beast.getContainer())) { - player.getPacketDispatch().sendMessage("There is not enough space left in your bank."); - return; - } - player.getBank().addAll(beast.getContainer()); - beast.getContainer().clear(); - } - /** * Makes the pet eat. * @param foodId The food item id. diff --git a/Server/src/main/java/core/net/packet/in/RunScriptPacketHandler.java b/Server/src/main/java/core/net/packet/in/RunScriptPacketHandler.java deleted file mode 100644 index 307029346..000000000 --- a/Server/src/main/java/core/net/packet/in/RunScriptPacketHandler.java +++ /dev/null @@ -1,47 +0,0 @@ -package core.net.packet.in; - -import static api.ContentAPIKt.*; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; -import core.net.packet.IncomingPacket; -import core.net.packet.IoBuffer; -import core.tools.StringUtils; -import kotlin.jvm.functions.Function1; - -/** - * Represents the incoming packet to handle a run script. - * @author 'Vexia - */ -public class RunScriptPacketHandler implements IncomingPacket { - - @Override - public void decode(Player player, int opcode, IoBuffer buffer) { - final Function1 script = player.getAttribute("runscript", null); - if (script == null || player.getLocks().isInteractionLocked()) { - return; - } - Object value = ""; - if (opcode == 244) { - value = StringUtils.longToString(buffer.getLong()); - } else if (opcode == 23) { - value = buffer.getInt(); - } else if (opcode == 65){ - value = buffer.getString();//"longInput" - } else { - value = buffer.getInt(); - } - if(value instanceof Integer) - { - if((int) value <= 0){ - player.removeAttribute("runscript"); - return; - } - } - try { - script.invoke(value); - } catch (NumberFormatException nfe){ - sendDialogue(player, "That number's a bit large, don't you think?"); - } - player.removeAttribute("runscript"); - } -} diff --git a/Server/src/main/kotlin/api/ContentAPI.kt b/Server/src/main/kotlin/api/ContentAPI.kt index 17024af16..cc79f5a60 100644 --- a/Server/src/main/kotlin/api/ContentAPI.kt +++ b/Server/src/main/kotlin/api/ContentAPI.kt @@ -22,6 +22,7 @@ import core.game.node.entity.player.link.emote.Emotes import core.game.node.entity.player.link.quest.QuestRepository import core.game.node.entity.skill.Skills import core.game.node.entity.skill.gather.SkillingTool +import core.game.node.entity.skill.summoning.familiar.BurdenBeast import core.game.node.item.GroundItem import core.game.node.item.GroundItemManager import core.game.node.item.Item @@ -39,6 +40,7 @@ import core.game.world.map.zone.ZoneBuilder import core.game.world.update.flag.chunk.AnimateObjectUpdateFlag import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics +import org.rs09.consts.Items import rs09.game.content.dialogue.DialogueFile import rs09.game.content.dialogue.SkillDialogueHandler import rs09.game.content.global.GlobalKillCounter @@ -57,7 +59,7 @@ import rs09.game.world.repository.Repository * @return the tool which meets the requirements or null if none. */ fun getTool(player: Player, pickaxe: Boolean): SkillingTool? { - return if(pickaxe) SkillingTool.getPickaxe(player) else SkillingTool.getHatchet(player) + return if (pickaxe) SkillingTool.getPickaxe(player) else SkillingTool.getHatchet(player) } /** @@ -99,7 +101,7 @@ fun inInventory(player: Player, item: Int, amount: Int = 1): Boolean { * @param id the ID of the item to check for the amount of * @return the amount of the given ID in the player's inventory */ -fun amountInInventory(player: Player, id: Int): Int{ +fun amountInInventory(player: Player, id: Int): Int { return player.inventory.getAmount(id) } @@ -109,7 +111,7 @@ fun amountInInventory(player: Player, id: Int): Int{ * @param id the ID of the item to check for * @return the amount of the ID in the player's bank. */ -fun amountInBank(player: Player, id: Int): Int{ +fun amountInBank(player: Player, id: Int): Int { return player.bank.getAmount(id) } @@ -119,7 +121,7 @@ fun amountInBank(player: Player, id: Int): Int{ * @param id the ID of the item to check for * @return the amount of the ID in the player's equipment. */ -fun amountInEquipment(player: Player, id: Int): Int{ +fun amountInEquipment(player: Player, id: Int): Int { return player.equipment.getAmount(id) } @@ -153,9 +155,8 @@ fun hasAnItem(player: Player, vararg ids: Int): ContainerisedItem { * @param god the God whose equipment we are checking for * @return true if the player has an item corresponding to the given god, false otherwise */ -fun hasGodItem(player: Player, god: God): Boolean -{ - god.validItems.forEach { if(amountInEquipment(player, it) > 0) return true } +fun hasGodItem(player: Player, god: God): Boolean { + god.validItems.forEach { if (amountInEquipment(player, it) > 0) return true } return false } @@ -173,7 +174,7 @@ fun removeItem(player: Player, item: T, container: Container = Container.INV else -> throw IllegalStateException("Invalid value passed for item") } - return when(container){ + return when (container) { Container.INVENTORY -> player.inventory.remove(it) Container.BANK -> player.bank.remove(it) Container.EQUIPMENT -> player.equipment.remove(it) @@ -189,7 +190,7 @@ fun removeItem(player: Player, item: T, container: Container = Container.INV * @return true if the item was successfully added */ fun addItem(player: Player, id: Int, amount: Int = 1, container: Container = Container.INVENTORY): Boolean { - val cont = when(container){ + val cont = when (container) { Container.INVENTORY -> player.inventory Container.BANK -> player.bank Container.EQUIPMENT -> player.equipment @@ -207,7 +208,7 @@ fun addItem(player: Player, id: Int, amount: Int = 1, container: Container = Con * @return the item that was previously in the slot, or null if none. */ fun replaceSlot(player: Player, slot: Int, item: Item, container: Container = Container.INVENTORY): Item? { - val cont = when(container) { + val cont = when (container) { Container.INVENTORY -> player.inventory Container.EQUIPMENT -> player.equipment Container.BANK -> player.bank @@ -222,20 +223,20 @@ fun replaceSlot(player: Player, slot: Int, item: Item, container: Container = Co * @param id the ID of the item to add to the player's inventory * @param amount the amount of the ID to add to the player's inventory, defaults to 1 */ -fun addItemOrDrop(player: Player, id: Int, amount: Int = 1){ +fun addItemOrDrop(player: Player, id: Int, amount: Int = 1) { val item = Item(id, amount) - if(!player.inventory.add(item)) GroundItemManager.create(item,player) + if (!player.inventory.add(item)) GroundItemManager.create(item, player) } /** * Clears an NPC with the "poof" smoke graphics commonly seen with random event NPCs. * @param npc the NPC object to initialize */ -fun poofClear(npc: NPC){ - submitWorldPulse(object : Pulse(){ +fun poofClear(npc: NPC) { + submitWorldPulse(object : Pulse() { var counter = 0 override fun pulse(): Boolean { - when(counter++){ + when (counter++) { 2 -> { npc.isInvisible = true; Graphics.send(Graphics(86), npc.location) } @@ -331,14 +332,14 @@ fun rewardXP(player: Player, skill: Int, amount: Double) { * @param loc the location to move the new object to if necessary. Defaults to null. */ fun replaceScenery(toReplace: Scenery, with: Int, for_ticks: Int, loc: Location? = null) { - val newLoc = when(loc){ + val newLoc = when (loc) { null -> toReplace.location else -> loc } if (for_ticks == -1) { - SceneryBuilder.replace(toReplace, toReplace.transform(with,toReplace.rotation,newLoc)) + SceneryBuilder.replace(toReplace, toReplace.transform(with, toReplace.rotation, newLoc)) } else { - SceneryBuilder.replace(toReplace, toReplace.transform(with,toReplace.rotation, newLoc), for_ticks) + SceneryBuilder.replace(toReplace, toReplace.transform(with, toReplace.rotation, newLoc), for_ticks) } toReplace.isActive = false } @@ -351,12 +352,12 @@ fun replaceScenery(toReplace: Scenery, with: Int, for_ticks: Int, loc: Location? * @Param rotation the Direction of the rotation it should use. Direction.NORTH, Direction.SOUTH, etc * @param loc the location to move the new object to if necessary. Defaults to null. */ -fun replaceScenery(toReplace: Scenery, with: Int, for_ticks: Int, rotation: Direction, loc: Location? = null){ - val newLoc = when(loc){ +fun replaceScenery(toReplace: Scenery, with: Int, for_ticks: Int, rotation: Direction, loc: Location? = null) { + val newLoc = when (loc) { null -> toReplace.location else -> loc } - val rot = when(rotation){ + val rot = when (rotation) { Direction.NORTH_WEST -> 0 Direction.NORTH -> 1 Direction.NORTH_EAST -> 2 @@ -367,9 +368,9 @@ fun replaceScenery(toReplace: Scenery, with: Int, for_ticks: Int, rotation: Dire Direction.WEST -> 3 } if (for_ticks == -1) { - SceneryBuilder.replace(toReplace, toReplace.transform(with,rot, newLoc)) + SceneryBuilder.replace(toReplace, toReplace.transform(with, rot, newLoc)) } else { - SceneryBuilder.replace(toReplace, toReplace.transform(with,rot,newLoc), for_ticks) + SceneryBuilder.replace(toReplace, toReplace.transform(with, rot, newLoc), for_ticks) } toReplace.isActive = false } @@ -453,7 +454,7 @@ fun animateScenery(player: Player, obj: Scenery, animationId: Int, global: Boole /** * Send an object animation independent of a player */ -fun animateScenery(obj: Scenery, animationId: Int){ +fun animateScenery(obj: Scenery, animationId: Int) { val animation = Animation(animationId) animation.setObject(obj) getRegionChunk(obj.location).flag(AnimateObjectUpdateFlag(animation)) @@ -484,8 +485,27 @@ fun spawnProjectile(source: Entity, dest: Entity, projectileId: Int) { * @param speed the speed the projectile travels at * @param angle the angle the projectile travels at */ -fun spawnProjectile(source: Location, dest: Location, projectile: Int, startHeight: Int, endHeight: Int, delay: Int, speed: Int, angle: Int){ - Projectile.create(source, dest, projectile, startHeight, endHeight, delay, speed, angle, source.getDistance(dest).toInt()).send() +fun spawnProjectile( + source: Location, + dest: Location, + projectile: Int, + startHeight: Int, + endHeight: Int, + delay: Int, + speed: Int, + angle: Int +) { + Projectile.create( + source, + dest, + projectile, + startHeight, + endHeight, + delay, + speed, + angle, + source.getDistance(dest).toInt() + ).send() } /** @@ -522,7 +542,7 @@ fun openInterface(player: Player, id: Int) { * @param player the player to open the interface for * @param id the ID of the interface to open */ -fun openOverlay(player: Player, id: Int){ +fun openOverlay(player: Player, id: Int) { player.interfaceManager.openOverlay(Component(id)) } @@ -530,7 +550,7 @@ fun openOverlay(player: Player, id: Int){ * Closes any open overlays for the given player * @param player the player to close for */ -fun closeOverlay(player: Player){ +fun closeOverlay(player: Player) { player.interfaceManager.closeOverlay() } @@ -576,7 +596,7 @@ fun sendDialogue(player: Player, message: String) { * @param forced whether or not to force the animation (usually not necessary) */ fun animate(entity: Entity, anim: T, forced: Boolean = false) { - val animation = when(anim){ + val animation = when (anim) { is Int -> Animation(anim) is Animation -> anim else -> throw IllegalStateException("Invalid value passed for anim") @@ -630,15 +650,15 @@ fun findNPC(id: Int): NPC? { * @param y the Y coordinate to use * @param z the Z coordinate to use */ -fun getScenery(x: Int, y: Int, z: Int): Scenery?{ - return RegionManager.getObject(z,x,y) +fun getScenery(x: Int, y: Int, z: Int): Scenery? { + return RegionManager.getObject(z, x, y) } /** * Gets the spawned scenery from the world map using the given Location object. * @param loc the Location object to use. */ -fun getScenery(loc: Location): Scenery?{ +fun getScenery(loc: Location): Scenery? { return RegionManager.getObject(loc) } @@ -667,7 +687,7 @@ fun findLocalNPC(entity: Entity, id: Int): NPC? { * @param entity the entity to check around * @param ids the IDs of the NPCs to look for */ -fun findLocalNPCs(entity: Entity, ids: IntArray): List{ +fun findLocalNPCs(entity: Entity, ids: IntArray): List { return RegionManager.getLocalNpcs(entity).filter { it.id in ids }.toList() } @@ -677,7 +697,7 @@ fun findLocalNPCs(entity: Entity, ids: IntArray): List{ * @param ids the IDs of the NPCs to look for * @param distance The maximum distance to the entity. */ -fun findLocalNPCs(entity: Entity, ids: IntArray, distance: Int): List{ +fun findLocalNPCs(entity: Entity, ids: IntArray, distance: Int): List { return RegionManager.getLocalNpcs(entity, distance).filter { it.id in ids }.toList() } @@ -685,8 +705,7 @@ fun findLocalNPCs(entity: Entity, ids: IntArray, distance: Int): List{ * @param regionId the ID of the region * @return a [ZoneBorders] encapsulating the entire region indicated by the provided regionId */ -fun getRegionBorders(regionId: Int) : ZoneBorders -{ +fun getRegionBorders(regionId: Int): ZoneBorders { return ZoneBorders.forRegion(regionId) } @@ -742,9 +761,9 @@ fun unlock(entity: Entity) { * @param transformTo the ID of the NPC to turn into * @param restoreTicks the number of ticks until the NPC returns to normal */ -fun transformNpc(npc: NPC, transformTo: Int, restoreTicks: Int){ +fun transformNpc(npc: NPC, transformTo: Int, restoreTicks: Int) { npc.transform(transformTo) - Pulser.submit(object : Pulse(restoreTicks){ + Pulser.submit(object : Pulse(restoreTicks) { override fun pulse(): Boolean { npc.reTransform() return true @@ -755,14 +774,14 @@ fun transformNpc(npc: NPC, transformTo: Int, restoreTicks: Int){ /** * Produces a Location object using the given x,y,z values */ -fun location(x: Int, y: Int, z: Int): Location{ - return Location.create(x,y,z) +fun location(x: Int, y: Int, z: Int): Location { + return Location.create(x, y, z) } /** * Checks if the given entity is within the given ZoneBorders */ -fun inBorders(entity: Entity, borders: ZoneBorders): Boolean{ +fun inBorders(entity: Entity, borders: ZoneBorders): Boolean { return borders.insideBorder(entity) } @@ -770,13 +789,13 @@ fun inBorders(entity: Entity, borders: ZoneBorders): Boolean{ * Checks if the given entity is within the given borders */ fun inBorders(entity: Entity, swX: Int, swY: Int, neX: Int, neY: Int): Boolean { - return ZoneBorders(swX,swY,neX,neY).insideBorder(entity) + return ZoneBorders(swX, swY, neX, neY).insideBorder(entity) } /** * AHeals the given entity for the given number of hitpoints */ -fun heal(entity: Entity, amount: Int){ +fun heal(entity: Entity, amount: Int) { entity.skills.heal(amount) } @@ -787,9 +806,9 @@ fun heal(entity: Entity, amount: Int){ * @param offset the offset of the desired varbit inside the varp. * @param value the value to set the varbit to */ -fun setVarbit(player: Player, varpIndex: Int, offset: Int, value: Int, save: Boolean = false){ - player.varpManager.get(varpIndex).setVarbit(offset,value).send(player) - if(save) player.varpManager.flagSave(varpIndex) +fun setVarbit(player: Player, varpIndex: Int, offset: Int, value: Int, save: Boolean = false) { + player.varpManager.get(varpIndex).setVarbit(offset, value).send(player) + if (save) player.varpManager.flagSave(varpIndex) } /** @@ -797,7 +816,7 @@ fun setVarbit(player: Player, varpIndex: Int, offset: Int, value: Int, save: Boo * @param player the player to clear for * @param varpIndex the index of the varp to clear */ -fun clearVarp(player: Player, varpIndex: Int){ +fun clearVarp(player: Player, varpIndex: Int) { player.varpManager.get(varpIndex).clear() } @@ -807,7 +826,7 @@ fun clearVarp(player: Player, varpIndex: Int){ * @param varpIndex the index of the varp to calculate the value of * @return the value of the varp */ -fun getVarpValue(player: Player, varpIndex: Int): Int{ +fun getVarpValue(player: Player, varpIndex: Int): Int { return player.varpManager.get(varpIndex).getValue() } @@ -828,12 +847,12 @@ fun getVarbitValue(player: Player, varpIndex: Int, offset: Int): Int { * @param dest the Location object to walk to * @param type the type of pathfinder to use. "smart" for the SMART pathfinder, "clip" for the noclip pathfinder, anything else for DUMB. */ -fun forceWalk(entity: Entity, dest: Location, type: String){ - if(type == "clip"){ +fun forceWalk(entity: Entity, dest: Location, type: String) { + if (type == "clip") { ForceMovement(entity, dest, 10, 10).run() return } - val pathfinder = when(type){ + val pathfinder = when (type) { "smart" -> Pathfinder.SMART else -> Pathfinder.DUMB } @@ -845,7 +864,7 @@ fun forceWalk(entity: Entity, dest: Location, type: String){ * Interrupts a given entity's walking queue * @param entity the entity to interrupt */ -fun stopWalk(entity: Entity){ +fun stopWalk(entity: Entity) { entity.walkingQueue.reset() } @@ -871,8 +890,8 @@ fun getChildren(scenery: Int): IntArray { * @param node the node to adjust the charge of * @param amount the amount to adjust by */ -fun adjustCharge(node: Node, amount: Int){ - when(node){ +fun adjustCharge(node: Node, amount: Int) { + when (node) { is Item -> node.charge += amount is Scenery -> node.charge += amount else -> SystemLogger.logErr("Attempt to adjust the charge of invalid type: ${node.javaClass.simpleName}") @@ -884,11 +903,12 @@ fun adjustCharge(node: Node, amount: Int){ * @param node the node whose charge to check * @return amount of charges the node has, or -1 if the node does not accept charges. */ -fun getCharge(node: Node): Int{ - when(node){ +fun getCharge(node: Node): Int { + when (node) { is Item -> return node.charge is Scenery -> return node.charge - else -> SystemLogger.logErr("Attempt to get charge of invalid type: ${node.javaClass.simpleName}").also { return -1 } + else -> SystemLogger.logErr("Attempt to get charge of invalid type: ${node.javaClass.simpleName}") + .also { return -1 } } } @@ -897,8 +917,8 @@ fun getCharge(node: Node): Int{ * @param node the node to set the charge for * @param charge the amount to set the node's charge to (default is 1000) */ -fun setCharge(node: Node, charge: Int){ - when(node){ +fun setCharge(node: Node, charge: Int) { + when (node) { is Item -> node.charge = charge is Scenery -> node.charge = charge else -> SystemLogger.logErr("Attempt to set the charge of invalid type: ${node.javaClass.simpleName}") @@ -911,7 +931,7 @@ fun setCharge(node: Node, charge: Int){ * @return the option the player used */ fun getUsedOption(player: Player): String { - return player.getAttribute("interact:option","INVALID") + return player.getAttribute("interact:option", "INVALID") } /** @@ -920,39 +940,56 @@ fun getUsedOption(player: Player): String { * @param anim the Animation object to use, can also be an ID. * @param gfx the Graphics object to use, can also be an ID. */ -fun visualize(entity: Entity, anim: A, gfx: G){ - val animation = when(anim){ +fun visualize(entity: Entity, anim: A, gfx: G) { + val animation = when (anim) { is Int -> Animation(anim) is Animation -> anim else -> throw IllegalStateException("Invalid parameter passed for animation.") } - val graphics = when(gfx){ + val graphics = when (gfx) { is Int -> Graphics(gfx) is Graphics -> gfx else -> throw IllegalStateException("Invalid parameter passed for graphics.") } - entity.visualize(animation,graphics) + entity.visualize(animation, graphics) } /** * Used to submit a pulse to the GameWorld's Pulser. * @param pulse the Pulse object to submit */ -fun submitWorldPulse(pulse: Pulse){ +fun submitWorldPulse(pulse: Pulse) { GameWorld.Pulser.submit(pulse) } +/** + * Used to submit a pulse to the GameWorld's Pulser, albeit with a cleaner syntax. + * @param task an anonymous function that will be run in the Pulse + * @return the newly submitted Pulse + */ +fun runWorldTask(task: () -> Unit): Pulse { + val pulse = object : Pulse() { + override fun pulse(): Boolean { + task.invoke() + return true + } + } + + submitWorldPulse(pulse) + return pulse +} + /** * Teleports or "instantly moves" an entity to a given Location object. * @param entity the entity to move * @param loc the Location object to move them to * @param type the teleport type to use (defaults to instant). An enum exists as TeleportManager.TeleportType. */ -fun teleport(entity: Entity, loc: Location, type: TeleportManager.TeleportType = TeleportManager.TeleportType.INSTANT){ - if(type == TeleportManager.TeleportType.INSTANT) entity.properties.teleportLocation = loc - else entity.teleporter.send(loc,type) +fun teleport(entity: Entity, loc: Location, type: TeleportManager.TeleportType = TeleportManager.TeleportType.INSTANT) { + if (type == TeleportManager.TeleportType.INSTANT) entity.properties.teleportLocation = loc + else entity.teleporter.send(loc, type) } /** @@ -961,7 +998,7 @@ fun teleport(entity: Entity, loc: Location, type: TeleportManager.TeleportType = * @param skill the Skill to set. A Skills enum exists that can be used. Ex: Skills.STRENGTH * @param level the level to set the skill to */ -fun setTempLevel(entity: Entity, skill: Int, level: Int){ +fun setTempLevel(entity: Entity, skill: Int, level: Int) { entity.skills.setLevel(skill, level) } @@ -991,7 +1028,7 @@ fun getDynLevel(entity: Entity, skill: Int): Int { * @param skill the Skill to adjust. A Skills enum exists that can be used. Ex: Skills.STRENGTH * @param amount the amount to adjust the skill by. Ex-Buff: 5, Ex-Debuff: -5 */ -fun adjustLevel(entity: Entity, skill: Int, amount: Int){ +fun adjustLevel(entity: Entity, skill: Int, amount: Int) { entity.skills.setLevel(skill, entity.skills.getStaticLevel(skill) + amount) } @@ -1001,14 +1038,14 @@ fun adjustLevel(entity: Entity, skill: Int, amount: Int){ * @param item the item to remove. Can be an Item object or an ID. * @param container the Container to remove the item from. An enum exists for this called Container. Ex: Container.BANK */ -fun removeAll(player: Player, item: T, container: Container){ - val it = when(item){ +fun removeAll(player: Player, item: T, container: Container) { + val it = when (item) { is Item -> item.id is Int -> item else -> throw IllegalStateException("Invalid value passed as item") } - when(container){ + when (container) { Container.EQUIPMENT -> player.equipment.remove(Item(it, amountInEquipment(player, it))) Container.BANK -> player.bank.remove(Item(it, amountInBank(player, it))) Container.INVENTORY -> player.inventory.remove(Item(it, amountInInventory(player, it))) @@ -1022,15 +1059,15 @@ fun removeAll(player: Player, item: T, container: Container){ * @param iface the ID of the interface to use * @param child the index of the child to send the string to */ -fun setInterfaceText(player: Player, string: String, iface: Int, child: Int){ - player.packetDispatch.sendString(string,iface,child) +fun setInterfaceText(player: Player, string: String, iface: Int, child: Int) { + player.packetDispatch.sendString(string, iface, child) } /** * Closes any open (non-chat) interfaces for the player * @param player the player to close the interface for */ -fun closeInterface(player: Player){ +fun closeInterface(player: Player) { player.interfaceManager.close() } @@ -1038,7 +1075,7 @@ fun closeInterface(player: Player){ * Closes any opened tab interfaces for the player * @param player the player to close the tab for */ -fun closeTabInterface(player: Player){ +fun closeTabInterface(player: Player) { player.interfaceManager.closeSingleTab() } @@ -1048,7 +1085,7 @@ fun closeTabInterface(player: Player){ * @param msg the message to send. * @param expr the FacialExpression to use. An enum exists for these called FacialExpression. Defaults to FacialExpression.FRIENDLY */ -fun sendPlayerDialogue(player: Player, msg: String, expr: FacialExpression = FacialExpression.FRIENDLY){ +fun sendPlayerDialogue(player: Player, msg: String, expr: FacialExpression = FacialExpression.FRIENDLY) { player.dialogueInterpreter.sendDialogues(player, expr, *splitLines(msg)) } @@ -1058,8 +1095,8 @@ fun sendPlayerDialogue(player: Player, msg: String, expr: FacialExpression = Fac * @param iface the ID of the interface to send it to * @param child the index of the child on the interface to send the model to */ -fun sendPlayerOnInterface(player: Player, iface: Int, child: Int){ - player.packetDispatch.sendPlayerOnInterface(iface,child) +fun sendPlayerOnInterface(player: Player, iface: Int, child: Int) { + player.packetDispatch.sendPlayerOnInterface(iface, child) } /** @@ -1069,7 +1106,7 @@ fun sendPlayerOnInterface(player: Player, iface: Int, child: Int){ * @param msg the message to send. * @param expr the FacialExpression to use. An enum exists for these called FacialExpression. Defaults to FacialExpression.FRIENDLY */ -fun sendNPCDialogue(player: Player, npc: Int, msg: String, expr: FacialExpression = FacialExpression.FRIENDLY){ +fun sendNPCDialogue(player: Player, npc: Int, msg: String, expr: FacialExpression = FacialExpression.FRIENDLY) { player.dialogueInterpreter.sendDialogues(npc, expr, *splitLines(msg)) } @@ -1080,8 +1117,8 @@ fun sendNPCDialogue(player: Player, npc: Int, msg: String, expr: FacialExpressio * @param iface the ID of the interface to send the animation to * @param child the index of the child on the interface to send the model to */ -fun sendAnimationOnInterface(player: Player, anim: Int, iface: Int, child: Int){ - player.packetDispatch.sendAnimationInterface(anim,iface,child) +fun sendAnimationOnInterface(player: Player, anim: Int, iface: Int, child: Int) { + player.packetDispatch.sendAnimationInterface(anim, iface, child) } /** @@ -1089,7 +1126,7 @@ fun sendAnimationOnInterface(player: Player, anim: Int, iface: Int, child: Int){ * @param player the player to register the listener for * @param handler the method to run when the listener is invoked (when the player logs out) */ -fun registerLogoutListener(player: Player, key: String, handler: (p: Player) -> Unit){ +fun registerLogoutListener(player: Player, key: String, handler: (p: Player) -> Unit) { player.logoutListeners[key] = handler } @@ -1098,7 +1135,7 @@ fun registerLogoutListener(player: Player, key: String, handler: (p: Player) -> * @param player the player to remove the logout listner from * @param key the key of the logout listener to remove. */ -fun clearLogoutListener(player: Player, key: String){ +fun clearLogoutListener(player: Player, key: String) { player.logoutListeners.remove(key) } @@ -1110,8 +1147,8 @@ fun clearLogoutListener(player: Player, key: String){ * @param item the ID of the item to send * @param amount the amount of the item to send - defaults to 1 */ -fun sendItemOnInterface(player: Player, iface: Int, child: Int, item: Int, amount: Int = 1){ - player.packetDispatch.sendItemOnInterface(item,amount,iface,child) +fun sendItemOnInterface(player: Player, iface: Int, child: Int, item: Int, amount: Int = 1) { + player.packetDispatch.sendItemOnInterface(item, amount, iface, child) } /** @@ -1120,8 +1157,16 @@ fun sendItemOnInterface(player: Player, iface: Int, child: Int, item: Int, amoun * @param item the ID of the item to show * @param message the text to display */ -fun sendItemDialogue(player: Player, item: Int, message: String){ - player.dialogueInterpreter.sendItemMessage(item, *splitLines(message)) +fun sendItemDialogue(player: Player, item: Any, message: String) { + val dialogueItem = when (item) { + is Item -> item + is Int -> Item(item) + else -> { + throw java.lang.IllegalArgumentException("Expected an Item or an Int, got ${item::class.java.simpleName}.") + } + } + + player.dialogueInterpreter.sendItemMessage(dialogueItem, *splitLines(message)) } /** @@ -1131,7 +1176,7 @@ fun sendItemDialogue(player: Player, item: Int, message: String){ * @param item2 the ID of the second item to show * @param message the text to display */ -fun sendDoubleItemDialogue(player: Player, item1: Int, item2: Int, message: String){ +fun sendDoubleItemDialogue(player: Player, item1: Int, item2: Int, message: String) { player.dialogueInterpreter.sendDoubleItemMessage(item1, item2, message) } @@ -1142,8 +1187,8 @@ fun sendDoubleItemDialogue(player: Player, item1: Int, item2: Int, message: Stri * @param prompt what to prompt the player * @param handler the method that handles the value gained from the input dialogue */ -fun sendInputDialogue(player: Player, numeric: Boolean, prompt: String, handler: (value: Any) -> Unit){ - if(numeric) sendInputDialogue(player, InputType.NUMERIC, prompt, handler) +fun sendInputDialogue(player: Player, numeric: Boolean, prompt: String, handler: (value: Any) -> Unit) { + if (numeric) sendInputDialogue(player, InputType.NUMERIC, prompt, handler) else sendInputDialogue(player, InputType.STRING_SHORT, prompt, handler) } @@ -1154,12 +1199,22 @@ fun sendInputDialogue(player: Player, numeric: Boolean, prompt: String, handler: * @param prompt what to prompt the player * @param handler the method that handles the value from the input dialogue */ -fun sendInputDialogue(player: Player, type: InputType, prompt: String, handler: (value: Any) -> Unit){ - when(type){ - InputType.NUMERIC, InputType.STRING_SHORT -> player.dialogueInterpreter.sendInput(type != InputType.NUMERIC, prompt) +fun sendInputDialogue(player: Player, type: InputType, prompt: String, handler: (value: Any) -> Unit) { + when (type) { + InputType.AMOUNT -> { + player.setAttribute("parseamount", true) + player.dialogueInterpreter.sendInput(true, prompt) + } + + InputType.NUMERIC, InputType.STRING_SHORT -> player.dialogueInterpreter.sendInput( + type != InputType.NUMERIC, + prompt + ) + InputType.STRING_LONG -> player.dialogueInterpreter.sendLongInput(prompt) InputType.MESSAGE -> player.dialogueInterpreter.sendMessageInput(prompt) } + player.setAttribute("runscript", handler) } @@ -1168,14 +1223,14 @@ fun sendInputDialogue(player: Player, type: InputType, prompt: String, handler: * @param entity the entity to make flee * @param from the entity to flee from */ -fun flee(entity: Entity, from: Entity){ +fun flee(entity: Entity, from: Entity) { lock(entity, 5) face(entity, from, 5) val diffX = entity.location.x - from.location.x val diffY = entity.location.y - from.location.y - forceWalk(entity, entity.location.transform(diffX,diffY,0), "DUMB") + forceWalk(entity, entity.location.transform(diffX, diffY, 0), "DUMB") } /** @@ -1183,14 +1238,14 @@ fun flee(entity: Entity, from: Entity){ * @param entity the entity to submit the pulse to * @param pulse the pulse to submit */ -fun submitIndividualPulse(entity: Entity, pulse: Pulse){ +fun submitIndividualPulse(entity: Entity, pulse: Pulse) { entity.pulseManager.run(pulse) } /** * Similar to submitIndividualPulse, but for non-repeating tasks, with a cleaner syntax. */ -fun runTask(entity: Entity, delay: Int = 0, task: () -> Unit){ +fun runTask(entity: Entity, delay: Int = 0, task: () -> Unit) { entity.pulseManager.run(object : Pulse(delay) { override fun pulse(): Boolean { task.invoke() @@ -1254,7 +1309,7 @@ fun finishQuest(player: Player, quest: String) { * @param id the ID of the scenery to get the definition for. * @return the scenery definition */ -fun sceneryDefinition(id: Int): SceneryDefinition{ +fun sceneryDefinition(id: Int): SceneryDefinition { return SceneryDefinition.forId(id) } @@ -1263,7 +1318,7 @@ fun sceneryDefinition(id: Int): SceneryDefinition{ * @param zone the zone to register * @param borders the ZoneBorders that compose the zone */ -fun registerMapZone(zone: MapZone, borders: ZoneBorders){ +fun registerMapZone(zone: MapZone, borders: ZoneBorders) { ZoneBuilder.configure(zone) zone.register(borders) } @@ -1275,8 +1330,8 @@ fun registerMapZone(zone: MapZone, borders: ZoneBorders){ * @param child the child on the interface to animate. * @param anim the ID of the animation to use. */ -fun animateInterface(player: Player, iface: Int, child: Int, anim: Int){ - player.packetDispatch.sendAnimationInterface(anim,iface,child) +fun animateInterface(player: Player, iface: Int, child: Int, anim: Int) { + player.packetDispatch.sendAnimationInterface(anim, iface, child) } /** @@ -1284,15 +1339,15 @@ fun animateInterface(player: Player, iface: Int, child: Int, anim: Int){ * @param ladderLoc the location of the ladder/stairs object you want to climb. * @param dest the destination for the climb. */ -fun addClimbDest(ladderLoc: Location, dest: Location){ - SpecialLadders.add(ladderLoc,dest) +fun addClimbDest(ladderLoc: Location, dest: Location) { + SpecialLadders.add(ladderLoc, dest) } /** * Sends a news announcement in game chat. * @param message the message to announce */ -fun sendNews(message: String){ +fun sendNews(message: String) { Repository.sendNews(message, 12, "CC6600") } @@ -1301,9 +1356,9 @@ fun sendNews(message: String){ * @param gfx the Graphics object, or the Integer ID of the graphics, to send. Either works. * @param location the location to send it to */ -fun sendGraphics(gfx: G, location: Location){ - when(gfx){ - is Int -> Graphics.send(Graphics(gfx),location) +fun sendGraphics(gfx: G, location: Location) { + when (gfx) { + is Int -> Graphics.send(Graphics(gfx), location) is Graphics -> Graphics.send(gfx, location) } } @@ -1325,12 +1380,12 @@ fun getMasteredSkillNames(player: Player): List { val hasMastered = player.getSkills().masteredSkills > 0 val masteredSkills = ArrayList() - if(hasMastered){ + if (hasMastered) { for ((skillId, skillName) in Skills.SKILL_NAME.withIndex()) { //phil you were looping every skill and performing a string comparison (another loop) for every one here. //that's some real shit code bro. //you were also performing a dynamic level check rather than a static. - if(hasLevelStat(player, skillId, 99)){ + if (hasLevelStat(player, skillId, 99)) { masteredSkills.add(skillName) } } @@ -1339,35 +1394,137 @@ fun getMasteredSkillNames(player: Player): List { } /** - * Dumps the given player's given container into that player's bank. + * Empties the provided container into the player's bank. * @param player the player - * @param container the player's container to dump. + * @param container the container to be emptied. + * @return The amount of items which were successfully transferred to the bank. + * * @author ceik * @author James Triantafylos + * @author vddCore */ -fun dumpContainer(player: Player, container: core.game.container.Container) { +fun dumpContainer(player: Player, container: core.game.container.Container): Int { val bank = player.bank - container.toArray().filterNotNull().forEach { item -> - if (!bank.hasSpaceFor(item)) { - player.packetDispatch.sendMessage("You have no more space in your bank.") - return - } - if (!bank.canAdd(item)) { - player.packetDispatch.sendMessage("A magical force prevents you from banking your " + item.name + ".") - return - } else { - if (container is EquipmentContainer) { - if(!InteractionListeners.run(item.id,player,item,false)) { - player.packetDispatch.sendMessage("A magical force prevents you from removing that item.") - return - } + var dumpedCount = 0 + + run beginDepositing@{ + container.toArray().filterNotNull().forEach { item -> + if (!bank.hasSpaceFor(item)) { + sendMessage(player, "You have no more space in your bank.") + return@beginDepositing + } + + if (!bank.canAdd(item)) { + sendMessage(player, "A magical force prevents you from banking the ${item.name}.") + return@forEach + } else { + if (container is EquipmentContainer) { + if (!InteractionListeners.run(item.id, player, item, false)) { + sendMessage(player, "A magical force prevents you from removing your ${item.name}.") + return@forEach + } + } + + container.remove(item) + bank.add(unnote(item)) + dumpedCount++ } - container.remove(item) - bank.add(if (item.definition.isUnnoted) item else Item(item.noteChange, item.amount)) } } + container.update() bank.update() + + return dumpedCount +} + +/** + * Attempts to empty the player's Beast of Burden's inventory + * into the player's bank. + * + * @param player The player whose Beast of Burden's inventory to empty. + * + * @author vddCore + */ +fun dumpBeastOfBurden(player: Player) { + val famMan = player.familiarManager + + if (!famMan.hasFamiliar()) { + sendMessage(player, "You don't have a familiar.") + return + } + + if (famMan.familiar !is BurdenBeast) { + sendMessage(player, "Your familiar is not a Beast of Burden.") + return + } + + val beast: BurdenBeast = (famMan.familiar as BurdenBeast) + + if (beast.container.isEmpty) { + sendMessage(player, "Your familiar's inventory is empty.") + return + } + + val itemCount = beast.container.itemCount() + val dumpedCount = dumpContainer(player, beast.container) + + + when { + dumpedCount == itemCount -> sendMessage(player, "Your familiar's inventory was deposited into your bank.") + dumpedCount > 0 -> { + val remainPhrase = when { + (itemCount - dumpedCount == 1) -> "item remains" + else -> "items remain" + } + + sendMessage(player, "${itemCount - dumpedCount} $remainPhrase in your familiar's inventory.") + } + } +} + +/** + * Converts an item into its noted representation. + * + * @param item The item to convert. + * @return Noted form of the item or the item itself if it's already, or cannot be, noted. + * + * @author vddCore + */ +fun note(item: Item): Item { + if (!item.definition.isUnnoted) + return item + + if (item.definition.noteId < 0) + return item + + return Item(item.definition.noteId, item.amount, item.charge) +} + +/** + * Converts a noted item into its unnoted representation. + * + * @param item The item to convert. + * @return Unnoted form of the item, or the item itself if it's already unnoted. + * + * @author vddCore + */ +fun unnote(item: Item): Item { + if (item.definition.isUnnoted) + return item + + return Item(item.noteChange, item.amount, item.charge) +} + +/** + * Checks if the player has the Seal of Passage equipped or if it's present in the inventory. + * + * @param player The player to inspect for item's presence. + * @return True if Seal of Passage present, false otherwise. + */ +fun hasSealOfPassage(player: Player): Boolean { + return isEquipped(player, Items.SEAL_OF_PASSAGE_9083) + || inInventory(player, Items.SEAL_OF_PASSAGE_9083) } /** @@ -1387,14 +1544,13 @@ fun Player.getCutsceneStage(): Int { return getAttribute(this, Cutscene.ATTRIBUTE_CUTSCENE_STAGE, 0) } -fun getServerConfig() : Toml -{ +fun getServerConfig(): Toml { return ServerConfigParser.tomlData ?: Toml() } fun getPathableRandomLocalCoordinate(target: Entity, radius: Int, center: Location, maxAttempts: Int = 3): Location { - val swCorner = center.transform(-radius,-radius, center.z) - val neCorner = center.transform(radius, radius, center.z) + val swCorner = center.transform(-radius, -radius, center.z) + val neCorner = center.transform(radius, radius, center.z) val borders = ZoneBorders(swCorner.x, swCorner.y, neCorner.x, neCorner.y, center.z) var attempts = maxAttempts diff --git a/Server/src/main/kotlin/api/InputType.kt b/Server/src/main/kotlin/api/InputType.kt index 139060cfc..73810bce5 100644 --- a/Server/src/main/kotlin/api/InputType.kt +++ b/Server/src/main/kotlin/api/InputType.kt @@ -1,6 +1,7 @@ package api enum class InputType { + AMOUNT, NUMERIC, STRING_SHORT, STRING_LONG, diff --git a/Server/src/main/kotlin/api/events/Events.kt b/Server/src/main/kotlin/api/events/Events.kt index 653bba333..3d8d90c1e 100644 --- a/Server/src/main/kotlin/api/events/Events.kt +++ b/Server/src/main/kotlin/api/events/Events.kt @@ -1,5 +1,6 @@ package api.events +import core.game.component.Component import core.game.node.Node import core.game.node.entity.Entity import core.game.node.entity.npc.NPC @@ -13,4 +14,6 @@ data class InteractionEvent(val target: Node, val option: String) : Event data class ButtonClickedEvent(val iface: Int, val buttonId: Int) : Event data class UsedWithEvent(val used: Int, val with: Int) : Event data class SelfDeath(val killer: Entity) : Event -data class TickEvent(val worldTicks: Int) : Event \ No newline at end of file +data class TickEvent(val worldTicks: Int) : Event +data class InterfaceOpenEvent(val component: Component) : Event +data class InterfaceCloseEvent(val component: Component) : Event \ No newline at end of file diff --git a/Server/src/main/kotlin/core/net/packet/in/RunScriptPacketHandler.kt b/Server/src/main/kotlin/core/net/packet/in/RunScriptPacketHandler.kt new file mode 100644 index 000000000..0c6363dd8 --- /dev/null +++ b/Server/src/main/kotlin/core/net/packet/in/RunScriptPacketHandler.kt @@ -0,0 +1,56 @@ +package core.net.packet.`in` + +import api.sendDialogue +import core.game.node.entity.player.Player +import core.net.packet.IncomingPacket +import core.net.packet.IoBuffer +import core.tools.StringUtils + +/** + * Handles an incoming script execution request packet. + * + * @author vddCore + */ +class RunScriptPacketHandler : IncomingPacket { + + private fun processInput(player: Player, value: Any, script: ((Any) -> Boolean)) { + if (value is Int && value <= 0) return + + var input = value + + if (player.getAttribute("parseamount", false)) { + input = value.toString().lowercase() + + if (!input.matches(Regex("^(\\d+)(k+|m+)?$"))) { + sendDialogue(player, "That doesn't look right. Please try again.") + return + } + + input = input.replace("k", "000").replace("m", "000000") + } + + try { + script(input) + } catch (_: NumberFormatException) { + sendDialogue(player, "That number's a bit large, don't you think?") + } + } + + override fun decode(player: Player, opcode: Int, buffer: IoBuffer) { + val script: ((Any) -> Boolean)? = player.getAttribute("runscript", null) + + if (script == null || player.locks.isInteractionLocked) + return + + val value: Any = when(opcode) { + 244 -> StringUtils.longToString(buffer.long) + 65 -> buffer.string + else -> buffer.int // Handles OpCode 23 and other cases. + } + + processInput(player, value, script) + + player.removeAttribute("parseamount") + player.removeAttribute("runscript") + } +} diff --git a/Server/src/main/kotlin/rs09/ServerConstants.kt b/Server/src/main/kotlin/rs09/ServerConstants.kt index f24f6e744..165c9f8f3 100644 --- a/Server/src/main/kotlin/rs09/ServerConstants.kt +++ b/Server/src/main/kotlin/rs09/ServerConstants.kt @@ -110,6 +110,9 @@ class ServerConstants { @JvmField var BANK_SIZE: Int = 496 + @JvmField + var BANK_BOOTH_QUICK_OPEN: Boolean = false + @JvmField var GE_AUTOSAVE_FREQUENCY = secondsToTicks(3600) //1 hour diff --git a/Server/src/main/kotlin/rs09/game/Event.kt b/Server/src/main/kotlin/rs09/game/Event.kt index 98cd2d58c..00fedfc41 100644 --- a/Server/src/main/kotlin/rs09/game/Event.kt +++ b/Server/src/main/kotlin/rs09/game/Event.kt @@ -12,4 +12,6 @@ object Event { @JvmStatic val UsedWith = UsedWithEvent::class.java @JvmStatic val SelfDeath = SelfDeath::class.java @JvmStatic val Tick = TickEvent::class.java + @JvmStatic val InterfaceOpened = InterfaceOpenEvent::class.java + @JvmStatic val InterfaceClosed = InterfaceCloseEvent::class.java } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/BankDepositDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/BankDepositDialogue.kt new file mode 100644 index 000000000..3cef22391 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/BankDepositDialogue.kt @@ -0,0 +1,57 @@ +package rs09.game.content.dialogue + +import api.* +import core.game.content.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import rs09.tools.START_DIALOGUE + +/** + * Represents the dialogue shown when the user presses + * "Deposit Beast of Burden" button on the Bank Interface. + * + * @author vddCore + */ +class BankDepositDialogue : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> options( + "Deposit Inventory", + "Deposit Worn Equipment", + "Deposit Beast of Burden", + "Cancel" + ).also { stage++ } + + 1 -> when (buttonID) { + 1 -> player?.let { + end() + + if (it.inventory.isEmpty) { + sendMessage(it, "You have nothing in your inventory that you can deposit.") + } else { + dumpContainer(it, it.inventory) + } + } + + 2 -> player?.let { + end() + + if (it.equipment.isEmpty) { + sendMessage(it, "You have no equipment that you can deposit.") + } else { + dumpContainer(it, it.equipment) + } + } + + 3 -> player?.let { + end() + dumpBeastOfBurden(it) + } + + 4 -> end() + } + } + + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/BankHelpDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/BankHelpDialogue.kt new file mode 100644 index 000000000..88fdd14d0 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/BankHelpDialogue.kt @@ -0,0 +1,57 @@ +package rs09.game.content.dialogue + +import api.* +import core.game.content.dialogue.DialogueInterpreter +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.Item +import org.rs09.consts.Components +import org.rs09.consts.Items +import rs09.tools.START_DIALOGUE + +/** + * Represents the dialogue shown when the user presses + * the "?" button on the Bank Interface. + * + * @author vddCore + */ +class BankHelpDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> options( + "Check Bank Value", + "Banking Assistance", + "Close" + ).also { stage++ } + + 1 -> when (buttonID) { + 1 -> player?.let { + end() + + val wealth = it.bank.wealth + + if (wealth > 0) { + val word = if (wealth != 1) "coins" else "coin" + + sendItemDialogue( + it, + Item(Items.COINS_995, wealth), + "
Your bank is worth ${wealth} ${word}." + ) + } else { + sendDialogue(it, "You have no valuables in your bank.") + } + } + + 2 -> player?.let { + end() + + it.bank.close() + openInterface(it, Components.BANK_V2_HELP_767) + } + + 3 -> end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/DepositAllDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/DepositAllDialogue.kt deleted file mode 100644 index a62cb2c10..000000000 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/DepositAllDialogue.kt +++ /dev/null @@ -1,55 +0,0 @@ -package rs09.game.content.dialogue - -import api.* -import core.game.content.dialogue.DialoguePlugin -import core.game.node.entity.player.Player -import core.plugin.Initializable - -@Initializable -/** - * Represents the dialogue plugin used to handle the deposit all/deposit inventory/etc button - * @author Splinter - * @author James Triantafylos - */ -class DepositAllDialogue(player: Player? = null) : DialoguePlugin(player) { - val ID = 628371 - - override fun newInstance(player: Player?): DialoguePlugin { - return DepositAllDialogue(player) - } - - override fun open(vararg args: Any?): Boolean { - options("Deposit Inventory", "Deposit Equipment", "Deposit Beast of Burden", "Cancel") - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when (buttonId) { - 1 -> { - if (player.inventory.isEmpty) { - player.packetDispatch.sendMessage("You have nothing in your inventory to deposit.") - } else { - dumpContainer(player, player.inventory) - } - } - 2 -> { - if (player.equipment.isEmpty) { - player.packetDispatch.sendMessage("You have no equipment to deposit.") - } else { - dumpContainer(player, player.equipment) - } - } - 3 -> { - player.familiarManager.dumpBob() - } - 4 -> { - } - } - end() - return true - } - - override fun getIds(): IntArray { - return intArrayOf(ID) - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt index 59fba6858..07ad27411 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/DialogueFile.kt @@ -18,10 +18,12 @@ abstract class DialogueFile { var dialoguePlugin: DialoguePlugin? = null abstract fun handle(componentID: Int, buttonID: Int) + fun load(player: Player, npc: NPC?, interpreter: DialogueInterpreter): DialogueFile{ this.player = player this.npc = npc this.interpreter = interpreter + return this } diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/lunarisle/SirsalBankerDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/lunarisle/SirsalBankerDialogue.kt index 2c5c15d13..6911657cd 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/lunarisle/SirsalBankerDialogue.kt +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/lunarisle/SirsalBankerDialogue.kt @@ -1,25 +1,24 @@ package rs09.game.content.dialogue.region.lunarisle +import api.hasSealOfPassage import core.game.content.dialogue.DialoguePlugin import core.game.content.dialogue.FacialExpression import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable -import org.rs09.consts.Items import org.rs09.consts.NPCs import rs09.game.ge.GrandExchangeRecords /** * @author qmqz */ - @Initializable class SirsalBankerDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (player.inventory.contains(Items.SEAL_OF_PASSAGE_9083, 1) || player.equipment.contains(Items.SEAL_OF_PASSAGE_9083, 1)) { + if (hasSealOfPassage(player)) { npc(FacialExpression.FRIENDLY, "Good day, how may I help you?").also { stage = 0 } } else { player(FacialExpression.FRIENDLY, "Hi, I...").also { stage = 2 } diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/PeerTheSeerDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/PeerTheSeerDialogue.kt index c5c7b3cb2..ea5a10281 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/PeerTheSeerDialogue.kt +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/PeerTheSeerDialogue.kt @@ -239,7 +239,7 @@ class PeerTheSeerDialogue(player: Player? = null) : DialoguePlugin(player) { 3 -> playerl(FacialExpression.HAPPY,"Nothing really, I just stopped by to say hello").also { stage = 160 } } 202 -> npcl(FacialExpression.HAPPY,"Of course, ${player.getAttribute("fremennikname","dingle")}. I am always happy to aid those who have earned the right to wear Fremennik sea boots.").also { - BankingPlugin.BankDepositInterface() + player.bank.openDepositBox() stage = 1000 } diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/BankDepositBoxInterface.kt b/Server/src/main/kotlin/rs09/game/interaction/inter/BankDepositBoxInterface.kt new file mode 100644 index 000000000..008e6aa2a --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/inter/BankDepositBoxInterface.kt @@ -0,0 +1,69 @@ +package rs09.game.interaction.inter + +import api.* +import api.sendMessage +import core.game.component.Component +import core.game.node.entity.player.Player +import core.game.node.entity.skill.summoning.familiar.FamiliarManager +import org.rs09.consts.Animations +import org.rs09.consts.Components +import rs09.game.interaction.InterfaceListener +import rs09.game.interaction.util.BankUtils + +private const val BUTTON_DEPOSIT_BOB = 13 + +private const val MENU_ELEMENT = 11 +private const val OP_AMOUNT_ONE = 155 +private const val OP_AMOUNT_FIVE = 196 +private const val OP_AMOUNT_TEN = 124 +private const val OP_AMOUNT_ALL = 199 +private const val OP_AMOUNT_X = 234 +private const val OP_EXAMINE = 168 + +/** + * Allows the user to interact with the + * Bank Deposit Box interface + * + * @author vddCore + */ +class BankDepositBoxInterface : InterfaceListener { + + private fun handleDepositBoxMenu(player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int): Boolean { + val item = player.inventory.get(slot) + ?: return true + + if (opcode == OP_EXAMINE) { + sendMessage(player, item.definition.examine) + return true + } + + runWorldTask { + when (opcode) { + OP_AMOUNT_ONE -> player.bank.addItem(slot, 1) + OP_AMOUNT_FIVE -> player.bank.addItem(slot, 5) + OP_AMOUNT_TEN -> player.bank.addItem(slot, 10) + OP_AMOUNT_X -> BankUtils.transferX( + player, + slot, + false, + // Needs to have a callback here because the depositing moment is independent from the world task. + player.bank::refreshDepositBoxInterface + ) + OP_AMOUNT_ALL -> player.bank.addItem(slot, player.inventory.getAmount(item)) + else -> player.debug("Unknown deposit box menu opcode $opcode") + } + + player.bank.refreshDepositBoxInterface() + animate(player, Animations.HUMAN_BANK_DEPOSIT_BOX_834) + } + + return true + } + + override fun defineInterfaceListeners() { + on(Components.BANK_DEPOSIT_BOX_11, ::handleDepositBoxMenu) + on(Components.BANK_DEPOSIT_BOX_11, BUTTON_DEPOSIT_BOB) { player, _, _, _, _, _ -> + dumpBeastOfBurden(player); true + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/BankInterface.kt b/Server/src/main/kotlin/rs09/game/interaction/inter/BankInterface.kt new file mode 100644 index 000000000..2b8048ef2 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/inter/BankInterface.kt @@ -0,0 +1,163 @@ +package rs09.game.interaction.inter + +import api.* +import core.game.component.Component +import core.game.node.entity.player.Player +import org.rs09.consts.Components +import org.rs09.consts.Items +import rs09.ServerConstants +import rs09.game.content.dialogue.BankHelpDialogue +import rs09.game.content.dialogue.BankDepositDialogue +import rs09.game.interaction.InterfaceListener +import rs09.game.interaction.util.BankUtils + +private const val MAIN_BUTTON_CLOSE = 10 +private const val MAIN_BUTTON_INSERT_MODE = 14 +private const val MAIN_BUTTON_NOTE_MODE = 16 +private const val MAIN_BUTTON_BOB_DEPOSIT = 18 +private const val MAIN_BUTTON_SEARCH_BANK = 20 +private const val MAIN_BUTTON_HELP = 23 + +private const val MENU_ELEMENT = 73 +private const val OP_AMOUNT_ONE = 155 +private const val OP_AMOUNT_FIVE = 196 +private const val OP_AMOUNT_TEN = 124 +private const val OP_AMOUNT_LAST_X = 199 +private const val OP_AMOUNT_X = 234 +private const val OP_AMOUNT_ALL = 168 +private const val OP_AMOUNT_ALL_BUT_ONE = 166 +private const val OP_EXAMINE = 9 + +private const val BANK_TAB_1 = 41 +private const val BANK_TAB_2 = 39 +private const val BANK_TAB_3 = 37 +private const val BANK_TAB_4 = 35 +private const val BANK_TAB_5 = 33 +private const val BANK_TAB_6 = 31 +private const val BANK_TAB_7 = 29 +private const val BANK_TAB_8 = 27 +private const val BANK_TAB_9 = 25 + +private val BANK_TABS = intArrayOf( + BANK_TAB_1, BANK_TAB_2, BANK_TAB_3, + BANK_TAB_4, BANK_TAB_5, BANK_TAB_6, + BANK_TAB_7, BANK_TAB_8, BANK_TAB_9 +) + +private const val OP_SET_TAB = 155 +private const val OP_COLLAPSE_TAB = 196 + +/** + * Allows the user to interact with the Bank Interface. + * + * @author vddCore + */ +class BankInterface : InterfaceListener { + + private fun onBankInterfaceOpen(player: Player, component: Component): Boolean { + player.bank.sendBankSpace(); + + val settings = IfaceSettingsBuilder() + .enableAllOptions() + .enableSlotSwitch() + .setInterfaceEventsDepth(2) + .build() + + player.packetDispatch.sendIfaceSettings( + settings, + 73, + Components.BANK_V2_MAIN_762, + 0, + ServerConstants.BANK_SIZE + ) + + return false + } + + private fun handleTabInteraction(player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int): Boolean { + if (getAttribute(player, "search", false)) { + player.bank.reopen() + } + + val clickedTabIndex = -((buttonID - 41) / 2) + + when (opcode) { + OP_SET_TAB -> { + if (player.bank.tabIndex != clickedTabIndex) { + player.bank.tabIndex = clickedTabIndex + } + } + + OP_COLLAPSE_TAB -> { + player.bank.collapseTab(clickedTabIndex) + } + } + + return true + } + + private fun handleBankMenu(player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int): Boolean { + val item = player.bank.get(slot) + ?: return true + + when (opcode) { + OP_AMOUNT_ONE -> runWorldTask { player.bank.takeItem(slot, 1) } + OP_AMOUNT_FIVE -> runWorldTask { player.bank.takeItem(slot, 5) } + OP_AMOUNT_TEN -> runWorldTask { player.bank.takeItem(slot, 10) } + OP_AMOUNT_LAST_X -> runWorldTask { player.bank.takeItem(slot, player.bank.lastAmountX) } + OP_AMOUNT_X -> runWorldTask { BankUtils.transferX(player, slot, true) } + OP_AMOUNT_ALL -> runWorldTask { player.bank.takeItem(slot, player.bank.getAmount(item)) } + OP_AMOUNT_ALL_BUT_ONE -> runWorldTask { player.bank.takeItem(slot, player.bank.getAmount(item) - 1) } + OP_EXAMINE -> sendMessage(player, item.definition.examine) + else -> player.debug("Unknown bank menu opcode $opcode") + } + + return true + } + + private fun handleInventoryMenu(player: Player, component: Component, opcode: Int, buttonID: Int, slot: Int, itemID: Int): Boolean { + val item = player.inventory.get(slot) + ?: return true + + when (opcode) { + OP_AMOUNT_ONE -> player.bank.addItem(slot, 1) + OP_AMOUNT_FIVE -> player.bank.addItem(slot, 5) + OP_AMOUNT_TEN -> player.bank.addItem(slot, 10) + OP_AMOUNT_LAST_X -> player.bank.addItem(slot, player.bank.lastAmountX) + OP_AMOUNT_X -> BankUtils.transferX(player, slot, false) + OP_AMOUNT_ALL -> player.bank.addItem(slot, player.inventory.getAmount(item)) + OP_EXAMINE -> sendMessage(player, item.definition.examine) + else -> player.debug("Unknown inventory menu opcode $opcode") + } + + return true + } + + override fun defineInterfaceListeners() { + onOpen(Components.BANK_V2_MAIN_762, ::onBankInterfaceOpen) + + on(Components.BANK_V2_MAIN_762) { player, component, opcode, buttonID, slot, itemID -> + when (buttonID) { + MAIN_BUTTON_HELP -> openDialogue(player, BankHelpDialogue()) + MAIN_BUTTON_BOB_DEPOSIT -> openDialogue(player, BankDepositDialogue()) + MAIN_BUTTON_INSERT_MODE -> player.bank.isInsertItems = !player.bank.isInsertItems + MAIN_BUTTON_NOTE_MODE -> player.bank.isNoteItems = !player.bank.isNoteItems + MAIN_BUTTON_SEARCH_BANK -> setAttribute(player, "search", true) + MENU_ELEMENT -> handleBankMenu(player, component, opcode, buttonID, slot, itemID) + in BANK_TABS -> handleTabInteraction(player, component, opcode, buttonID, slot, itemID) + } + + return@on true + } + + on(Components.BANK_V2_HELP_767) { player, component, opcode, buttonID, slot, itemID -> + when (buttonID) { + MAIN_BUTTON_CLOSE -> player.bank.open() + } + + return@on true + } + + on(Components.BANK_V2_SIDE_763, ::handleInventoryMenu) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/npc/BankerNPCListener.kt b/Server/src/main/kotlin/rs09/game/interaction/npc/BankerNPCListener.kt new file mode 100644 index 000000000..dd9090e20 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/npc/BankerNPCListener.kt @@ -0,0 +1,109 @@ +package rs09.game.interaction.npc + +import api.getScenery +import api.location +import api.openDialogue +import core.game.node.Node +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.map.path.Pathfinder +import org.rs09.consts.NPCs +import rs09.game.content.dialogue.region.lunarisle.SirsalBankerDialogue +import rs09.game.ge.GrandExchangeRecords +import rs09.game.interaction.InteractionListener +import rs09.game.interaction.`object`.BankBoothHandler +import rs09.game.node.entity.npc.other.BankerNPC +import rs09.game.system.SystemLogger + +/** + * Allows the user to interact with banker NPC options. + * + * @author vddCore + */ +class BankerNPCListener : InteractionListener { + companion object { + val BANKER_NPCS = intArrayOf( + NPCs.BANKER_44, NPCs.BANKER_45, NPCs.BANKER_494, NPCs.BANKER_495, NPCs.BANKER_496, NPCs.BANKER_497, + NPCs.BANKER_498, NPCs.BANKER_499, NPCs.BANKER_1036, NPCs.BANKER_1360, NPCs.BANKER_2163, NPCs.BANKER_2164, + NPCs.BANKER_2354, NPCs.BANKER_2355, NPCs.BANKER_2568, NPCs.BANKER_2569, NPCs.BANKER_2570, NPCs.BANKER_3198, + NPCs.BANKER_3199, NPCs.BANKER_5258, NPCs.BANKER_5259, NPCs.BANKER_5260, NPCs.BANKER_5261, NPCs.BANKER_5776, + NPCs.BANKER_5777, NPCs.BANKER_5912, NPCs.BANKER_5913, NPCs.BANKER_6200, NPCs.BANKER_6532, NPCs.BANKER_6533, + NPCs.BANKER_6534, NPCs.BANKER_6535, NPCs.BANKER_6538, NPCs.BANKER_7445, NPCs.BANKER_7446, NPCs.BANKER_7605, + + NPCs.BANK_TUTOR_4907, + + NPCs.GHOST_BANKER_1702, NPCs.GNOME_BANKER_166, NPCs.NARDAH_BANKER_3046, + NPCs.OGRESS_BANKER_7049, NPCs.OGRESS_BANKER_7050, NPCs.SIRSAL_BANKER_4519, + + NPCs.FADLI_958, NPCs.ARNOLD_LYDSPOR_3824, NPCs.MAGNUS_GRAM_5488 + ) + + fun provideDestinationOverride(entity: Entity, node: Node): Location { + val npc = node as NPC + + return when(npc.id) { + /* Ogress bankers are 2x2 with their spawn being offset to south-western tile. */ + NPCs.OGRESS_BANKER_7049, + NPCs.OGRESS_BANKER_7050 -> npc.location.transform(3, 1, 0) + + /* Magnus has no bank booth nearby so we need to handle that edge case here. */ + NPCs.MAGNUS_GRAM_5488 -> npc.location.transform(Direction.NORTH, 2) + + else -> { + if (npc is BankerNPC) { + npc.findAdjacentBankBoothLocation()?.let { + return it.second + } + } + + val path = Pathfinder.find(entity, node) + + if (path.isSuccessful) { + val pt = path.points.last + return Location(pt.x, pt.y) + } + + return npc.location + } + } + } + } + + override fun defineListeners() { + on(BANKER_NPCS, NPC, "bank") { player, node -> + val npc = node as NPC + + if (BankerNPC.checkLunarIsleRestriction(player, node)) { + openDialogue(player, npc.id, npc) + return@on true + } + + npc.faceLocation(null) + player.bank.open(); true + } + + on(BANKER_NPCS, NPC, "collect") { player, node -> + val npc = node as NPC + + if (BankerNPC.checkLunarIsleRestriction(player, node)) { + openDialogue(player, npc.id, npc) + return@on true + } + + npc.faceLocation(null) + GrandExchangeRecords.getInstance(player).openCollectionBox(); true + } + + on(BANKER_NPCS, NPC, "talk-to") { player, node -> + val npc = node as NPC + openDialogue(player, npc.id, npc); true + } + } + + override fun defineDestinationOverrides() { + setDest(NPC, BANKER_NPCS, "bank", "collect", "talk-to", handler = ::provideDestinationOverride) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt b/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt index 59beda1b9..af9e31aa0 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/npc/NPCTalkListener.kt @@ -7,7 +7,6 @@ import rs09.game.content.activity.gnomecooking.* import rs09.game.content.ame.RandomEventManager import rs09.game.content.ame.RandomEvents import rs09.game.interaction.InteractionListener -import rs09.game.system.SystemLogger /** * Handles the NPC talk-to option. @@ -66,5 +65,7 @@ class NPCTalkListener : InteractionListener { } return@setDest node.location } + + setDest(NPC, BankerNPCListener.BANKER_NPCS, "talk-to", handler = BankerNPCListener::provideDestinationOverride) } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/BankBoothHandler.kt b/Server/src/main/kotlin/rs09/game/interaction/object/BankBoothHandler.kt new file mode 100644 index 000000000..9f1e7819e --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/object/BankBoothHandler.kt @@ -0,0 +1,135 @@ +package rs09.game.interaction.`object` + +import api.openDialogue +import core.game.content.dialogue.DialogueInterpreter +import core.game.node.Node +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.IronmanMode +import core.game.world.map.Direction +import core.game.world.map.Location +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import rs09.ServerConstants +import rs09.game.ge.GrandExchangeRecords +import rs09.game.interaction.InteractionListener +import rs09.game.node.entity.npc.other.BankerNPC +import rs09.game.world.repository.Repository + +/** + * Allows the user to interact with bank booths. + * + * @author vddCore + */ +class BankBoothHandler : InteractionListener { + + companion object { + val BANK_BOOTHS = intArrayOf( + Scenery.BANK_BOOTH_2213, Scenery.BANK_BOOTH_2214, Scenery.BANK_BOOTH_3045, Scenery.BANK_BOOTH_5276, + Scenery.BANK_BOOTH_6084, Scenery.BANK_BOOTH_10517, Scenery.BANK_BOOTH_11338, Scenery.BANK_BOOTH_11402, + Scenery.BANK_BOOTH_11758, Scenery.BANK_BOOTH_12798, Scenery.BANK_BOOTH_12799, Scenery.BANK_BOOTH_12800, + Scenery.BANK_BOOTH_12801, Scenery.BANK_BOOTH_14367, Scenery.BANK_BOOTH_14368, Scenery.BANK_BOOTH_16700, + Scenery.BANK_BOOTH_18491, Scenery.BANK_BOOTH_19230, Scenery.BANK_BOOTH_20325, Scenery.BANK_BOOTH_20326, + Scenery.BANK_BOOTH_20327, Scenery.BANK_BOOTH_20328, Scenery.BANK_BOOTH_22819, Scenery.BANK_BOOTH_24914, + Scenery.BANK_BOOTH_25808, Scenery.BANK_BOOTH_26972, Scenery.BANK_BOOTH_29085, Scenery.BANK_BOOTH_30015, + Scenery.BANK_BOOTH_30016, Scenery.BANK_BOOTH_34205, Scenery.BANK_BOOTH_34752, Scenery.BANK_BOOTH_35647, + Scenery.BANK_BOOTH_35648, Scenery.BANK_BOOTH_36262, Scenery.BANK_BOOTH_36786, Scenery.BANK_BOOTH_37474 + ) + } + + /** + * Searches an area in the order described + * by each number with X being the location + * of node being interacted with. + * + * [1] + * [4][X][2] + * [3] + */ + private fun locateAdjacentBankerLinear(node: Node): NPC? { + for (dir in arrayOf(Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST)) { + Repository.findNPC(node.location.transform(dir))?.let { return it } + } + + return null + } + + /** + * If size is 1 then the following method + * searches a square area in the following + * order with X being the location of node + * that was interacted with. + * + * ->[ ] + * [ X ] + * [ ]-> + * + */ + private fun locateAdjacentBankerSquare(node: Node, size: Int = 1): NPC? { + for (y in (node.location.y - size)..(node.location.y + size)) { + for (x in (node.location.x - size)..(node.location.x + size)) { + Repository.findNPC(Location(x, y))?.let { return it } + } + } + + return null + } + + private fun tryInvokeBankerDialogue(player: Player, node: Node) { + /** + * First, we look for regular bankers that neatly stand in front of the bank booth. + * If that fails, we expand the search to a larger area. + */ + (locateAdjacentBankerLinear(node) ?: locateAdjacentBankerSquare(node, 2))?.let { + if (DialogueInterpreter.contains(it.id)) { + it.faceLocation(node.location) + openDialogue(player, it.id, NPC(it.id, it.location)) + } else { + player.dialogueInterpreter.open(NPCs.BANKER_494) + } + } + } + + private fun quickBankBoothUse(player: Player, node: Node): Boolean { + if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) { + return true + } + + if (BankerNPC.checkLunarIsleRestriction(player, node)) { + tryInvokeBankerDialogue(player, node) + return true + } + + player.bank.open() + return true + } + + private fun regularBankBoothUse(player: Player, node: Node): Boolean { + if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) { + return true + } + + if (ServerConstants.BANK_BOOTH_QUICK_OPEN) { + return quickBankBoothUse(player, node) + } + + tryInvokeBankerDialogue(player, node) + return true + } + + private fun collectBankBoothUse(player: Player, node: Node): Boolean { + if (BankerNPC.checkLunarIsleRestriction(player, node)) { + tryInvokeBankerDialogue(player, node) + return true + } + + GrandExchangeRecords.getInstance(player).openCollectionBox() + return true + } + + override fun defineListeners() { + on(BANK_BOOTHS, SCENERY, "use-quickly", "bank", handler = ::quickBankBoothUse) + on(BANK_BOOTHS, SCENERY, "use", handler = ::regularBankBoothUse) + on(BANK_BOOTHS, SCENERY, "collect", handler = ::collectBankBoothUse) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/BankChestHandler.kt b/Server/src/main/kotlin/rs09/game/interaction/object/BankChestHandler.kt new file mode 100644 index 000000000..9c7330cca --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/object/BankChestHandler.kt @@ -0,0 +1,39 @@ +package rs09.game.interaction.`object` + +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.IronmanMode +import org.rs09.consts.Scenery +import rs09.game.interaction.InteractionListener + +private val BANK_CHESTS = intArrayOf( + Scenery.BANK_CHEST_3194, + Scenery.BANK_CHEST_4483, + Scenery.BANK_CHEST_10562, + Scenery.BANK_CHEST_14382, + Scenery.BANK_CHEST_16695, + Scenery.BANK_CHEST_16696, + Scenery.BANK_CHEST_21301, + Scenery.BANK_CHEST_27662, + Scenery.BANK_CHEST_27663 +) + +/** + * Allows the user to interact with Bank Chests. + * + * @author vddCore + */ +class BankChestHandler : InteractionListener { + private fun useBankChest(player: Player, node: Node): Boolean { + if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) { + return true + } + + player.bank.open() + return true + } + + override fun defineListeners() { + on(BANK_CHESTS, SCENERY, "bank", "use", handler = ::useBankChest) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/object/BankDepositBoxHandler.kt b/Server/src/main/kotlin/rs09/game/interaction/object/BankDepositBoxHandler.kt new file mode 100644 index 000000000..c4ccebc2f --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/object/BankDepositBoxHandler.kt @@ -0,0 +1,60 @@ +package rs09.game.interaction.`object` + +import core.game.component.CloseEvent +import core.game.component.Component +import core.game.container.access.InterfaceContainer +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.IronmanMode +import org.rs09.consts.Components +import org.rs09.consts.Scenery +import rs09.game.interaction.InteractionListener + +private val BANK_DEPOSIT_BOXES = intArrayOf( + Scenery.BANK_DEPOSIT_BOX_9398, + Scenery.BANK_DEPOSIT_BOX_20228, + Scenery.BANK_DEPOSIT_BOX_25937, + Scenery.BANK_DEPOSIT_BOX_26969, + Scenery.BANK_DEPOSIT_BOX_34755, + Scenery.BANK_DEPOSIT_BOX_36788, + Scenery.BANK_DEPOSIT_BOX_39830 +) + +/** + * Allows the user to interact with bank deposit boxes. + * + * @author vddCore + */ +class BankDepositBoxHandler : InteractionListener { + + private fun openDepositBox(player: Player, node: Node) : Boolean { + if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) { + return true + } + + player.interfaceManager.open(Component(Components.BANK_DEPOSIT_BOX_11)).closeEvent = CloseEvent { p, _ -> + p.interfaceManager.openDefaultTabs() + return@CloseEvent true + } + + player.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6) + InterfaceContainer.generateItems( + player, + player.inventory.toArray(), + arrayOf( + "Examine", + "Deposit-X", + "Deposit-All", + "Deposit-10", + "Deposit-5", + "Deposit-1" + ), 11, 15, 5, 7 + ) + + return true + } + + override fun defineListeners() { + on(BANK_DEPOSIT_BOXES, SCENERY, "deposit", handler = ::openDepositBox) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/util/BankUtils.kt b/Server/src/main/kotlin/rs09/game/interaction/util/BankUtils.kt new file mode 100644 index 000000000..0f6ab9160 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/util/BankUtils.kt @@ -0,0 +1,23 @@ +package rs09.game.interaction.util + +import api.InputType +import api.sendInputDialogue +import api.sendMessage +import core.game.node.entity.player.Player + +object BankUtils { + fun transferX(player: Player, slot: Int, withdraw: Boolean, after: (() -> Unit)? = null) { + sendInputDialogue(player, InputType.AMOUNT, "Enter the amount:") { value -> + val number = Integer.parseInt(value.toString()) + + if (withdraw) { + player.bank.takeItem(slot, number) + } else { + player.bank.addItem(slot, number) + } + + player.bank.updateLastAmountX(number) + after?.let { it() } + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/npc/other/BankerNPC.kt b/Server/src/main/kotlin/rs09/game/node/entity/npc/other/BankerNPC.kt new file mode 100644 index 000000000..5b64c7320 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/node/entity/npc/other/BankerNPC.kt @@ -0,0 +1,77 @@ +package rs09.game.node.entity.npc.other + +import api.getScenery +import api.hasSealOfPassage +import core.game.content.dialogue.DialogueInterpreter +import core.game.node.Node +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location +import core.plugin.Initializable +import rs09.game.interaction.npc.BankerNPCListener +import rs09.game.interaction.`object`.BankBoothHandler + +private const val LUNAR_ISLE_BANK_REGION = 8253 + +@Initializable +class BankerNPC : AbstractNPC { + + companion object { + /** + * This is poorly named, but performs a few checks to see if the player + * is trying to access the bank on the Lunar Isle and returns a boolean + * controlling whether or not to pass the quick bank or collection use. + * + * TODO + * The location of this method is shit too. Find a better place for it? + */ + fun checkLunarIsleRestriction(player: Player, node: Node): Boolean { + if (node.location.regionId != LUNAR_ISLE_BANK_REGION) + return false + + if (!hasSealOfPassage(player)) { + return true + } + + return false + } + } + + //Constructor spaghetti because Arios I guess + constructor() : super(0, null) + private constructor(id: Int, location: Location) : super(id, location) + + override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { + return BankerNPC(id, location) + } + + fun findAdjacentBankBoothLocation(): Pair? { + for (side in arrayOf(Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.EAST)) { + val boothLocation = location.transform(side) + val sceneryObject = getScenery(boothLocation) + + if (sceneryObject != null && sceneryObject.id in BankBoothHandler.BANK_BOOTHS) { + return Pair(side, boothLocation.transform(side, 1)) + } + } + + return null + } + + override fun init() { + super.init() + + findAdjacentBankBoothLocation()?.let { + val (boothDirection, _) = it + + direction = boothDirection + isWalks = false + + setAttribute("facing_booth", true) + } + } + + override fun getIds(): IntArray = BankerNPCListener.BANKER_NPCS +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/link/diary/DiaryEventHook.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/link/diary/DiaryEventHook.kt index ef2c70b8f..e44fc94f1 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/link/diary/DiaryEventHook.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/player/link/diary/DiaryEventHook.kt @@ -8,19 +8,24 @@ import core.game.node.entity.player.link.diary.DiaryType import core.game.node.entity.skill.slayer.Tasks import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.Components import org.rs09.consts.Items import org.rs09.consts.NPCs import org.rs09.consts.Scenery import rs09.game.Event +import rs09.game.system.command.Privilege -class DiaryEventHook : LoginListener -{ +private const val TASK_DRAYNOR_BANK = 15 + +class DiaryEventHook : LoginListener, Commands { override fun login(player: Player) { player.hook(Event.ResourceProduced, DiaryGatherHooks) player.hook(Event.Teleport, DiaryTeleportHooks) player.hook(Event.NPCKilled, DiaryCombatHooks) player.hook(Event.FireLit, DiaryFireHooks) player.hook(Event.Interaction, DiaryInteractionEvents) + player.hook(Event.InterfaceOpened, DiaryInterfaceOpenEvents) + player.hook(Event.InterfaceClosed, DiaryInterfaceCloseEvents) } companion object { @@ -29,161 +34,223 @@ class DiaryEventHook : LoginListener } } - private object DiaryInteractionEvents : EventHook - { + private object DiaryInterfaceCloseEvents : EventHook { + override fun process(entity: Entity, event: InterfaceCloseEvent) { + // TODO("Not yet implemented") + } + } + + private object DiaryInterfaceOpenEvents : EventHook { + override fun process(entity: Entity, event: InterfaceOpenEvent) { + if (entity !is Player) + return + + when (entity.viewport.region.id) { + 12338 -> if (event.component.id == Components.BANK_V2_MAIN_762) { + finishTask( + entity, + DiaryType.LUMBRIDGE, + 1, + TASK_DRAYNOR_BANK + ) + } + } + } + } + + private object DiaryInteractionEvents : EventHook { override fun process(entity: Entity, event: InteractionEvent) { - if(entity !is Player) return + if (entity !is Player) return val regionId = entity.viewport.region.id - if(event.target is core.game.node.scenery.Scenery) - when(event.target.id) - { - 11729, 11727 -> if(regionId == 11828) finishTask(entity, DiaryType.FALADOR, 0, 3) - 11889 -> if(regionId == 11572 && isEquipped(entity, Items.PROSELYTE_SALLET_9672) && isEquipped(entity, Items.PROSELYTE_HAUBERK_9674) && isEquipped(entity, Items.PROSELYTE_CUISSE_9676)) finishTask(entity, DiaryType.FALADOR, 2, 0) - 30941 -> if(regionId == 12184) finishTask(entity, DiaryType.FALADOR, 2, 6) - 36771 -> if(regionId == 12850) finishTask(entity, DiaryType.LUMBRIDGE, 0, 0) - 12537 -> if(regionId == 12337) finishTask(entity, DiaryType.LUMBRIDGE, 0, 11) - 26934 -> if(regionId == 12342) finishTask(entity, DiaryType.VARROCK, 0, 10) - 24350,24361 -> if(regionId == 12854) finishTask(entity, DiaryType.VARROCK, 0, 18) - in 115..122 -> if(event.option == "burst") finishTask(entity, DiaryType.FALADOR, 0, 12) + if (event.target is core.game.node.scenery.Scenery) + when (event.target.id) { + 11729, 11727 -> if (regionId == 11828) finishTask(entity, DiaryType.FALADOR, 0, 3) + 11889 -> if (regionId == 11572 && isEquipped(entity, Items.PROSELYTE_SALLET_9672) && isEquipped( + entity, + Items.PROSELYTE_HAUBERK_9674 + ) && isEquipped(entity, Items.PROSELYTE_CUISSE_9676) + ) finishTask(entity, DiaryType.FALADOR, 2, 0) + 30941 -> if (regionId == 12184) finishTask(entity, DiaryType.FALADOR, 2, 6) + 36771 -> if (regionId == 12850) finishTask(entity, DiaryType.LUMBRIDGE, 0, 0) + 12537 -> if (regionId == 12337) finishTask(entity, DiaryType.LUMBRIDGE, 0, 11) + 26934 -> if (regionId == 12342) finishTask(entity, DiaryType.VARROCK, 0, 10) + 24350, 24361 -> if (regionId == 12854) finishTask(entity, DiaryType.VARROCK, 0, 18) + in 115..122 -> if (event.option == "burst") finishTask(entity, DiaryType.FALADOR, 0, 12) 16149 -> finishTask(entity, DiaryType.VARROCK, 0, 4) //2112 -> if() } } } - private object DiaryFireHooks : EventHook - { + private object DiaryFireHooks : EventHook { val lumCastleBorders = ZoneBorders(3216, 3207, 3225, 3233, 0) override fun process(entity: Entity, event: LitFireEvent) { - if(entity !is Player) return + if (entity !is Player) return val region = entity.viewport.region.id - when(region) - { - 10806 -> if(event.logId == Items.MAGIC_LOGS_1513) finishTask(entity, DiaryType.SEERS_VILLAGE, 2, 5) - 12593,12849 -> if(event.logId == Items.LOGS_1511) finishTask(entity, DiaryType.LUMBRIDGE, 1, 9) + when (region) { + 10806 -> if (event.logId == Items.MAGIC_LOGS_1513) finishTask(entity, DiaryType.SEERS_VILLAGE, 2, 5) + 12593, 12849 -> if (event.logId == Items.LOGS_1511) finishTask(entity, DiaryType.LUMBRIDGE, 1, 9) else -> { - if(event.logId == Items.WILLOW_LOGS_1519 && lumCastleBorders.insideBorder(entity)) - finishTask(entity, DiaryType.LUMBRIDGE,2 ,3) + if (event.logId == Items.WILLOW_LOGS_1519 && lumCastleBorders.insideBorder(entity)) + finishTask(entity, DiaryType.LUMBRIDGE, 2, 3) } } } } - private object DiaryGatherHooks : EventHook - { + private object DiaryGatherHooks : EventHook { override fun process(entity: Entity, event: ResourceProducedEvent) { - if(entity !is Player) return + if (entity !is Player) return val regionId = entity.viewport.region.id - when(event.itemId) - { + when (event.itemId) { //Cut a log from a teak tree Items.TEAK_LOGS_6333 -> finishTask(entity, DiaryType.KARAMJA, 1, 7) //Cut a log from a mahogany tree Items.MAHOGANY_LOGS_6332 -> finishTask(entity, DiaryType.KARAMJA, 1, 8) Items.LOGS_1511 -> { //Cut down a dying tree in the Lumber Yard - if(event.source.id == Scenery.DYING_TREE_24168 && regionId == 13110) + if (event.source.id == Scenery.DYING_TREE_24168 && regionId == 13110) finishTask(entity, DiaryType.VARROCK, 0, 6) } Items.YEW_LOGS_1515 -> { - if(regionId == 10806) { - setAttribute(entity, "/save:diary:seers:cut-yew", getAttribute(entity, "diary:seers:cut-yew", 0) + 1) + if (regionId == 10806) { + setAttribute( + entity, + "/save:diary:seers:cut-yew", + getAttribute(entity, "diary:seers:cut-yew", 0) + 1 + ) if (getAttribute(entity, "diary:seers:cut-yew", 0) >= 5) finishTask(entity, DiaryType.SEERS_VILLAGE, 2, 1) } } - Items.WILLOW_LOGS_1519 -> if(regionId == 12850) finishTask(entity, DiaryType.LUMBRIDGE, 2, 6) + Items.WILLOW_LOGS_1519 -> if (regionId == 12850) finishTask(entity, DiaryType.LUMBRIDGE, 2, 6) - Items.RAW_MACKEREL_353 -> if(regionId == 11317) finishTask(entity, DiaryType.SEERS_VILLAGE, 0, 11) + Items.RAW_MACKEREL_353 -> if (regionId == 11317) finishTask(entity, DiaryType.SEERS_VILLAGE, 0, 11) - Items.RAW_BASS_363 -> if(regionId == 11317 && !getAttribute(entity, "diary:seers:bass-caught", false)) entity.setAttribute("/save:diary:seers:bass-caught", true) + Items.RAW_BASS_363 -> if (regionId == 11317 && !getAttribute( + entity, + "diary:seers:bass-caught", + false + ) + ) entity.setAttribute("/save:diary:seers:bass-caught", true) - Items.RAW_SHARK_383 -> if(regionId == 11317) setAttribute(entity, "/save:diary:seers:shark-caught", getAttribute(entity, "diary:seers:shark-caught", 0) + 1) + Items.RAW_SHARK_383 -> if (regionId == 11317) setAttribute( + entity, + "/save:diary:seers:shark-caught", + getAttribute(entity, "diary:seers:shark-caught", 0) + 1 + ) - Items.RAW_SHRIMPS_2514, Items.RAW_SHRIMPS_317 -> if(regionId == 12849) finishTask(entity, DiaryType.LUMBRIDGE, 0, 13) + Items.RAW_SHRIMPS_2514, Items.RAW_SHRIMPS_317 -> if (regionId == 12849) finishTask( + entity, + DiaryType.LUMBRIDGE, + 0, + 13 + ) - Items.RAW_PIKE_349 -> if(regionId == 12850) finishTask(entity, DiaryType.LUMBRIDGE, 1, 4) + Items.RAW_PIKE_349 -> if (regionId == 12850) finishTask(entity, DiaryType.LUMBRIDGE, 1, 4) - Items.RAW_SALMON_331 -> if(regionId == 12850) finishTask(entity, DiaryType.LUMBRIDGE, 2, 9) + Items.RAW_SALMON_331 -> if (regionId == 12850) finishTask(entity, DiaryType.LUMBRIDGE, 2, 9) - Items.RAW_TROUT_335 -> if(regionId == 12341) finishTask(entity, DiaryType.VARROCK, 0, 16) + Items.RAW_TROUT_335 -> if (regionId == 12341) finishTask(entity, DiaryType.VARROCK, 0, 16) Items.IRON_ORE_440 -> { - if(regionId == 13108) finishTask(entity, DiaryType.VARROCK, 0, 2) - else if(regionId == 13107) finishTask(entity, DiaryType.LUMBRIDGE, 1, 0) + if (regionId == 13108) finishTask(entity, DiaryType.VARROCK, 0, 2) + else if (regionId == 13107) finishTask(entity, DiaryType.LUMBRIDGE, 1, 0) } - Items.LIMESTONE_3211 -> if(regionId == 13366) finishTask(entity, DiaryType.VARROCK, 0, 15) + Items.LIMESTONE_3211 -> if (regionId == 13366) finishTask(entity, DiaryType.VARROCK, 0, 15) - Items.GOLD_ORE_444 -> if(regionId == 10802) finishTask(entity, DiaryType.KARAMJA, 0, 2) + Items.GOLD_ORE_444 -> if (regionId == 10802) finishTask(entity, DiaryType.KARAMJA, 0, 2) - Items.UNCUT_RED_TOPAZ_1629 -> if(regionId == 11310 || regionId == 11410) finishTask(entity, DiaryType.KARAMJA, 1, 18) + Items.UNCUT_RED_TOPAZ_1629 -> if (regionId == 11310 || regionId == 11410) finishTask( + entity, + DiaryType.KARAMJA, + 1, + 18 + ) - Items.SOFT_CLAY_1761 -> if(regionId == 12596) finishTask(entity, DiaryType.LUMBRIDGE, 0, 5) + Items.SOFT_CLAY_1761 -> if (regionId == 12596) finishTask(entity, DiaryType.LUMBRIDGE, 0, 5) - Items.COPPER_ORE_436 -> if(regionId == 12849) finishTask(entity, DiaryType.LUMBRIDGE, 0, 12) + Items.COPPER_ORE_436 -> if (regionId == 12849) finishTask(entity, DiaryType.LUMBRIDGE, 0, 12) - Items.SILVER_ORE_442 -> if(regionId == 13107) finishTask(entity, DiaryType.LUMBRIDGE, 2, 10) + Items.SILVER_ORE_442 -> if (regionId == 13107) finishTask(entity, DiaryType.LUMBRIDGE, 2, 10) - Items.COAL_453 -> if(regionId == 12593) finishTask(entity, DiaryType.LUMBRIDGE, 2, 11) + Items.COAL_453 -> if (regionId == 12593) finishTask(entity, DiaryType.LUMBRIDGE, 2, 11) - Items.BASS_365 -> if(regionId == 11317 && getAttribute(entity, "diary:seers:bass-caught", false)) finishTask(entity, DiaryType.SEERS_VILLAGE, 1, 11) + Items.BASS_365 -> if (regionId == 11317 && getAttribute( + entity, + "diary:seers:bass-caught", + false + ) + ) finishTask(entity, DiaryType.SEERS_VILLAGE, 1, 11) - Items.SHARK_385 -> if(regionId == 11317 && getAttribute(entity, "diary:seers:shark-cooked", false) && isEquipped(entity, Items.COOKING_GAUNTLETS_775)) entity.incrementAttribute("/save:diary:seers:shark-cooked") + Items.SHARK_385 -> if (regionId == 11317 && getAttribute( + entity, + "diary:seers:shark-cooked", + false + ) && isEquipped(entity, Items.COOKING_GAUNTLETS_775) + ) entity.incrementAttribute("/save:diary:seers:shark-cooked") - Items.LOBSTER_379 -> if(event.source.id == Scenery.COOKING_RANGE_114) finishTask(entity, DiaryType.LUMBRIDGE, 2, 4) + Items.LOBSTER_379 -> if (event.source.id == Scenery.COOKING_RANGE_114) finishTask( + entity, + DiaryType.LUMBRIDGE, + 2, + 4 + ) } - when(event.original) - { - Items.RAW_RAT_MEAT_2134 -> if((regionId == 12593 || regionId == 12849) && event.itemId == Items.COOKED_MEAT_2142) finishTask(entity, DiaryType.LUMBRIDGE, 1, 10) + when (event.original) { + Items.RAW_RAT_MEAT_2134 -> if ((regionId == 12593 || regionId == 12849) && event.itemId == Items.COOKED_MEAT_2142) finishTask( + entity, + DiaryType.LUMBRIDGE, + 1, + 10 + ) } - if(getAttribute(entity, "diary:seers:shark-caught", 0) >= 5) + if (getAttribute(entity, "diary:seers:shark-caught", 0) >= 5) finishTask(entity, DiaryType.SEERS_VILLAGE, 2, 7) - if(getAttribute(entity, "diary:seers:shark-cooked", 0) >= 5) + if (getAttribute(entity, "diary:seers:shark-cooked", 0) >= 5) finishTask(entity, DiaryType.SEERS_VILLAGE, 2, 8) - if((regionId == 12593 || regionId == 12849) && event.source.name.startsWith("dead", true)) + if ((regionId == 12593 || regionId == 12849) && event.source.name.startsWith("dead", true)) finishTask(entity, DiaryType.LUMBRIDGE, 1, 8) - if(event.source.id == NPCs.FISHING_SPOT_333 && entity.zoneMonitor.isInZone("karamja")) + if (event.source.id == NPCs.FISHING_SPOT_333 && entity.zoneMonitor.isInZone("karamja")) finishTask(entity, DiaryType.KARAMJA, 0, 6) - if(event.source.id == Scenery.YEW_TREE_8513 && regionId == 11828) + if (event.source.id == Scenery.YEW_TREE_8513 && regionId == 11828) finishTask(entity, DiaryType.FALADOR, 2, 3) - if(event.source.id == Scenery.COOKING_RANGE_114 && regionId == 12850) + if (event.source.id == Scenery.COOKING_RANGE_114 && regionId == 12850) finishTask(entity, DiaryType.LUMBRIDGE, 0, 7) } } - private object DiaryTeleportHooks : EventHook - { + private object DiaryTeleportHooks : EventHook { override fun process(entity: Entity, event: TeleportEvent) { TODO("Not yet implemented") } } - private object DiaryCombatHooks : EventHook - { + private object DiaryCombatHooks : EventHook { val faladorDucks = intArrayOf(NPCs.DUCK_46, NPCs.DUCK_2693, NPCs.DUCK_6113) val lumbridgeCows = intArrayOf(81, 397, 955, 1766, 1767, 3309) val elementalNPCs = intArrayOf(1019, 1020, 1021, 1022) val wyverns = Tasks.SKELETAL_WYVERN.npcs - val metalDragons = intArrayOf(NPCs.BRONZE_DRAGON_1590, NPCs.IRON_DRAGON_1591, NPCs.STEEL_DRAGON_1592, NPCs.STEEL_DRAGON_3590) + val metalDragons = + intArrayOf(NPCs.BRONZE_DRAGON_1590, NPCs.IRON_DRAGON_1591, NPCs.STEEL_DRAGON_1592, NPCs.STEEL_DRAGON_3590) val lumZombies = intArrayOf(NPCs.ZOMBIE_73, NPCs.ZOMBIE_74) override fun process(entity: Entity, event: NPCKillEvent) { - if(entity !is Player) return - when(event.npc.id) - { + if (entity !is Player) return + when (event.npc.id) { //FALADOR LEVEL 0 TASK 10 in faladorDucks -> { - if(entity.location.withinDistance(Location(2991, 3383, 0))) + if (entity.location.withinDistance(Location(2991, 3383, 0))) finishTask(entity, DiaryType.FALADOR, 0, 9) } @@ -195,22 +262,21 @@ class DiaryEventHook : LoginListener } NPCs.JOGRE_113 -> { - if(entity.viewport.region.id == 11412) + if (entity.viewport.region.id == 11412) finishTask(entity, DiaryType.KARAMJA, 0, 9) } in elementalNPCs -> { - if(!entity.location.withinDistance(Location(2719,9889,0), 100)) + if (!entity.location.withinDistance(Location(2719, 9889, 0), 100)) return - when(event.npc.id) - { + when (event.npc.id) { 1019 -> setAttribute(entity, "/save:diary:seers:elemental:fire", true) 1020 -> setAttribute(entity, "/save:diary:seers:elemental:earth", true) 1021 -> setAttribute(entity, "/save:diary:seers:elemental:air", true) 1022 -> setAttribute(entity, "/save:diary:seers:elemental:water", true) } for (element in arrayOf("fire", "earth", "air", "water")) - if(!getAttribute(entity, "diary:seers:$element", false)) + if (!getAttribute(entity, "diary:seers:$element", false)) return finishTask(entity, DiaryType.SEERS_VILLAGE, 1, 4) } @@ -218,31 +284,64 @@ class DiaryEventHook : LoginListener NPCs.KET_ZEK_2743, NPCs.KET_ZEK_2744 -> finishTask(entity, DiaryType.KARAMJA, 2, 1) in metalDragons -> { - if(entity.viewport.region.id == 10899 || entity.viewport.region.id == 10900) + if (entity.viewport.region.id == 10899 || entity.viewport.region.id == 10900) finishTask(entity, DiaryType.KARAMJA, 2, 9) } in wyverns -> finishTask(entity, DiaryType.FALADOR, 2, 8) in lumZombies -> { - if(entity.viewport.region.id == 12438 || entity.viewport.region.id == 12439) + if (entity.viewport.region.id == 12438 || entity.viewport.region.id == 12439) finishTask(entity, DiaryType.LUMBRIDGE, 1, 18) } NPCs.GIANT_RAT_86 -> { - if(entity.viewport.region.id == 12593 || entity.viewport.region.id == 12849) + if (entity.viewport.region.id == 12593 || entity.viewport.region.id == 12849) finishTask(entity, DiaryType.LUMBRIDGE, 1, 7) } - NPCs.TOWER_ARCHER_688 -> setAttribute(entity, "/save:diary:seers:tower-archers", getAttribute(entity, "diary:seers:tower-archers", 0) or 1) - NPCs.TOWER_ARCHER_689 -> setAttribute(entity, "/save:diary:seers:tower-archers", getAttribute(entity, "diary:seers:tower-archers", 0) or (1 shl 1)) - NPCs.TOWER_ARCHER_690 -> setAttribute(entity, "/save:diary:seers:tower-archers", getAttribute(entity, "diary:seers:tower-archers", 0) or (1 shl 2)) - NPCs.TOWER_ARCHER_691 -> setAttribute(entity, "/save:diary:seers:tower-archers", getAttribute(entity, "diary:seers:tower-archers", 0) or (1 shl 3)) + NPCs.TOWER_ARCHER_688 -> setAttribute( + entity, + "/save:diary:seers:tower-archers", + getAttribute(entity, "diary:seers:tower-archers", 0) or 1 + ) + NPCs.TOWER_ARCHER_689 -> setAttribute( + entity, + "/save:diary:seers:tower-archers", + getAttribute(entity, "diary:seers:tower-archers", 0) or (1 shl 1) + ) + NPCs.TOWER_ARCHER_690 -> setAttribute( + entity, + "/save:diary:seers:tower-archers", + getAttribute(entity, "diary:seers:tower-archers", 0) or (1 shl 2) + ) + NPCs.TOWER_ARCHER_691 -> setAttribute( + entity, + "/save:diary:seers:tower-archers", + getAttribute(entity, "diary:seers:tower-archers", 0) or (1 shl 3) + ) } - if(getAttribute(entity,"diary:seers:tower-archers", 0) == 0xF) + if (getAttribute(entity, "diary:seers:tower-archers", 0) == 0xF) finishTask(entity, DiaryType.SEERS_VILLAGE, 1, 6) } } + + override fun defineCommands() { + define("cleardiary", Privilege.ADMIN) { player, _ -> + // I know this sucks but it's only for debugging. + // Please don't slap me, daddy. + // + for (type in DiaryType.values()) { + val diary = player.achievementDiaryManager.getDiary(type) + for (level in 0 until diary.levelStarted.size) { + for (task in 0 until diary.taskCompleted[level].size) { + diary.resetTask(player, level, task) + } + } + } + sendMessage(player, "All achievement diaries cleared successfully.") + } + } } diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/DevelopmentCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/DevelopmentCommandSet.kt index 717cb3661..4525c8793 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/DevelopmentCommandSet.kt +++ b/Server/src/main/kotlin/rs09/game/system/command/sets/DevelopmentCommandSet.kt @@ -38,6 +38,10 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { } } + define("region") {player, args -> + sendMessage(player, "Region ID: ${player.viewport.region.regionId}") + } + define("spellbook"){player, args -> if(args.size < 2){ reject(player,"Usage: ::spellbook [int]. 0 = MODERN, 1 = ANCIENTS, 2 = LUNARS") diff --git a/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt b/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt index dd9a76957..e88c06ac7 100644 --- a/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt +++ b/Server/src/main/kotlin/rs09/game/system/config/ServerConfigParser.kt @@ -116,6 +116,7 @@ object ServerConfigParser { ServerConstants.RULES_AND_INFO_ENABLED = data.getBoolean("world.show_rules", true) ServerConstants.BOTS_INFLUENCE_PRICE_INDEX = data.getBoolean("world.bots_influence_ge_price", true) ServerConstants.REVENANT_POPULATION = data.getLong("world.revenant_population", 30L).toInt() + ServerConstants.BANK_BOOTH_QUICK_OPEN = data.getBoolean("world.bank_booth_quick_open", false) ServerConstants.DISCORD_GE_WEBHOOK = data.getString("server.discord_webhook", "") }