From 0b9d540d455db43bbf2c44fbc887b7880100f3c3 Mon Sep 17 00:00:00 2001 From: skelsoft <9147770-skelsoft@users.noreply.gitlab.com> Date: Fri, 18 Mar 2022 23:35:19 +0000 Subject: [PATCH] Redwings Drop III: Ceikry GE, Job, Slayer, Achievement Diary rewrites --- .../game/container/impl/BankContainer.java | 16 +- .../activity/guild/RangingGuildPlugin.java | 15 - .../activity/partyroom/BalloonManager.java | 3 - .../stronghold/StrongHoldSecurityPlugin.java | 1 - .../activity/tzhaar/TzhaarFightCaveNPC.java | 12 - .../content/dialogue/GrandExchangeClerk.java | 10 +- .../content/global/EnchantedJewellery.java | 5 +- .../global/action/ClimbActionHandler.java | 39 - .../main/java/core/game/ge/GEGuidePrice.java | 4 +- .../inter/GrandExchangeInterface.java | 395 +++------- .../game/interaction/npc/NPCTradePlugin.kt | 20 + .../interaction/object/BankingPlugin.java | 13 +- .../object/GrandExchangePlugin.java | 12 +- .../object/LumbridgeBasementPlugin.java | 67 +- .../interaction/object/SlashWebPlugin.java | 2 + .../java/core/game/node/entity/Entity.java | 58 ++ .../combat/special/ClobberSpecialHandler.java | 42 +- .../special/SeercullSpecialHandler.java | 80 ++ .../combat/special/WeakenSpecialHandler.java | 78 ++ .../java/core/game/node/entity/npc/NPC.java | 30 +- .../node/entity/npc/drop/NPCDropTables.java | 4 +- .../game/node/entity/npc/other/CowNPC.java | 12 - .../game/node/entity/npc/other/DuckNPC.java | 11 - .../entity/npc/other/ElementalMonsterNPC.java | 74 -- .../game/node/entity/npc/other/JogreNPC.java | 63 -- .../npc/other/LumbridgeSwampGiantRatNPC.java | 58 -- .../node/entity/npc/other/MetalDragonNPC.java | 9 - .../game/node/entity/npc/other/ZombieNPC.java | 56 -- .../core/game/node/entity/player/Player.java | 11 +- .../player/info/login/LoginConfiguration.java | 11 +- .../player/info/portal/PlayerSQLManager.java | 2 +- .../entity/player/link/BankPinManager.java | 7 +- .../entity/player/link/TeleportManager.java | 5 +- .../skill/construction/HouseManager.java | 2 +- .../questhall/MountedGloryPlugin.java | 3 +- .../entity/skill/cooking/CookableItems.java | 7 +- .../skill/cooking/SinewCookingPulse.java | 32 + .../skill/cooking/StandardCookingPulse.java | 10 +- .../skill/firemaking/FireMakingPulse.java | 15 +- .../game/node/entity/skill/fishing/Fish.java | 2 +- .../fletching/items/bow/StringPulse.java | 2 +- .../woodcutting/WoodcuttingSkillPulse.java | 52 +- .../node/entity/skill/magic/MagicStaff.java | 4 +- .../skill/slayer/AberrantSpectreNPC.java | 1 + .../node/entity/skill/slayer/BansheeNPC.java | 1 + .../entity/skill/slayer/CaveHorrorNPC.java | 1 + .../entity/skill/slayer/DustDevilNPC.java | 1 + .../node/entity/skill/slayer/KuraskNPC.java | 2 +- .../game/node/entity/skill/slayer/Master.java | 10 +- .../skill/slayer/MirrorShieldHandler.java | 1 + .../skill/slayer/SkeletalWyvernNPC.java | 9 - .../entity/skill/slayer/SlayerManager.java | 347 --------- .../skill/slayer/SlayerMasterDialogue.java | 2 +- .../node/entity/skill/slayer/TurothNPC.java | 2 +- .../slayer/dungeon/LumbridgeDungeon.java | 2 +- .../skill/slayer/dungeon/SmokeDungeon.java | 2 +- .../node/entity/skill/thieving/Stall.java | 4 +- .../skill/thieving/StallThiefPulse.java | 13 +- .../main/java/core/game/node/item/Item.java | 1 - .../core/game/system/SystemTermination.java | 2 - .../java/core/game/world/map/Location.java | 17 + .../java/core/game/world/map/RegionManager.kt | 25 +- .../world/map/zone/impl/WildernessZone.java | 3 - .../java/core/gui/tab/GrandExchangeTab.java | 13 +- .../core/net/packet/in/ExaminePacket.java | 3 + .../net/packet/in/GrandExchangePacket.java | 24 +- .../core/net/packet/in/InteractionPacket.java | 2 + .../core/tools/mysql/DatabaseManager.java | 3 + Server/src/main/kotlin/api/ContentAPI.kt | 12 +- Server/src/main/kotlin/api/LoginListener.kt | 7 + Server/src/main/kotlin/api/events/Event.kt | 3 + .../src/main/kotlin/api/events/EventHook.kt | 7 + Server/src/main/kotlin/api/events/Events.kt | 11 + Server/src/main/kotlin/rs09/Server.kt | 5 +- .../src/main/kotlin/rs09/ServerConstants.kt | 4 +- Server/src/main/kotlin/rs09/game/Event.kt | 11 + .../kotlin/rs09/game/ai/general/ScriptAPI.kt | 20 +- .../ai/general/scriptrepository/Adventurer.kt | 2 +- .../general/scriptrepository/SharkCatcher.kt | 5 +- .../activity/vinesweeper/Vinesweeper.kt | 8 +- ...DumpContainer.kt => DepositAllDialogue.kt} | 8 +- .../region/apeatoll/marim/DagaDialogue.kt | 18 +- .../region/etceteria/FishmongerEtcDialogue.kt | 38 + .../region/lunarisle/SirsalBankerDialogue.kt | 2 +- .../miscellania/FishmongerMiscDialogue.kt | 16 +- .../rellekka/BjornAndEldgrimDialogues.kt | 52 ++ .../region/rellekka/BlaninDialogue.kt | 44 ++ .../region/rellekka/CouncilWorkerDialogue.kt | 12 +- .../dialogue/region/rellekka/DronDialogue.kt | 44 ++ .../rellekka/FishmongerRellekkaDialogue.kt | 43 ++ .../region/rellekka/FurTraderDialogue.kt | 13 +- .../region/rellekka/IngridHradsonDialogue.kt | 49 ++ .../rellekka/LonghallBouncerDialogue.kt | 44 ++ .../dialogue/region/rellekka/ReesoDialogue.kt | 41 + .../region/rellekka/TalkToChiefDialogue.kt | 47 ++ .../region/rellekka/VolfOlasfsonDialogue.kt | 48 ++ .../rellekka/WaitingOnTheShowDialogues.kt | 35 + .../content/global/action/EquipHandler.kt | 4 +- .../content/global/action/PickupHandler.kt | 1 - .../rs09/game/content/jobs/JobManager.kt | 13 +- .../jobs/WorkForInteractionListener.kt | 28 +- .../ChieftanBrundtDialogue.kt | 5 +- .../CouncilWorkerFTDialogue.kt | 37 +- .../thefremenniktrials/FishermanDialogue.kt | 9 + .../thefremenniktrials/PeerTheSeerDialogue.kt | 8 +- .../thefremenniktrials/SeersHouseListeners.kt | 5 + .../TFTInteractionListeners.kt | 29 +- .../main/kotlin/rs09/game/ge/GEAutoStock.kt | 2 +- Server/src/main/kotlin/rs09/game/ge/GEDB.kt | 165 +++++ .../main/kotlin/rs09/game/ge/GrandExchange.kt | 274 ++++++- .../kotlin/rs09/game/ge/GrandExchangeOffer.kt | 186 ++++- .../rs09/game/ge/GrandExchangeRecords.kt | 242 ++++++ .../main/kotlin/rs09/game/ge/OfferManager.kt | 526 ------------- .../rs09/game/ge/PlayerGrandExchange.kt | 700 ------------------ .../game/interaction/InteractionListener.kt | 7 + .../game/interaction/InteractionListeners.kt | 3 + .../interaction/inter/ge/ExchangeItemSets.kt | 82 ++ .../game/interaction/inter/ge/StockMarket.kt | 396 ++++++++++ .../interaction/item/BraceletOfClayPlugin.kt | 7 +- .../item/ItemQuestRequirementListener.kt | 118 +++ .../game/interaction/item/LegendsCapeLock.kt | 15 - .../interaction/item/QuestCapeListener.kt | 21 - .../interaction/npc/NPCDepositListener.kt | 29 + .../region/rellekka/RellekkaListeners.kt | 1 + .../combat/handlers/MeleeSwingHandler.kt | 2 +- .../combat/handlers/RangeSwingHandler.kt | 2 +- .../entity/player/info/login/LoginParser.kt | 8 +- .../player/info/login/PlayerSaveParser.kt | 16 +- .../entity/player/info/login/PlayerSaver.kt | 16 +- .../player/link/diary/DiaryEventHook.kt | 229 ++++++ .../decoration/questhall/MountedGlory.kt | 3 +- .../skill/gather/fishing/FishingPulse.kt | 142 +--- .../skill/gather/mining/MiningSkillPulse.kt | 66 +- .../skill/slayer/SlayerEquipmentFlags.kt | 7 +- .../node/entity/skill/slayer/SlayerFlags.kt | 2 +- .../entity/skill/slayer/SlayerKillHook.kt | 46 ++ .../node/entity/skill/slayer/SlayerManager.kt | 172 +++++ .../node/entity/skill/slayer/SlayerUtils.kt | 45 +- .../system/command/oldsys/VisualCommand.kt | 8 +- .../command/sets/DevelopmentCommandSet.kt | 21 + .../system/command/sets/MiscCommandSet.kt | 254 ++++--- .../system/command/sets/SlayerCommandSet.kt | 2 +- .../system/command/sets/SpawnCommandSet.kt | 12 +- .../main/kotlin/rs09/game/world/GameWorld.kt | 7 + .../rs09/game/world/callback/CallbackHub.kt | 1 - .../world/repository/DisconnectionQueue.kt | 2 +- .../rs09/game/world/repository/Repository.kt | 14 + 147 files changed, 3488 insertions(+), 2996 deletions(-) create mode 100644 Server/src/main/java/core/game/node/entity/combat/special/SeercullSpecialHandler.java create mode 100644 Server/src/main/java/core/game/node/entity/combat/special/WeakenSpecialHandler.java delete mode 100644 Server/src/main/java/core/game/node/entity/npc/other/ElementalMonsterNPC.java delete mode 100644 Server/src/main/java/core/game/node/entity/npc/other/JogreNPC.java delete mode 100644 Server/src/main/java/core/game/node/entity/npc/other/LumbridgeSwampGiantRatNPC.java delete mode 100644 Server/src/main/java/core/game/node/entity/npc/other/ZombieNPC.java create mode 100644 Server/src/main/java/core/game/node/entity/skill/cooking/SinewCookingPulse.java delete mode 100644 Server/src/main/java/core/game/node/entity/skill/slayer/SlayerManager.java create mode 100644 Server/src/main/kotlin/api/LoginListener.kt create mode 100644 Server/src/main/kotlin/api/events/Event.kt create mode 100644 Server/src/main/kotlin/api/events/EventHook.kt create mode 100644 Server/src/main/kotlin/api/events/Events.kt create mode 100644 Server/src/main/kotlin/rs09/game/Event.kt rename Server/src/main/kotlin/rs09/game/content/dialogue/{DumpContainer.kt => DepositAllDialogue.kt} (85%) create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/etceteria/FishmongerEtcDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/BjornAndEldgrimDialogues.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/BlaninDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/DronDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/FishmongerRellekkaDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/IngridHradsonDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/LonghallBouncerDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/ReesoDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/TalkToChiefDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/VolfOlasfsonDialogue.kt create mode 100644 Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/WaitingOnTheShowDialogues.kt create mode 100644 Server/src/main/kotlin/rs09/game/ge/GEDB.kt create mode 100644 Server/src/main/kotlin/rs09/game/ge/GrandExchangeRecords.kt delete mode 100644 Server/src/main/kotlin/rs09/game/ge/OfferManager.kt delete mode 100644 Server/src/main/kotlin/rs09/game/ge/PlayerGrandExchange.kt create mode 100644 Server/src/main/kotlin/rs09/game/interaction/inter/ge/ExchangeItemSets.kt create mode 100644 Server/src/main/kotlin/rs09/game/interaction/inter/ge/StockMarket.kt create mode 100644 Server/src/main/kotlin/rs09/game/interaction/item/ItemQuestRequirementListener.kt delete mode 100644 Server/src/main/kotlin/rs09/game/interaction/item/LegendsCapeLock.kt delete mode 100644 Server/src/main/kotlin/rs09/game/interaction/item/QuestCapeListener.kt create mode 100644 Server/src/main/kotlin/rs09/game/interaction/npc/NPCDepositListener.kt create mode 100644 Server/src/main/kotlin/rs09/game/node/entity/player/link/diary/DiaryEventHook.kt rename Server/src/main/{java/core => kotlin/rs09}/game/node/entity/skill/slayer/SlayerEquipmentFlags.kt (94%) create mode 100644 Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerKillHook.kt create mode 100644 Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerManager.kt 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 016dee8f9..bc59f94c1 100644 --- a/Server/src/main/java/core/game/container/impl/BankContainer.java +++ b/Server/src/main/java/core/game/container/impl/BankContainer.java @@ -1,5 +1,7 @@ package core.game.container.impl; +import core.game.component.CloseEvent; +import core.game.container.access.InterfaceContainer; import rs09.ServerConstants; import core.game.component.Component; import core.game.container.*; @@ -81,6 +83,18 @@ public final class BankContainer extends Container { this.player = player; } + /** + * Method used to open the deposit box. + */ + public void openDepositBox() { + player.getInterfaceManager().open(new Component(11)).setCloseEvent((player, c) -> { + player.getInterfaceManager().openDefaultTabs(); + return true; + }); + player.getInterfaceManager().hideTabs(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); + } + /** * Open the bank. */ @@ -130,7 +144,7 @@ public final class BankContainer extends Container { BankContainer.this.close(); return true; }); - this.player.getBank().refresh(player.getBank().listener); + refresh(listener); player.getInterfaceManager().openSingleTab(new Component(763)); player.getInventory().getListeners().add(player.getBank().listener); player.getInventory().refresh(); diff --git a/Server/src/main/java/core/game/content/activity/guild/RangingGuildPlugin.java b/Server/src/main/java/core/game/content/activity/guild/RangingGuildPlugin.java index 8e445cd0b..07551a021 100644 --- a/Server/src/main/java/core/game/content/activity/guild/RangingGuildPlugin.java +++ b/Server/src/main/java/core/game/content/activity/guild/RangingGuildPlugin.java @@ -1073,21 +1073,6 @@ public final class RangingGuildPlugin extends OptionHandler { super(id, location); } - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - Player player = (Player) killer; - if (!player.getAchievementDiaryManager().hasCompletedTask(DiaryType.SEERS_VILLAGE, 1, 6)) { - player.setAttribute("/save:diary:seers:tower-archers", - player.getAttribute("diary:seers:tower-archers", 0) - | 1 << getId() - 688); - if (player.getAttribute("diary:seers:tower-archers", 0) == 15) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 6); - } - } - } - } @Override public AbstractNPC construct(int id, Location location, Object... objects) { diff --git a/Server/src/main/java/core/game/content/activity/partyroom/BalloonManager.java b/Server/src/main/java/core/game/content/activity/partyroom/BalloonManager.java index 8bbd52077..af26ab59e 100644 --- a/Server/src/main/java/core/game/content/activity/partyroom/BalloonManager.java +++ b/Server/src/main/java/core/game/content/activity/partyroom/BalloonManager.java @@ -244,9 +244,6 @@ public final class BalloonManager extends OptionHandler { getBalloons().remove(object); player.animate(Animation.create(10017)); - // Pop a party balloon - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 0, 12); - GameWorld.getPulser().submit(new Pulse(1) { int counter; diff --git a/Server/src/main/java/core/game/content/activity/stronghold/StrongHoldSecurityPlugin.java b/Server/src/main/java/core/game/content/activity/stronghold/StrongHoldSecurityPlugin.java index 50227a414..f80105131 100644 --- a/Server/src/main/java/core/game/content/activity/stronghold/StrongHoldSecurityPlugin.java +++ b/Server/src/main/java/core/game/content/activity/stronghold/StrongHoldSecurityPlugin.java @@ -121,7 +121,6 @@ public final class StrongHoldSecurityPlugin extends MapZone implements Plugin
Edgeville - if (destination.equals(3096, 9867, 0)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 0, 10); - } - - // Find the highest point in Varrock - if (destination.equals(3206, 3497, 3) || destination.equals(3223, 3472, 3) || destination.equals(3258, 3486, 3)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 0, 18); - } - return true; } }); diff --git a/Server/src/main/java/core/game/ge/GEGuidePrice.java b/Server/src/main/java/core/game/ge/GEGuidePrice.java index b077d94ce..09736376a 100644 --- a/Server/src/main/java/core/game/ge/GEGuidePrice.java +++ b/Server/src/main/java/core/game/ge/GEGuidePrice.java @@ -3,7 +3,7 @@ package core.game.ge; import core.game.component.Component; import core.game.node.entity.player.Player; import core.tools.StringUtils; -import rs09.game.ge.OfferManager; +import rs09.game.ge.GrandExchange; /** * Represents the glass used to open the guide prices for the different type of @@ -140,7 +140,7 @@ public final class GEGuidePrice { player.getPacketDispatch().sendInterfaceConfig(642, i, false); } for (GuideItem item : getItems()) { - player.getPacketDispatch().sendString("" + OfferManager.getRecommendedPrice(item.item, false) + " gp", COMPONENT.getId(), item.getChildData()[0]); + player.getPacketDispatch().sendString("" + GrandExchange.getRecommendedPrice(item.item, false) + " gp", COMPONENT.getId(), item.getChildData()[0]); } } } diff --git a/Server/src/main/java/core/game/interaction/inter/GrandExchangeInterface.java b/Server/src/main/java/core/game/interaction/inter/GrandExchangeInterface.java index c8ebdf9fc..64260ca90 100644 --- a/Server/src/main/java/core/game/interaction/inter/GrandExchangeInterface.java +++ b/Server/src/main/java/core/game/interaction/inter/GrandExchangeInterface.java @@ -8,10 +8,8 @@ import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; import core.game.ge.GEGuidePrice; import core.game.ge.GEItemSet; -import core.game.ge.GrandExchangeDatabase; import core.game.ge.OfferState; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.system.task.Pulse; @@ -21,9 +19,10 @@ import core.net.packet.out.ContainerPacket; import core.plugin.Initializable; import core.plugin.Plugin; import kotlin.Unit; +import rs09.game.ge.GrandExchange; import rs09.game.ge.GrandExchangeOffer; -import rs09.game.ge.OfferManager; -import rs09.game.ge.PlayerGrandExchange; +import rs09.game.ge.GrandExchangeRecords; +import rs09.game.interaction.inter.ge.StockMarket; import rs09.game.interaction.npc.BogrogPouchSwapper; import rs09.game.world.GameWorld; @@ -37,8 +36,8 @@ public class GrandExchangeInterface extends ComponentPlugin { @Override public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(105, this); // Main interface - ComponentDefinition.put(107, this); // Selling tab + //ComponentDefinition.put(105, this); // Main interface + //ComponentDefinition.put(107, this); // Selling tab ComponentDefinition.put(109, this); // Collection interface ComponentDefinition.put(389, this); // Search interface ComponentDefinition.put(644, this); // Item sets inventory interface @@ -47,64 +46,26 @@ public class GrandExchangeInterface extends ComponentPlugin { return this; } - /** - * Increments the opened grand exchange offer amount. - * @param player The player. - * @param amount The amount to increment. - */ - @SuppressWarnings("deprecation") - private void setOfferAmount(Player player, GrandExchangeOffer offer, int amount) { - if (offer == null || amount < 0 || offer.getOfferState() != OfferState.PENDING) { - return; - } - offer.setAmount(amount); - player.getConfigManager().send(1110, offer.getAmount()); - } - - /** - * Increments the opened grand exchange offer amount. - * @param player The player. - */ - @SuppressWarnings("deprecation") - private void setOfferValue(Player player, GrandExchangeOffer offer, int value) { - if (offer == null || value < 1 || offer.getOfferState() != OfferState.PENDING) { - return; - } - if (value == OfferManager.getRecommendedPrice(offer.getItemID(), false)) { - player.getAudioManager().send(new Audio(4043, 1, 1)); - } else if (value > offer.getOfferedValue()) { - player.getAudioManager().send(new Audio(4041, 1, 1)); - } else { - player.getAudioManager().send(new Audio(4045, 1, 1)); - } - offer.setOfferedValue(value); - player.varpManager.get(1111).setVarbit(0,offer.getOfferedValue()).send(player); - } - @Override public boolean handle(final Player player, final Component component, final int opcode, final int button, final int slot, final int itemId) { GameWorld.getPulser().submit(new Pulse(1, player) { @Override public boolean pulse() { switch (component.getId()) { - case 644: - case 645: - handleItemSet(player, component, opcode, button, slot, itemId); - return true; - case 389: - handleSearchInterface(player, opcode, button, slot, itemId); - return true; - case 107: - handleSellingTab(player, opcode, button, slot, itemId); - return true; - case 109: - handleCollectionBox(player, opcode, button, slot, itemId); - return true; - case 642: - handleGuidePrice(player, opcode, button, slot, itemId); - return true; + case 644: + case 645: + handleItemSet(player, component, opcode, button, slot, itemId); + return true; + case 389: + handleSearchInterface(player, opcode, button, slot, itemId); + return true; + case 109: + handleCollectionBox(player, opcode, button, slot, itemId); + return true; + case 642: + handleGuidePrice(player, opcode, button, slot, itemId); + return true; } - handleMainInterface(player, opcode, button, slot, itemId); return true; } }); @@ -122,37 +83,9 @@ public class GrandExchangeInterface extends ComponentPlugin { */ public boolean handleSearchInterface(final Player player, int opcode, int button, int slot, int itemId) { switch (button) { - case 10: - player.getInterfaceManager().closeChatbox(); - return true; - } - return false; - } - - /** - * Handles the selling tab interface options. - * @param player The player. - * @param opcode The packet opcode. - * @param button The button id. - * @param slot The slot. - * @param itemId The item id. - * @return {@code true} if the option got handled. - */ - public boolean handleSellingTab(final Player player, int opcode, int button, int slot, int itemId) { - if (button != 18 || slot < 0 || slot > 27) { - return false; - } - Item item = player.getInventory().get(slot); - if (item == null) { - return false; - } - switch (opcode) { - case 196: - player.getPacketDispatch().sendMessage(item.getDefinition().getExamine()); - return true; - case 155: - player.getPlayerGrandExchange().constructSale(item); - return true; + case 10: + player.getInterfaceManager().closeChatbox(); + return true; } return false; } @@ -169,20 +102,20 @@ public class GrandExchangeInterface extends ComponentPlugin { public boolean handleCollectionBox(final Player player, int opcode, int button, int slot, int itemId) { int index = -1; switch (button) { - case 18: - case 23: - case 28: - index = (button - 18) >> 2; - break; - case 36: - case 44: - case 52: - index = 3 + ((button - 36) >> 3); - break; + case 18: + case 23: + case 28: + index = (button - 18) >> 2; + break; + case 36: + case 44: + case 52: + index = 3 + ((button - 36) >> 3); + break; } GrandExchangeOffer offer; - if (index > -1 && (offer = player.getPlayerGrandExchange().getOffers()[index]) != null) { - player.getPlayerGrandExchange().withdraw(offer, slot >> 1); + if (index > -1 && (offer = player.getExchangeRecords().getOffer(player.getExchangeRecords().getOfferRecords()[index])) != null) { + StockMarket.withdraw(player, offer, slot >> 1); } return true; } @@ -213,192 +146,44 @@ public class GrandExchangeInterface extends ComponentPlugin { return; } switch (opcode) { - case 124: - player.getPacketDispatch().sendMessage(item.getDefinition().getExamine()); - break; - case 196: - if (inventory) { - if (player.getInventory().freeSlots() < set.getComponents().length - 1) { - player.getPacketDispatch().sendMessage("You don't have enough inventory space for the component parts."); - return; + case 124: + player.getPacketDispatch().sendMessage(item.getDefinition().getExamine()); + break; + case 196: + if (inventory) { + if (player.getInventory().freeSlots() < set.getComponents().length - 1) { + player.getPacketDispatch().sendMessage("You don't have enough inventory space for the component parts."); + return; + } + if (!player.getInventory().remove(item, false)) { + return; + } + for (int id : set.getComponents()) { + player.getInventory().add(new Item(id, 1)); + } + player.getInventory().refresh(); + player.getPacketDispatch().sendMessage("You successfully traded your set for its component items!"); + } else { + if (!player.getInventory().containItems(set.getComponents())) { + player.getPacketDispatch().sendMessage("You don't have the parts that make up this set."); + break; + } + for (int id : set.getComponents()) { + player.getInventory().remove(new Item(id, 1), false); + } + player.getInventory().add(item); + player.getInventory().refresh(); + player.getPacketDispatch().sendMessage("You successfully traded your item components for a set!"); } - if (!player.getInventory().remove(item, false)) { - return; - } - for (int id : set.getComponents()) { - player.getInventory().add(new Item(id, 1)); - } - player.getInventory().refresh(); - player.getPacketDispatch().sendMessage("You successfully traded your set for its component items!"); - } else { - if (!player.getInventory().containItems(set.getComponents())) { - player.getPacketDispatch().sendMessage("You don't have the parts that make up this set."); - break; - } - for (int id : set.getComponents()) { - player.getInventory().remove(new Item(id, 1), false); - } - player.getInventory().add(item); - player.getInventory().refresh(); - player.getPacketDispatch().sendMessage("You successfully traded your item components for a set!"); - } - player.getAudioManager().send(new Audio(4044, 1, 1)); - PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, player.getAttribute("container-key", 93), player.getInventory(), false)); - break; - case 155: - player.getPacketDispatch().sendMessage((String) CS2Mapping.forId(1089).getMap().get(set.getItemId())); - break; + player.getAudioManager().send(new Audio(4044, 1, 1)); + PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, player.getAttribute("container-key", 93), player.getInventory(), false)); + break; + case 155: + player.getPacketDispatch().sendMessage((String) CS2Mapping.forId(1089).getMap().get(set.getItemId())); + break; } } - /** - * Handles the main interface options. - * @param player The player. - * @param opcode The packet opcode. - * @param button The button id. - * @param slot The slot. - * @param itemId The item id. - * @return {@code true} if the option got handled. - */ - public boolean handleMainInterface(final Player player, int opcode, int button, int slot, int itemId) { - final GrandExchangeOffer offer = player.getPlayerGrandExchange().getTemporaryOffer(); - final GrandExchangeOffer opened = player.getPlayerGrandExchange().getOpenedOffer(); - int amount = offer == null ? 0 : offer.getAmount(); - switch (button) { - case 209: - case 211: - if (opened == null) { - return false; - } - player.getPlayerGrandExchange().withdraw(opened, (button - 209) >> 1); - return true; - case 190: - player.getPlayerGrandExchange().confirmOffer(); - player.getPlayerGrandExchange().setTemporaryOffer(null); - return true; - case 194: - player.getPlayerGrandExchange().openSearch(); - return true; - case 203: - if (opened == null) { - return false; - } - player.getPlayerGrandExchange().abort(opened.getIndex()); - return true; - case 18: - case 34: - case 50: - case 69: - case 88: - case 107: - if (opcode == 205) { - player.getPlayerGrandExchange().abort((button - 18) >> 4); - return true; - } - player.getPlayerGrandExchange().view((button - 18) >> 4); - return true; - case 30: - case 46: - case 62: - case 81: - case 100: - case 119: - player.getPlayerGrandExchange().openBuy((button - 30) >> 4); - return true; - case 31: - case 47: - case 63: - case 82: - case 101: - case 120: - player.getPlayerGrandExchange().openSell((button - 31) >> 4); - return true; - case 157: // -1 - setOfferAmount(player, offer, amount - 1); - return true; - case 159: // +1 - setOfferAmount(player, offer, amount + 1); - return true; - case 162: // 1 - setOfferAmount(player, offer, offer != null && offer.getSell() ? 1 : amount + 1); - return true; - case 164: // 10 - setOfferAmount(player, offer, offer != null && offer.getSell() ? 10 : amount + 10); - return true; - case 166: // 100 - setOfferAmount(player, offer, offer != null && offer.getSell() ? 100 : amount + 100); - return true; - case 168: // 1000 / sell all - if (offer != null && offer.getSell()) { - setOfferAmount(player, offer, PlayerGrandExchange.getInventoryAmount(player, offer.getItemID())); - return true; - } - setOfferAmount(player, offer, amount + 1000); - return true; - case 170: // value x - sendInputDialogue(player, false, "Enter the amount:", (value) -> { - if (player.getInterfaceManager().getChatbox().getId() == 389) { - player.getPlayerGrandExchange().openSearch(); - } - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - s = s.replace("m","000000"); - s = s.replace("M","000000"); - setOfferAmount(player, offer, Integer.parseInt(s)); - return Unit.INSTANCE; - }); - return false; - case 180: - if (offer != null) { - setOfferValue(player, offer, OfferManager.getRecommendedPrice(offer.getItemID(), false)); - return true; - } - return false; - case 177: // mid - 5% value - case 183: // mid + 5% value - if (offer != null) { - setOfferValue(player, offer, (int) (OfferManager.getRecommendedPrice(offer.getItemID(), false) * (button == 177 ? 0.95 : 1.05))); - return true; - } - return false; - case 171: // Decrease value by 1 - case 173: // Increase value with 1 - if (offer != null) { - setOfferValue(player, offer, offer.getOfferedValue() + (button == 171 ? -1 : 1)); - return true; - } - return false; - case 185: // Set value x - if (offer == null) { - player.getPacketDispatch().sendMessage("Please select an offer first."); - return true; - } - sendInputDialogue(player, false, "Enter the amount:", (value) -> { - if (player.getInterfaceManager().getChatbox().getId() == 389) { - player.getPlayerGrandExchange().openSearch(); - } - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - s = s.replace("m","000000"); - s = s.replace("M","000000"); - setOfferValue(player, offer, Integer.parseInt(s)); - return Unit.INSTANCE; - }); - return false; - case 195: - player.getInterfaceManager().close(); - return true; - case 127: - player.getPlayerGrandExchange().setTemporaryOffer(null); - player.getInterfaceManager().closeSingleTab(); - player.getInterfaceManager().closeChatbox(); - return true; - } - return false; - } - /*** * Method used to handle the guide price opcode. * @param player the player. @@ -409,29 +194,29 @@ public class GrandExchangeInterface extends ComponentPlugin { */ private void handleGuidePrice(final Player player, final int opcode, final int buttonId, final int slot, final int itemId) { switch (opcode) { - case 155: - GEGuidePrice.GuideType type = player.getAttribute("guide-price", null); - if (type == null) { - return; - } - int subtract = 0; - if (buttonId >= 15 && buttonId <= 23) { - subtract = 15; - } - if (buttonId >= 43 && buttonId <= 57) { - subtract = 43; - } - if (buttonId >= 89 && buttonId <= 103) { - subtract = 89; - } - if (buttonId >= 135 && buttonId <= 144) { - subtract = 135; - } - if (buttonId >= 167 && buttonId <= 182) { - subtract = 167; - } - player.getPacketDispatch().sendMessage(ItemDefinition.forId(type.getItems()[buttonId - subtract].getItem()).getExamine()); - break; + case 155: + GEGuidePrice.GuideType type = player.getAttribute("guide-price", null); + if (type == null) { + return; + } + int subtract = 0; + if (buttonId >= 15 && buttonId <= 23) { + subtract = 15; + } + if (buttonId >= 43 && buttonId <= 57) { + subtract = 43; + } + if (buttonId >= 89 && buttonId <= 103) { + subtract = 89; + } + if (buttonId >= 135 && buttonId <= 144) { + subtract = 135; + } + if (buttonId >= 167 && buttonId <= 182) { + subtract = 167; + } + player.getPacketDispatch().sendMessage(ItemDefinition.forId(type.getItems()[buttonId - subtract].getItem()).getExamine()); + break; } } } diff --git a/Server/src/main/java/core/game/interaction/npc/NPCTradePlugin.kt b/Server/src/main/java/core/game/interaction/npc/NPCTradePlugin.kt index b1a67f4c6..72928510e 100644 --- a/Server/src/main/java/core/game/interaction/npc/NPCTradePlugin.kt +++ b/Server/src/main/java/core/game/interaction/npc/NPCTradePlugin.kt @@ -3,6 +3,7 @@ package core.game.interaction.npc import api.* import core.cache.def.impl.NPCDefinition import core.game.component.Component +import core.game.content.dialogue.FacialExpression import core.plugin.Initializable import core.game.interaction.OptionHandler import core.game.node.Node @@ -12,6 +13,7 @@ import core.plugin.Plugin import core.game.node.entity.skill.crafting.TanningProduct import org.rs09.consts.NPCs import rs09.game.interaction.InteractionListener +import rs09.tools.END_DIALOGUE /** * Represents the plugin used for an npc with the trade option. @@ -42,6 +44,24 @@ class NPCTradePlugin : InteractionListener() { node.asNpc().openShop(player) return@on true } + + on(NPCs.FUR_TRADER_1316, NPC,"trade") { player, node -> + if (!isQuestComplete(player, "Fremennik Trials")) { + sendNPCDialogue(player, NPCs.FUR_TRADER_1316, "I don't sell to outerlanders.", FacialExpression.ANNOYED).also { END_DIALOGUE } + } else { + END_DIALOGUE.also { node.asNpc().openShop(player) } + } + return@on true + } + + on(NPCs.FISH_MONGER_1315, NPC,"trade") { player, node -> + if (!isQuestComplete(player, "Fremennik Trials")) { + sendNPCDialogue(player, NPCs.FISH_MONGER_1315, "I don't sell to outerlanders.", FacialExpression.ANNOYED).also { END_DIALOGUE } + } else { + END_DIALOGUE.also { node.asNpc().openShop(player) } + } + return@on true + } } override fun defineDestinationOverrides() { 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 60ecc5a96..eead402be 100644 --- a/Server/src/main/java/core/game/interaction/object/BankingPlugin.java +++ b/Server/src/main/java/core/game/interaction/object/BankingPlugin.java @@ -32,8 +32,8 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; import kotlin.Unit; -import org.rs09.consts.Items; -import rs09.game.content.dialogue.DumpContainer; +import rs09.game.content.dialogue.DepositAllDialogue; +import rs09.game.ge.GrandExchangeRecords; import rs09.game.ge.GrandExchangeOffer; import rs09.game.world.GameWorld; @@ -92,7 +92,7 @@ public final class BankingPlugin extends OptionHandler { checkAchievements(player); return true; case "collect": - player.getPlayerGrandExchange().openCollectionBox(); + player.getExchangeRecords().openCollectionBox(); return true; case "deposit": openDepositBox(player); @@ -174,7 +174,8 @@ public final class BankingPlugin extends OptionHandler { return true; } interpreter.sendDialogues(id, FacialExpression.HALF_GUILTY, "Good day, How may I help you?"); - for (GrandExchangeOffer o : player.getPlayerGrandExchange().getOffers()) { + for (GrandExchangeRecords.OfferRecord r : player.getExchangeRecords().getOfferRecords()) { + GrandExchangeOffer o = player.getExchangeRecords().getOffer(r); if (o != null && (o.getWithdraw()[0] != null || o.getWithdraw()[1] != null)) { stage = -1; break; @@ -428,7 +429,7 @@ public final class BankingPlugin extends OptionHandler { case 762: switch (button) { case 18: - p.getDialogueInterpreter().open(new DumpContainer().getID()); + p.getDialogueInterpreter().open(new DepositAllDialogue().getID()); return true; case 23: p.getDialogueInterpreter().sendOptions("Select an Option", "Check bank value", "Banking assistance", "Close"); @@ -675,7 +676,7 @@ public final class BankingPlugin extends OptionHandler { player.getBank().open(); checkAchievements(player); } else { - player.getPlayerGrandExchange().openCollectionBox(); + player.getExchangeRecords().openCollectionBox(); } return true; } diff --git a/Server/src/main/java/core/game/interaction/object/GrandExchangePlugin.java b/Server/src/main/java/core/game/interaction/object/GrandExchangePlugin.java index f1fa68c4d..5ac8f57de 100644 --- a/Server/src/main/java/core/game/interaction/object/GrandExchangePlugin.java +++ b/Server/src/main/java/core/game/interaction/object/GrandExchangePlugin.java @@ -12,6 +12,8 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.world.map.Location; import core.plugin.Plugin; +import rs09.game.interaction.inter.ge.ExchangeItemSets; +import rs09.game.interaction.inter.ge.StockMarket; /** * Represents the plugin used for grand exchange npc and object options. @@ -56,17 +58,17 @@ public final class GrandExchangePlugin extends OptionHandler { player.getDialogueInterpreter().sendDialogue("The Grand Exchange desk seems to be closed..."); break; } - player.getPlayerGrandExchange().open(); + StockMarket.openFor(player); break; case "history": - player.getPlayerGrandExchange().openHistoryLog(player); + player.getExchangeRecords().openHistoryLog(player); break; case "collect": if (!GrandExchangeDatabase.hasInitialized()) { player.getDialogueInterpreter().sendDialogue("The Grand Exchange desk seems to be closed..."); break; } - player.getPlayerGrandExchange().openCollectionBox(); + player.getExchangeRecords().openCollectionBox(); break; case "info-logs": GEGuidePrice.open(player, GuideType.LOGS); @@ -84,7 +86,7 @@ public final class GrandExchangePlugin extends OptionHandler { GEGuidePrice.open(player, GuideType.WEAPONS_AND_ARMOUR); break; case "sets": - player.getPlayerGrandExchange().openItemSets(); + ExchangeItemSets.openFor(player); break; } return true; @@ -137,7 +139,7 @@ public final class GrandExchangePlugin extends OptionHandler { player.getBank().open(); break; case "collect": - player.getPlayerGrandExchange().openCollectionBox(); + player.getExchangeRecords().openCollectionBox(); break; } return true; diff --git a/Server/src/main/java/core/game/interaction/object/LumbridgeBasementPlugin.java b/Server/src/main/java/core/game/interaction/object/LumbridgeBasementPlugin.java index b568be5c8..b1fa523cd 100644 --- a/Server/src/main/java/core/game/interaction/object/LumbridgeBasementPlugin.java +++ b/Server/src/main/java/core/game/interaction/object/LumbridgeBasementPlugin.java @@ -55,7 +55,7 @@ public class LumbridgeBasementPlugin extends OptionHandler { SceneryDefinition.forId(40849).getHandlers().put("option:jump-down", this); SceneryDefinition.forId(40260).getHandlers().put("option:climb-through", this); SceneryDefinition.forId(41077).getHandlers().put("option:crawl-through", this); - PluginManager.definePlugins(new LightCreatureNPC(), new LightCreatureHandler(), new FishMongerDialogue()); + PluginManager.definePlugins(new LightCreatureNPC(), new LightCreatureHandler()); SceneryBuilder.add(new Scenery(40260, Location.create(2526, 5828, 2), 2)); return this; } @@ -256,69 +256,4 @@ public class LumbridgeBasementPlugin extends OptionHandler { } } - - /** - * Handles the fish monger dialogue. - * @author Vexia - * - */ - public class FishMongerDialogue extends DialoguePlugin { - - /** - * Constructs the {@code FishMongerDialogue} - */ - public FishMongerDialogue() { - /* - * empty. - */ - } - - /** - * Constructs the {@code FishMongerDialogue} - */ - public FishMongerDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FishMongerDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("Hello, " + player.getUsername() + " would you like to look at", "my wares?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("Yes, please.", "No, thanks."); - stage++; - break; - case 1: - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - end(); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] {1369}; - } - - } } diff --git a/Server/src/main/java/core/game/interaction/object/SlashWebPlugin.java b/Server/src/main/java/core/game/interaction/object/SlashWebPlugin.java index 6824d7f93..3ee3a14b7 100644 --- a/Server/src/main/java/core/game/interaction/object/SlashWebPlugin.java +++ b/Server/src/main/java/core/game/interaction/object/SlashWebPlugin.java @@ -12,6 +12,7 @@ import core.game.node.entity.combat.equipment.WeaponInterface.AttackStyle; import core.game.node.entity.combat.equipment.WeaponInterface.WeaponInterfaces; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; +import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; @@ -73,6 +74,7 @@ public final class SlashWebPlugin extends OptionHandler { } final boolean success = RandomFunction.random(2) == 1; player.lock(2); + player.getAudioManager().send(new Audio(2548)); player.animate(weapon == KNIFE ? KNIFE_ANIMATION : ANIMATION); if (success) { player.getPacketDispatch().sendMessage("You slash the web apart."); diff --git a/Server/src/main/java/core/game/node/entity/Entity.java b/Server/src/main/java/core/game/node/entity/Entity.java index 311013efe..c01cb1a36 100644 --- a/Server/src/main/java/core/game/node/entity/Entity.java +++ b/Server/src/main/java/core/game/node/entity/Entity.java @@ -1,5 +1,7 @@ package core.game.node.entity; +import api.events.Event; +import api.events.EventHook; import core.game.content.holiday.HolidayEvent; import core.game.interaction.DestinationFlag; import core.game.node.Node; @@ -28,6 +30,7 @@ import core.game.world.update.flag.context.Graphics; import rs09.game.node.entity.combat.CombatSwingHandler; import rs09.game.world.update.UpdateMasks; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -107,6 +110,13 @@ public abstract class Entity extends Node { */ private final ActionLocks locks = new ActionLocks(); + + /** + * The mapping of event types to event hooks + */ + private HashMap, ArrayList> hooks = new HashMap<>(); + + /** * If the entity is invisible. */ @@ -145,6 +155,54 @@ public abstract class Entity extends Node { } } + /** + * Dispatches an event to this entity's event hooks + */ + public void dispatch(Event event) + { + if(this.hooks.containsKey(event.getClass())) + { + this.hooks.get(event.getClass()).forEach((hook) -> { hook.process(this, event); }); + } + } + + /** + * Unhooks an eventhook from this entity + */ + public void unhook(EventHook hook) + { + GameWorld.getPulser().submit(new Pulse() { + @Override + public boolean pulse() { + for(ArrayList s : hooks.values()) s.remove(hook); + return true; + } + }); + } + + /** + * Hooks an eventhook to this entity + */ + public void hook(Event event, EventHook hook) + { + hook(event.getClass(), hook); + } + + public void hook(Class event, EventHook hook) + { + ArrayList hookList; + if(hooks.get(event) != null) + { + hookList = hooks.get(event); + } + else + { + hookList = new ArrayList(); + } + hookList.add(hook); + hooks.put(event, hookList); + } + /** * Initializes the entity. */ diff --git a/Server/src/main/java/core/game/node/entity/combat/special/ClobberSpecialHandler.java b/Server/src/main/java/core/game/node/entity/combat/special/ClobberSpecialHandler.java index ee8969075..dfe5723ce 100644 --- a/Server/src/main/java/core/game/node/entity/combat/special/ClobberSpecialHandler.java +++ b/Server/src/main/java/core/game/node/entity/combat/special/ClobberSpecialHandler.java @@ -1,6 +1,5 @@ package core.game.node.entity.combat.special; -import core.plugin.Initializable; import core.game.node.entity.skill.Skills; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -10,13 +9,12 @@ import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; +import core.plugin.Initializable; import core.plugin.Plugin; -import core.tools.RandomFunction; /** - * Represents the dragon axe's clobbrer special handler. - * @author Emperor - * @version 1.0 + * Handles the Dragon axe special attack. + * @author Crash */ @Initializable public final class ClobberSpecialHandler extends MeleeSwingHandler implements Plugin { @@ -36,8 +34,14 @@ public final class ClobberSpecialHandler extends MeleeSwingHandler implements Pl */ private static final Graphics GRAPHIC = new Graphics(479, 96); + @Override public Object fireEvent(String identifier, Object... args) { + switch (identifier) { + case "instant_spec": + case "ncspec": + return true; + } return null; } @@ -49,29 +53,13 @@ public final class ClobberSpecialHandler extends MeleeSwingHandler implements Pl @Override public int swing(Entity entity, Entity victim, BattleState state) { - if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { + Player p = (Player) entity; + if (!p.getSettings().drainSpecial(SPECIAL_ENERGY)) return -1; - } - state.setStyle(CombatStyle.MELEE); - int hit = 0; - if (entity instanceof Player) { - entity.asPlayer().sendChat("Chop chop!"); - } - if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.05, 1.0)) { - hit = RandomFunction.random(calculateHit(entity, victim, 1.0)); - if (hit > 9) { - int amount = hit / 10; - victim.getSkills().updateLevel(Skills.DEFENCE, -amount, 0); - victim.getSkills().updateLevel(Skills.MAGIC, -amount, 0); - } - } - entity.getSkills().updateLevel(Skills.WOODCUTTING, 3); - state.setEstimatedHit(hit); - return 1; + p.sendChat("Chop chop!"); + p.visualize(ANIMATION, GRAPHIC); + p.getSkills().updateLevel(Skills.WOODCUTTING, 3, p.getSkills().getStaticLevel(Skills.WOODCUTTING) + 3); + return -1; } - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - entity.visualize(ANIMATION, GRAPHIC); - } } diff --git a/Server/src/main/java/core/game/node/entity/combat/special/SeercullSpecialHandler.java b/Server/src/main/java/core/game/node/entity/combat/special/SeercullSpecialHandler.java new file mode 100644 index 000000000..8f9961017 --- /dev/null +++ b/Server/src/main/java/core/game/node/entity/combat/special/SeercullSpecialHandler.java @@ -0,0 +1,80 @@ +package core.game.node.entity.combat.special; + +import core.game.node.entity.skill.Skills; +import core.game.node.entity.Entity; +import core.game.node.entity.combat.BattleState; +import core.game.node.entity.combat.CombatStyle; +import rs09.game.node.entity.combat.handlers.RangeSwingHandler; +import core.game.node.entity.impl.Projectile; +import core.game.node.entity.player.Player; +import core.game.world.update.flag.context.Graphics; +import core.plugin.Plugin; +import core.plugin.Initializable; +import core.tools.RandomFunction; + +/** + * Represents the Seercull's special attack which lowers the opponent's magic + * level. + * @author Splinter + * @version 1.0 + */ +@Initializable +public final class SeercullSpecialHandler extends RangeSwingHandler implements Plugin { + + /** + * The special energy required. + */ + private static final int SPECIAL_ENERGY = 100; + + /** + * The attack animation. + */ + private static final Graphics DRAWBACK_GFX = new Graphics(472, 96); + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + CombatStyle.RANGE.getSwingHandler().register(6724, this); + return this; + } + + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + Player p = (Player) entity; + configureRangeData(p, state); + if (state.getWeapon() == null || !Companion.hasAmmo(entity, state)) { + entity.getProperties().getCombatPulse().stop(); + p.getSettings().toggleSpecialBar(); + return -1; + } + if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { + return -1; + } + int hit = 0; + if (isAccurateImpact(entity, victim, CombatStyle.RANGE, 1.05, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.0)); + victim.getSkills().updateLevel(Skills.MAGIC, -hit, 0); + } + Companion.useAmmo(entity, state, victim.getLocation()); + state.setEstimatedHit(hit); + return 1; + } + + @Override + public void visualize(Entity entity, Entity victim, BattleState state) { + victim.graphics(new Graphics(474)); + int speed = (int) (35 + (entity.getLocation().getDistance(victim.getLocation()) * 10)); + entity.visualize(entity.getProperties().getAttackAnimation(), DRAWBACK_GFX); + Projectile.create(entity, victim, 473, 40, 40, 40, speed, 15, 11).send(); + } + + @Override + public void impact(final Entity entity, final Entity victim, final BattleState state) { + int hit = state.getEstimatedHit(); + victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.RANGE, state); + } +} diff --git a/Server/src/main/java/core/game/node/entity/combat/special/WeakenSpecialHandler.java b/Server/src/main/java/core/game/node/entity/combat/special/WeakenSpecialHandler.java new file mode 100644 index 000000000..c4f6cca8c --- /dev/null +++ b/Server/src/main/java/core/game/node/entity/combat/special/WeakenSpecialHandler.java @@ -0,0 +1,78 @@ +package core.game.node.entity.combat.special; + +import core.game.node.entity.skill.Skills; +import core.game.node.entity.Entity; +import core.game.node.entity.combat.BattleState; +import core.game.node.entity.combat.CombatStyle; +import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; +import core.game.node.entity.impl.Animator.Priority; +import core.game.node.entity.player.Player; +import core.game.node.entity.player.link.audio.Audio; +import core.game.world.update.flag.context.Animation; +import core.game.world.update.flag.context.Graphics; +import core.plugin.Plugin; +import core.plugin.Initializable; +import core.tools.RandomFunction; + +/** + * Handles Darklight's special attack, Weaken. + * @author Crash, based on Emperor's code for other special attacks + */ +@Initializable +public final class WeakenSpecialHandler extends MeleeSwingHandler implements Plugin { + + /** + * The special energy required. + */ + private static final int SPECIAL_ENERGY = 50; + + /** + * The attack animation. + */ + private static final Animation ANIMATION = new Animation(2890, Priority.HIGH); + + /** + * The graphic. + */ + private static final Graphics GRAPHIC = new Graphics(483); + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + CombatStyle.MELEE.getSwingHandler().register(6746, this); + return this; + } + + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { + return -1; + } + state.setStyle(CombatStyle.MELEE); + int hit = 0; + if (isAccurateImpact(entity, victim, CombatStyle.MELEE, 1.0, 1.0)) { + hit = RandomFunction.random(calculateHit(entity, victim, 1.0)); + } + state.setEstimatedHit(hit); + if (victim instanceof Player) { + ((Player) victim).getPacketDispatch().sendMessage("You have been drained."); + } + int lower = (int) (victim.getSkills().getLevel(Skills.DEFENCE) * 0.05); + victim.getSkills().updateLevel(Skills.DEFENCE, -lower, 0); + int lower2 = (int) (victim.getSkills().getLevel(Skills.ATTACK) * 0.05); + victim.getSkills().updateLevel(Skills.ATTACK, -lower2, 0); + int lower3 = (int) (victim.getSkills().getLevel(Skills.STRENGTH) * 0.05); + victim.getSkills().updateLevel(Skills.STRENGTH, -lower3, 0); + return hit; + } + + @Override + public void visualize(Entity entity, Entity victim, BattleState state) { + entity.visualize(ANIMATION, GRAPHIC); + entity.asPlayer().getAudioManager().send(new Audio(225), true); + } +} diff --git a/Server/src/main/java/core/game/node/entity/npc/NPC.java b/Server/src/main/java/core/game/node/entity/npc/NPC.java index 7174eefe4..a7368a983 100644 --- a/Server/src/main/java/core/game/node/entity/npc/NPC.java +++ b/Server/src/main/java/core/game/node/entity/npc/NPC.java @@ -1,5 +1,6 @@ package core.game.node.entity.npc; +import api.events.NPCKillEvent; import core.cache.def.impl.NPCDefinition; import core.game.content.dialogue.DialoguePlugin; import core.game.content.global.shop.Shop; @@ -442,17 +443,9 @@ public class NPC extends Entity { if(walkRadius == 0) walkRadius = 3; } - getPulseManager().run(new MovementPulse(this, getProperties().getSpawnLocation(), Pathfinder.SMART) { - @Override - public boolean pulse() { - return true; - } - }, "movement"); if (aggressiveHandler != null) { aggressiveHandler.setPauseTicks(walkRadius + 1); } - nextWalk = GameWorld.getTicks() + walkRadius + 1; - return; } if (aggressive && aggressiveHandler != null && aggressiveHandler.selectTarget()) { return; @@ -527,12 +520,6 @@ public class NPC extends Entity { if (getZoneMonitor().handleDeath(killer)) { return; } - if (task != null && killer instanceof Player && ((Player) killer).getSlayer().getTask() == task && ((Player) killer).getSlayer().hasTask()) { - ((Player) killer).getSlayer().finalizeDeath(killer.asPlayer(), this); - } - if (killer instanceof Player && killer.getAttribute("jobs:id",null) != null) { - JobManager.handleDeath(id,(Player) killer); - } setRespawnTick(GameWorld.getTicks() + definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 17)); Player p = !(killer instanceof Player) ? null : (Player) killer; if (p != null) { @@ -543,6 +530,7 @@ public class NPC extends Entity { if (!isRespawn()) { clear(); } + killer.dispatch(new NPCKillEvent(this)); } /** @@ -717,7 +705,19 @@ public class NPC extends Entity { */ protected Location getMovementDestination() { if (!pathBoundMovement || movementPath == null || movementPath.length < 1) { - return getProperties().getSpawnLocation().transform(-5 + RandomFunction.random(getWalkRadius()), -5 + RandomFunction.random(getWalkRadius()), 0); + Location returnToSpawnLocation = getProperties().getSpawnLocation().transform(-5 + RandomFunction.random(getWalkRadius()), -5 + RandomFunction.random(getWalkRadius()), 0); + int dist = (int) Location.getDistance(location, returnToSpawnLocation); + int pathLimit = 15; + if(dist > pathLimit || dist < -pathLimit) + { + int diffX = returnToSpawnLocation.getX() - location.getX(); + int diffY = returnToSpawnLocation.getY() - location.getY(); + returnToSpawnLocation = location.transform( + Math.min(Math.max(-pathLimit, diffX), pathLimit), //Try to path no more than pathLimit and no less than -pathLimit tiles away in the X and Y directions + Math.min(Math.max(-pathLimit, diffY), pathLimit), + 0); + } + return returnToSpawnLocation; } Location l = movementPath[movementIndex++]; if (movementIndex == movementPath.length) { diff --git a/Server/src/main/java/core/game/node/entity/npc/drop/NPCDropTables.java b/Server/src/main/java/core/game/node/entity/npc/drop/NPCDropTables.java index 339e9ca3a..2746eb667 100644 --- a/Server/src/main/java/core/game/node/entity/npc/drop/NPCDropTables.java +++ b/Server/src/main/java/core/game/node/entity/npc/drop/NPCDropTables.java @@ -20,7 +20,7 @@ import rs09.game.ai.AIRepository; import rs09.game.ai.general.GeneralBotCreator; import rs09.game.content.global.GlobalKillCounter; import rs09.game.content.global.NPCDropTable; -import rs09.game.ge.OfferManager; +import rs09.game.ge.GrandExchange; import rs09.game.system.config.ItemConfigParser; import rs09.game.world.repository.Repository; @@ -162,7 +162,7 @@ public final class NPCDropTables { } } player.sendMessage(player.getInterfaceManager().isResizable()+""); - int price = item.getName().endsWith("charm") ? 100 : OfferManager.getRecommendedPrice(itemId, false); + int price = item.getName().endsWith("charm") ? 100 : GrandExchange.getRecommendedPrice(itemId, false); looter.getGlobalData().setLootSharePoints(looter.getGlobalData().getLootSharePoints() - (price) + ((price / looters.size()))); looter.sendMessage((player.getInterfaceManager().isResizable() ? "" : "") + "You received: " + item.getAmount() + " " + item.getName()); for (Player p : looters) { diff --git a/Server/src/main/java/core/game/node/entity/npc/other/CowNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/CowNPC.java index aeb831611..5b35b1c94 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/CowNPC.java +++ b/Server/src/main/java/core/game/node/entity/npc/other/CowNPC.java @@ -50,18 +50,6 @@ public final class CowNPC extends AbstractNPC { return new CowNPC(id, location); } - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - if (killer != null) { - Player player = killer.asPlayer(); - // Obtain a cow-hide from a cow in the field north-east of

Lumbridge - if (player.getViewport().getRegion().getId() == 12850 || player.getViewport().getRegion().getId() == 12851) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 1); - } - } - } - @Override public int[] getIds() { return new int[] { 81, 397, 955, 1766, 1767, 3309 }; diff --git a/Server/src/main/java/core/game/node/entity/npc/other/DuckNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/DuckNPC.java index 4c833c265..6b049e1dd 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/DuckNPC.java +++ b/Server/src/main/java/core/game/node/entity/npc/other/DuckNPC.java @@ -50,17 +50,6 @@ public final class DuckNPC extends AbstractNPC { return new DuckNPC(id, location); } - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - final Player player = killer.asPlayer(); - if (this.getLocation().withinDistance(new Location(2991,3383,0))) { - player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR,0, 9); - } - } - } - @Override public int[] getIds() { return new int[] { 46, 2693, 6113 }; diff --git a/Server/src/main/java/core/game/node/entity/npc/other/ElementalMonsterNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/ElementalMonsterNPC.java deleted file mode 100644 index 58012f6de..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/ElementalMonsterNPC.java +++ /dev/null @@ -1,74 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.game.node.entity.Entity; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.world.map.Location; -import core.plugin.Initializable; - -/** - * Represents the Earth, Fire, Air, and Water Elemental NPCs. - * @author afaroutdude - */ -@Initializable -public final class ElementalMonsterNPC extends AbstractNPC { - - /** - * Constructs a new {@code DuckNPC} {@code Object}. - */ - public ElementalMonsterNPC() { - super(0, null); - } - - /** - * Constructs a new {@code DuckNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public ElementalMonsterNPC(int id, Location location) { - super(id, location, true); - } - - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new ElementalMonsterNPC(id, location); - } - - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - final Player player = killer.asPlayer(); - if (this.getLocation().withinDistance(new Location(2719, 9889, 0), 100)) { - switch (this.getId()) { - case 1019: - player.setAttribute("/save:diary:seers:elemental:fire", true); - break; - case 1020: - player.setAttribute("/save:diary:seers:elemental:earth", true); - break; - case 1021: - player.setAttribute("/save:diary:seers:elemental:air", true); - break; - case 1022: - player.setAttribute("/save:diary:seers:elemental:water", true); - break; - } - if (player.getAttribute("diary:seers:elemental:fire", false) - && player.getAttribute("diary:seers:elemental:earth", false) - && player.getAttribute("diary:seers:elemental:air", false) - && player.getAttribute("diary:seers:elemental:water", false)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 4); - } - } - } - } - - @Override - public int[] getIds() { - return new int[] { 1019, 1020, 1021, 1022 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/JogreNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/JogreNPC.java deleted file mode 100644 index 37583de6a..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/JogreNPC.java +++ /dev/null @@ -1,63 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.game.node.entity.Entity; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.world.map.Location; -import core.plugin.Initializable; - -/** - * Represents a Jogre NPC. - * @author Vexia - */ -@Initializable -public class JogreNPC extends AbstractNPC { - - /** - * If the jogre is in the pothole zone. - */ - private boolean inPothole; - - /** - * Constructs a new {@code JogreNPC} {@code Object} - * @param id the id. - * @param location the location. - */ - public JogreNPC(int id, Location location) { - super(id, location); - } - - @Override - public void init() { - super.init(); - inPothole = this.getViewport().getRegion().getId() == 11412; - } - - /** - * Constructs a new {@code JogreNPC} {@code Object} - */ - public JogreNPC() { - this(-1, null); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new JogreNPC(id, location); - } - - @Override - public void finalizeDeath(Entity killer) { - if (inPothole && killer.isPlayer()) { - Player player = killer.asPlayer(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 0, 9); - } - super.finalizeDeath(killer); - } - - @Override - public int[] getIds() { - return new int[] { 113 }; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/LumbridgeSwampGiantRatNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/LumbridgeSwampGiantRatNPC.java deleted file mode 100644 index 1d669f90d..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/LumbridgeSwampGiantRatNPC.java +++ /dev/null @@ -1,58 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.game.node.entity.Entity; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.plugin.Initializable; -import core.game.world.map.Location; - -/** - * Represents a rat npc. - * @author afaroutdude - */ -@Initializable -public class LumbridgeSwampGiantRatNPC extends AbstractNPC { - - /** - * The NPC ids of NPCs using this plugin. - */ - private static final int[] ID = { 86 }; - - /** - * Constructs a new {@code RatNPC} {@code Object}. - */ - public LumbridgeSwampGiantRatNPC() { - super(0, null); - } - - /** - * Constructs a new {@code RatNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - private LumbridgeSwampGiantRatNPC(int id, Location location) { - super(id, location, true); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new LumbridgeSwampGiantRatNPC(id, location); - } - - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - - // Kill a giant rat in Lumbridge Swamp - if (killer instanceof Player && (killer.getViewport().getRegion().getId() == 12593 || killer.getViewport().getRegion().getId() == 12849)) { - killer.asPlayer().getAchievementDiaryManager().finishTask(killer.asPlayer(), DiaryType.LUMBRIDGE, 1, 7); - } - } - - @Override - public int[] getIds() { - return ID; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/npc/other/MetalDragonNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/MetalDragonNPC.java index c557cd54a..4e27ddea8 100644 --- a/Server/src/main/java/core/game/node/entity/npc/other/MetalDragonNPC.java +++ b/Server/src/main/java/core/game/node/entity/npc/other/MetalDragonNPC.java @@ -53,15 +53,6 @@ public final class MetalDragonNPC extends AbstractNPC { return combatAction; } - @Override - public void finalizeDeath(Entity killer) { - if (killer.isPlayer() && (killer.getViewport().getRegion().getId() == 10899 || killer.getViewport().getRegion().getId() == 10900)) { - Player player = killer.asPlayer(); - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 9); - } - super.finalizeDeath(killer); - } - @Override public int getDragonfireProtection(boolean fire) { return 0x2 | 0x4 | 0x8; diff --git a/Server/src/main/java/core/game/node/entity/npc/other/ZombieNPC.java b/Server/src/main/java/core/game/node/entity/npc/other/ZombieNPC.java deleted file mode 100644 index 510186043..000000000 --- a/Server/src/main/java/core/game/node/entity/npc/other/ZombieNPC.java +++ /dev/null @@ -1,56 +0,0 @@ -package core.game.node.entity.npc.other; - -import core.game.node.entity.Entity; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.plugin.Initializable; -import core.game.world.map.Location; - -/** - * Handles the zombie npc. - * @author Vexia - * - */ -@Initializable -public class ZombieNPC extends AbstractNPC { - - /** - * Constructs the {@code ZombieNPC} - */ - public ZombieNPC() { - super(0, null); - } - - /** - * Constructs the {@code ZombieNPC} - * @param id The id. - * @param location The location. - */ - public ZombieNPC(int id, Location location) { - super(id, location); - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - Player player = killer.asPlayer(); - // Defeat a zombie in the sewers under the jail - if (player.getViewport().getRegion().getId() == 12438 || player.getViewport().getRegion().getId() == 12439) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 18); - } - } - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new ZombieNPC(id, location); - } - - @Override - public int[] getIds() { - return new int[] {73, 74}; - } - -} diff --git a/Server/src/main/java/core/game/node/entity/player/Player.java b/Server/src/main/java/core/game/node/entity/player/Player.java index be00c2442..786c2cce3 100644 --- a/Server/src/main/java/core/game/node/entity/player/Player.java +++ b/Server/src/main/java/core/game/node/entity/player/Player.java @@ -40,7 +40,7 @@ import core.game.node.entity.player.link.skillertasks.SkillerTasks; import core.game.node.entity.skill.Skills; import core.game.node.entity.skill.construction.HouseManager; import core.game.node.entity.skill.hunter.HunterManager; -import core.game.node.entity.skill.slayer.SlayerManager; +import rs09.game.node.entity.skill.slayer.SlayerManager; import core.game.node.entity.skill.summoning.familiar.FamiliarManager; import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; @@ -77,7 +77,7 @@ import rs09.GlobalStats; import rs09.ServerConstants; import rs09.game.VarpManager; import rs09.game.content.ame.RandomEventManager; -import rs09.game.ge.PlayerGrandExchange; +import rs09.game.ge.GrandExchangeRecords; import rs09.game.node.entity.combat.CombatSwingHandler; import rs09.game.node.entity.combat.equipment.EquipmentDegrader; import rs09.game.node.entity.player.info.login.PlayerSaver; @@ -86,7 +86,6 @@ import rs09.game.node.entity.state.newsys.State; import rs09.game.node.entity.state.newsys.StateRepository; import rs09.game.system.SystemLogger; import rs09.game.world.GameWorld; -import rs09.game.world.repository.DisconnectionQueue; import rs09.game.world.repository.Repository; import rs09.game.world.update.MapChunkRenderer; import rs09.game.world.update.NPCRenderer; @@ -251,7 +250,7 @@ public class Player extends Entity { /** * The new grand exchange interface manager. */ - private final PlayerGrandExchange playerGrandExchange = new PlayerGrandExchange(this); + public final GrandExchangeRecords exchangeRecords = new GrandExchangeRecords(this); /** * The familiar manager. @@ -398,6 +397,8 @@ public class Player extends Entity { @Override public void init() { + if(!artificial) + SystemLogger.logInfo(getUsername() + " initialising..."); if (!artificial) { getProperties().setSpawnLocation(ServerConstants.HOME_LOCATION); getDetails().getSession().setObject(this); @@ -1445,7 +1446,7 @@ public class Player extends Entity { public BrawlingGlovesManager getBrawlingGlovesManager() { return brawlingGlovesManager;} - public PlayerGrandExchange getPlayerGrandExchange() { return playerGrandExchange; } + public GrandExchangeRecords getExchangeRecords() { return exchangeRecords; } public boolean hasActiveState(String key){ State state = states.get(key); diff --git a/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java b/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java index 5f58882e0..cb8796527 100644 --- a/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java +++ b/Server/src/main/java/core/game/node/entity/player/info/login/LoginConfiguration.java @@ -80,6 +80,11 @@ public final class LoginConfiguration { } else { configureGameWorld(player); } + + if(!player.isArtificial()) + { + GameWorld.getLoginListeners().forEach((listener) -> listener.login(player)); + } } /** @@ -203,6 +208,8 @@ public final class LoginConfiguration { * @param player the player. */ public static final void config(final Player player) { + if(!player.isArtificial()) + SystemLogger.logInfo("configuring player " + player.getUsername()); player.getInventory().refresh(); player.getEquipment().refresh(); player.getSkills().refresh(); @@ -212,7 +219,7 @@ public final class LoginConfiguration { player.getPacketDispatch().sendRunEnergy(); player.getFamiliarManager().login(); player.getInterfaceManager().openDefaultTabs(); - player.getPlayerGrandExchange().init(); + player.getExchangeRecords().init(); player.getPacketDispatch().sendString("Friends List - World " + GameWorld.getSettings().getWorldId(), 550, 3); player.getConfigManager().init(); player.getAntiMacroHandler().init(); @@ -221,6 +228,8 @@ public final class LoginConfiguration { player.getInterfaceManager().close(); player.getEmoteManager().refresh(); player.getInterfaceManager().openInfoBars(); + if(!player.isArtificial()) + SystemLogger.logInfo("finished configuring player " + player.getUsername()); } /** diff --git a/Server/src/main/java/core/game/node/entity/player/info/portal/PlayerSQLManager.java b/Server/src/main/java/core/game/node/entity/player/info/portal/PlayerSQLManager.java index 6e7256331..a038aacf6 100644 --- a/Server/src/main/java/core/game/node/entity/player/info/portal/PlayerSQLManager.java +++ b/Server/src/main/java/core/game/node/entity/player/info/portal/PlayerSQLManager.java @@ -108,7 +108,7 @@ public final class PlayerSQLManager { table.getColumn("credits").updateValue(player.getDetails().credits); table.getColumn("bank").updateValue(player.getBank().format()); table.getColumn("lastLogin").updateValue(player.getDetails().getLastLogin()); - table.getColumn("ge").updateValue(player.getPlayerGrandExchange().format()); + table.getColumn("ge").updateValue(player.getExchangeRecords().format()); table.getColumn("inventory").updateValue(player.getInventory().format()); table.getColumn("equipment").updateValue(player.getEquipment().format()); table.getColumn("netWorth").updateValue(player.getMonitor().getNetworth()); diff --git a/Server/src/main/java/core/game/node/entity/player/link/BankPinManager.java b/Server/src/main/java/core/game/node/entity/player/link/BankPinManager.java index 8457e8f5f..d7a3b9853 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/BankPinManager.java +++ b/Server/src/main/java/core/game/node/entity/player/link/BankPinManager.java @@ -1,10 +1,10 @@ package core.game.node.entity.player.link; -import core.cache.misc.buffer.ByteBufferUtils; import core.game.component.CloseEvent; import core.game.component.Component; import core.game.node.entity.player.Player; +import rs09.game.interaction.inter.ge.StockMarket; import rs09.game.world.GameWorld; import core.net.packet.PacketRepository; import core.net.packet.context.ChildPositionContext; @@ -14,7 +14,6 @@ import core.net.packet.out.StringPacket; import core.tools.RandomFunction; import org.json.simple.JSONObject; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -203,9 +202,9 @@ public class BankPinManager { player.getBank().open(); } } else if (buttonId == 3) { - player.getPlayerGrandExchange().openCollectionBox(); + player.getExchangeRecords().openCollectionBox(); } else if (buttonId == 4) { - player.getPlayerGrandExchange().open(); + StockMarket.openFor(player); } } diff --git a/Server/src/main/java/core/game/node/entity/player/link/TeleportManager.java b/Server/src/main/java/core/game/node/entity/player/link/TeleportManager.java index 8799fe91d..7577d874f 100644 --- a/Server/src/main/java/core/game/node/entity/player/link/TeleportManager.java +++ b/Server/src/main/java/core/game/node/entity/player/link/TeleportManager.java @@ -6,6 +6,7 @@ import core.game.node.entity.Entity; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.quest.Quest; +import core.game.node.entity.player.link.audio.Audio; import core.game.system.task.Pulse; import rs09.game.world.GameWorld; import core.game.world.map.Location; @@ -197,7 +198,7 @@ public class TeleportManager { public boolean pulse() { if (delay == 0) { if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(200); + entity.asPlayer().getAudioManager().send(new Audio(200), true); } entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); entity.graphics(new Graphics(getSettings().getStartGfx())); @@ -206,7 +207,7 @@ public class TeleportManager { fireRandom(entity, location); } else if (delay == 4) { if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(201); + entity.asPlayer().getAudioManager().send(new Audio(201), true); } entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); entity.graphics(new Graphics(getSettings().getEndGfx())); diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/HouseManager.java b/Server/src/main/java/core/game/node/entity/skill/construction/HouseManager.java index 017e43efb..17c5e358e 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/HouseManager.java +++ b/Server/src/main/java/core/game/node/entity/skill/construction/HouseManager.java @@ -202,7 +202,7 @@ public final class HouseManager { player.getConfigManager().set(261, buildingMode); player.getConfigManager().set(262, getRoomAmount()); player.getAudioManager().send(new Audio(984)); -// player.getMusicPlayer().unlock(454, true); + player.getMusicPlayer().unlock(454, true); GameWorld.getPulser().submit(new Pulse(1, player) { @Override public boolean pulse() { diff --git a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/questhall/MountedGloryPlugin.java b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/questhall/MountedGloryPlugin.java index bc41e7ec0..16958bba4 100644 --- a/Server/src/main/java/core/game/node/entity/skill/construction/decoration/questhall/MountedGloryPlugin.java +++ b/Server/src/main/java/core/game/node/entity/skill/construction/decoration/questhall/MountedGloryPlugin.java @@ -7,6 +7,7 @@ import core.game.content.dialogue.DialogueInterpreter; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; +import core.game.node.entity.player.link.audio.Audio; import core.game.system.task.Pulse; import rs09.game.world.GameWorld; import core.game.world.map.Location; @@ -78,7 +79,7 @@ public class MountedGloryPlugin extends OptionHandler { } player.lock(); player.visualize(ANIMATION, GRAPHICS); - player.getAudioManager().send(200); + player.getAudioManager().send(new Audio(200), true); player.getImpactHandler().setDisabledTicks(4); GameWorld.getPulser().submit(new Pulse(4, player) { @Override diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/CookableItems.java b/Server/src/main/java/core/game/node/entity/skill/cooking/CookableItems.java index 669c725a3..a3c61a043 100644 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/CookableItems.java +++ b/Server/src/main/java/core/game/node/entity/skill/cooking/CookableItems.java @@ -90,7 +90,12 @@ public enum CookableItems { //Miscellaneous RAW_OOMLIE(Items.RAW_OOMLIE_2337, 0, Items.BURNT_OOMLIE_2426, 50, 0, 999,0,0), // always burns OOMLIE_WRAP(Items.COOKED_OOMLIE_WRAP_2343, Items.WRAPPED_OOMLIE_2341, Items.BURNT_OOMLIE_WRAP_2345, 50, 110, 999,0,0), - SEAWEED(Items.SEAWEED_401,0,Items.SODA_ASH_1781,0,0,999,0,0); + SEAWEED(Items.SEAWEED_401,0,Items.SODA_ASH_1781,0,0,999,0,0), + /** + * Sinew gets overridden by BEEF in this enum, due to values being looked up by the items RAW id. + * This gets corrected in {@link core.game.node.entity.skill.cooking.SinewCookingPulse} + */ + SINEW(Items.SINEW_9436,Items.RAW_BEEF_2132,Items.SINEW_9436,0,3,0,0,0); public final static HashMapcookingMap = new HashMap<>(); public final static HashMapintentionalBurnMap = new HashMap<>(); diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/SinewCookingPulse.java b/Server/src/main/java/core/game/node/entity/skill/cooking/SinewCookingPulse.java new file mode 100644 index 000000000..786befa67 --- /dev/null +++ b/Server/src/main/java/core/game/node/entity/skill/cooking/SinewCookingPulse.java @@ -0,0 +1,32 @@ +package core.game.node.entity.skill.cooking; + +import core.game.node.entity.player.Player; +import core.game.node.item.Item; +import core.game.node.scenery.Scenery; + +/** + * Fixes the half assed previous implementation of cooking Sinew. + * @author Woah + */ +public class SinewCookingPulse extends StandardCookingPulse { + + public SinewCookingPulse(Player player, Scenery object, int initial, int product, int amount) { + super(player, object, initial, product, amount); + } + + @Override + public boolean checkRequirements() { + properties = CookableItems.SINEW; + return super.checkRequirements(); + } + + @Override + public boolean isBurned(Player player, Scenery object, int food) { + return false; + } + + @Override + public String getMessage(Item food, Item product, boolean burned) { + return "You dry a piece of beef and extract the sinew."; + } +} diff --git a/Server/src/main/java/core/game/node/entity/skill/cooking/StandardCookingPulse.java b/Server/src/main/java/core/game/node/entity/skill/cooking/StandardCookingPulse.java index 1d9926992..6ac0155c0 100644 --- a/Server/src/main/java/core/game/node/entity/skill/cooking/StandardCookingPulse.java +++ b/Server/src/main/java/core/game/node/entity/skill/cooking/StandardCookingPulse.java @@ -32,6 +32,7 @@ public class StandardCookingPulse extends Pulse { private Player player; private double experience; private boolean burned = false; + public CookableItems properties; public StandardCookingPulse(Player player, Scenery object, int initial, int product, int amount) { this.player = player; @@ -43,6 +44,7 @@ public class StandardCookingPulse extends Pulse { @Override public void start() { + properties = CookableItems.forId(initial); if (checkRequirements()) { super.start(); cook(player, object, CookableItems.cookingMap.get(initial) != null && isBurned(player, object, initial), initial, product); @@ -63,7 +65,6 @@ public class StandardCookingPulse extends Pulse { } public boolean checkRequirements() { - CookableItems properties = CookableItems.forId(initial); this.level = 1; this.experience = 0; if (properties != null) { @@ -214,11 +215,8 @@ public class StandardCookingPulse extends Pulse { if (food.getId() == Items.RAW_OOMLIE_2337) { return "The meat is far too delicate to cook like this. Perhaps you should wrap something around it to protect it from the heat."; } - switch (product.getId()) { - case Items.SINEW_9436: - return "You dry the meat into sinew."; - case Items.SODA_ASH_1781: - return "You burn the seaweed into soda ash."; + if (product.getId() == Items.SODA_ASH_1781) { + return "You burn the seaweed into soda ash."; } if (CookableItems.intentionalBurn(food.getId())) { return "You deliberately burn the perfectly good piece of meat."; diff --git a/Server/src/main/java/core/game/node/entity/skill/firemaking/FireMakingPulse.java b/Server/src/main/java/core/game/node/entity/skill/firemaking/FireMakingPulse.java index e8dda160d..b76115fb5 100644 --- a/Server/src/main/java/core/game/node/entity/skill/firemaking/FireMakingPulse.java +++ b/Server/src/main/java/core/game/node/entity/skill/firemaking/FireMakingPulse.java @@ -1,5 +1,6 @@ package core.game.node.entity.skill.firemaking; +import api.events.LitFireEvent; import core.game.world.map.zone.ZoneBorders; import core.game.content.quest.tutorials.tutorialisland.TutorialSession; import core.game.content.quest.tutorials.tutorialisland.TutorialStage; @@ -140,20 +141,8 @@ public final class FireMakingPulse extends SkillPulse { int playerRegion = player.getViewport().getRegion().getId(); - if (fire == Log.MAGIC && playerRegion == 10806) { - player.getAchievementDiaryManager().finishTask(player,DiaryType.SEERS_VILLAGE,2, 5); - } - // Light a campfire from normal logs in Lumbridge Swamp - if (fire == Log.NORMAL && (playerRegion == 12593 || playerRegion == 12849)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 9); - } - // Light a willow log fire in Lumbridge Castle courtyard - if (fire == Log.WILLOW - && new ZoneBorders(3216, 3207, 3225, 3233, 0).insideBorder(player)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 2, 3); - } - setLastFire(); + player.dispatch(new LitFireEvent(fire.getLogId())); } @Override diff --git a/Server/src/main/java/core/game/node/entity/skill/fishing/Fish.java b/Server/src/main/java/core/game/node/entity/skill/fishing/Fish.java index 3a8480039..a776d948c 100644 --- a/Server/src/main/java/core/game/node/entity/skill/fishing/Fish.java +++ b/Server/src/main/java/core/game/node/entity/skill/fishing/Fish.java @@ -28,7 +28,7 @@ public enum Fish { LOBSTER(new Item(377), 40, 90, 0.16, 0.375), BASS(new Item(363), 46, 100, 0.078, 0.16), SWORDFISH(new Item(371), 50, 100, 0.105, 0.191), - LAVA_EEL(new Item(2148), 53, 100, 0.227, 0.379), + LAVA_EEL(new Item(2148), 53, 30, 0.227, 0.379), MONKFISH(new Item(7944), 62, 120, 0.293, 0.356), KARAMBWAN(new Item(3142), 65, 105, 0.414, 0.629), SHARK(new Item(383), 76, 110, 0.121, 0.16), diff --git a/Server/src/main/java/core/game/node/entity/skill/fletching/items/bow/StringPulse.java b/Server/src/main/java/core/game/node/entity/skill/fletching/items/bow/StringPulse.java index 02cf1c051..47424b7f3 100644 --- a/Server/src/main/java/core/game/node/entity/skill/fletching/items/bow/StringPulse.java +++ b/Server/src/main/java/core/game/node/entity/skill/fletching/items/bow/StringPulse.java @@ -40,7 +40,7 @@ public class StringPulse extends SkillPulse { @Override public boolean checkRequirements() { if (getDelay() == 1) { - setDelay(bow.string == 1777 ? 2 : 1); + setDelay(2); } if (player.getSkills().getLevel(Skills.FLETCHING) < bow.level) { player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + bow.level + " to string this bow."); diff --git a/Server/src/main/java/core/game/node/entity/skill/gather/woodcutting/WoodcuttingSkillPulse.java b/Server/src/main/java/core/game/node/entity/skill/gather/woodcutting/WoodcuttingSkillPulse.java index 5fb6a2cf0..c567031c6 100644 --- a/Server/src/main/java/core/game/node/entity/skill/gather/woodcutting/WoodcuttingSkillPulse.java +++ b/Server/src/main/java/core/game/node/entity/skill/gather/woodcutting/WoodcuttingSkillPulse.java @@ -1,5 +1,6 @@ package core.game.node.entity.skill.gather.woodcutting; +import api.events.ResourceGatheredEvent; import core.cache.def.impl.ItemDefinition; import core.game.container.impl.EquipmentContainer; import core.game.content.dialogue.FacialExpression; @@ -159,6 +160,7 @@ public class WoodcuttingSkillPulse extends Pulse { } //give the reward player.getInventory().add(new Item(reward, rewardAmount)); + player.dispatch(new ResourceGatheredEvent(reward, rewardAmount, node)); int cutLogs = player.getAttribute(STATS_BASE + ":" + STATS_LOGS,0); player.setAttribute("/save:" + STATS_BASE + ":" + STATS_LOGS,++cutLogs); @@ -174,7 +176,6 @@ public class WoodcuttingSkillPulse extends Pulse { } } - applyAchievementTask(reward); // apply achievements } // Tutorial stuff, maybe? if (tutorialStage == 7) { @@ -261,55 +262,6 @@ public class WoodcuttingSkillPulse extends Pulse { return reward; } - /** - * Checks if the has completed any achievements from their diary - */ - private void applyAchievementTask(int reward) { - // Cut a log from a teak tree - if (reward == Items.TEAK_LOGS_6333) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 7); - } - // Cut a log from a mahogany tree - if (reward == Items.MAHOGANY_LOGS_6332) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 8); - } - - int playerRegion = player.getViewport().getRegion().getId(); - - // Chop down a dying tree in the Lumber Yard - if (node.getId() == 24168 && playerRegion == 13110) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 0, 6); - } - if (resource == WoodcuttingNode.YEW && playerRegion == 10806) { - if (!player.getAchievementDiaryManager().hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 1)) { - player.setAttribute("/save:diary:seers:cut-yew", player.getAttribute("diary:seers:cut-yew", 0) + 1); - } - if (player.getAttribute("diary:seers:cut-yew", 0) >= 5) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 2, 1); - } - } - - /* if (resource.isFarming()) { - PatchWrapper tree = player.getFarmingManager().getPatchWrapper(node.getWrapper().getId()); - if (node.getId() == 8389 - && node.getLocation().equals(3003, 3372, 0) - && Trees.forNode(tree.getNode()) != null - && (Trees.forNode(tree.getNode()) == Trees.YEW || Trees.forNode(tree.getNode()) == Trees.MAGIC)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 2, 3); - } - } -*/ - // Cut down a dead tree in Lumbridge Swamp - if (resource.name().toLowerCase().startsWith("dead") && (playerRegion == 12593 || playerRegion == 12849)) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 8); - } - - // Cut a willow tree, east of Lumbridge Castle - if (resource.name().toLowerCase().startsWith("willow") && playerRegion == 12850) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 2, 6); - } - } - /** * Checks if the player gets rewarded. * diff --git a/Server/src/main/java/core/game/node/entity/skill/magic/MagicStaff.java b/Server/src/main/java/core/game/node/entity/skill/magic/MagicStaff.java index bae873e7f..921157d91 100644 --- a/Server/src/main/java/core/game/node/entity/skill/magic/MagicStaff.java +++ b/Server/src/main/java/core/game/node/entity/skill/magic/MagicStaff.java @@ -1,5 +1,7 @@ package core.game.node.entity.skill.magic; +import org.rs09.consts.Items; + import java.util.HashMap; import java.util.Map; @@ -12,7 +14,7 @@ public enum MagicStaff { /** * Represents the fire rune staves. */ - FIRE_RUNE(554, 1387, 1393, 1401, 3053, 3055, 3056, 11736, 11738), + FIRE_RUNE(554, 1387, 1393, 1401, 3053, 3054, 3055, 3056, 11736, 11738), /** * Represents the water rune staves. diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/AberrantSpectreNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/AberrantSpectreNPC.java index 1e61efe34..48a7e64b4 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/AberrantSpectreNPC.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/AberrantSpectreNPC.java @@ -9,6 +9,7 @@ import core.game.world.map.Location; import core.plugin.Initializable; import rs09.game.node.entity.combat.CombatSwingHandler; import rs09.game.node.entity.combat.handlers.MagicSwingHandler; +import rs09.game.node.entity.skill.slayer.SlayerEquipmentFlags; /** * Handles the aberrant spectre npc. diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/BansheeNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/BansheeNPC.java index b5ab44ff0..9f15a94c9 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/BansheeNPC.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/BansheeNPC.java @@ -11,6 +11,7 @@ import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; +import rs09.game.node.entity.skill.slayer.SlayerEquipmentFlags; import rs09.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/CaveHorrorNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/CaveHorrorNPC.java index 30cc07f39..e5bf16f8a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/CaveHorrorNPC.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/CaveHorrorNPC.java @@ -11,6 +11,7 @@ import core.game.node.entity.player.Player; import core.game.world.map.Location; import core.plugin.Initializable; import core.tools.RandomFunction; +import rs09.game.node.entity.skill.slayer.SlayerEquipmentFlags; /** * Handles the Cave Horrors on Mos'le Harmless. You technically do not need a witchwood icon to fight them. diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/DustDevilNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/DustDevilNPC.java index 663c7a3ff..c4487cb9b 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/DustDevilNPC.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/DustDevilNPC.java @@ -11,6 +11,7 @@ import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; import core.game.world.map.Location; +import rs09.game.node.entity.skill.slayer.SlayerEquipmentFlags; /** * Handles the dust devil npc. diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/KuraskNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/KuraskNPC.java index cde7e0474..84f94896c 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/KuraskNPC.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/KuraskNPC.java @@ -43,7 +43,7 @@ public final class KuraskNPC extends AbstractNPC { boolean effective = false; if (state.getAttacker() instanceof Player) { final Player player = (Player) state.getAttacker(); - effective = new SlayerUtils(player).hasBroadWeaponEquipped(state); + effective = SlayerUtils.hasBroadWeaponEquipped(player, state); } if (!effective) { state.setEstimatedHit(0); diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/Master.java b/Server/src/main/java/core/game/node/entity/skill/slayer/Master.java index 1b444a68e..83ee6be4d 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/Master.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/Master.java @@ -218,9 +218,9 @@ public enum Master { } int npc_id,required_combat,required_slayer; - int[] assignment_range; + public int[] assignment_range; int[] streakPoints; - List tasks; + public List tasks; Master(int npc_id, int required_combat, int required_slayer, int[] assignment_range, int[] streakPoints, Task... tasks) { this.npc_id = npc_id; this.required_combat = required_combat; @@ -251,11 +251,11 @@ public enum Master { } public static class Task{ - Tasks task; - Integer weight; + public Tasks task; + public Integer weight; Task(Tasks task, Integer weight){ this.task = task; this.weight = weight; } } -} +} \ No newline at end of file diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/MirrorShieldHandler.java b/Server/src/main/java/core/game/node/entity/skill/slayer/MirrorShieldHandler.java index db7552b92..63f6c935a 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/MirrorShieldHandler.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/MirrorShieldHandler.java @@ -5,6 +5,7 @@ import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; import rs09.game.node.entity.combat.handlers.MeleeSwingHandler; import core.game.node.entity.player.Player; +import rs09.game.node.entity.skill.slayer.SlayerEquipmentFlags; /** * The swing handler for an npc that requires a mirror shield with its victim. diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/SkeletalWyvernNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/SkeletalWyvernNPC.java index cb8588c6c..1508bc990 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SkeletalWyvernNPC.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/SkeletalWyvernNPC.java @@ -84,15 +84,6 @@ public final class SkeletalWyvernNPC extends AbstractNPC { return COMBAT_HANDLER; } - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - if (killer instanceof Player) { - final Player player = killer.asPlayer(); - player.getAchievementDiaryManager().finishTask(player,DiaryType.FALADOR, 2, 8); - } - } - @Override public int[] getIds() { return Tasks.SKELETAL_WYVERN.getNpcs(); diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerManager.java b/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerManager.java deleted file mode 100644 index f418495b2..000000000 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerManager.java +++ /dev/null @@ -1,347 +0,0 @@ -package core.game.node.entity.skill.slayer; - -import core.cache.def.impl.NPCDefinition; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.skill.Skills; -import core.tools.RandomFunction; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import rs09.game.node.entity.skill.slayer.SlayerFlags; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Manages the players slayer task. - * @author Vexia - * - */ -public final class SlayerManager { - - /** - * The player instance. - */ - private final Player player; - - /** - * The player's slayer flags - */ - public final SlayerFlags flags; - - /** - * Constructs a new {@Code SlayerManager} {@Code Object} - * @param player The player. - */ - public SlayerManager(Player player) { - this.player = player; - this.flags = new SlayerFlags(player); - } - - public void parse(JSONObject slayerData){ - Object m = slayerData.get("master"); - if(m != null) { - flags.setMaster(Master.forId(Integer.parseInt(m.toString()))); - } - Object t = slayerData.get("taskId"); - if(t != null) - flags.setTask(Tasks.values()[Integer.parseInt(t.toString())]); - Object a = slayerData.get("taskAmount"); - if(a != null) - flags.setTaskAmount(Integer.parseInt(a.toString())); - Object points = slayerData.get("points"); - if(points != null){ - flags.setPoints(Integer.parseInt(points.toString())); - } - Object taskStreak = slayerData.get("taskStreak"); - if(taskStreak != null){ - flags.setTaskStreak(Integer.parseInt(taskStreak.toString())); - } - Object la = slayerData.get("learned_rewards"); - if(la != null) { - JSONArray learnedArray = (JSONArray) slayerData.get("learned_rewards"); - for (int i = 0; i < learnedArray.size(); i++) { - boolean unlocked = (boolean) learnedArray.get(i); - switch(i){ - case 0: - if(unlocked) flags.unlockBroads(); - break; - case 1: - if(unlocked) flags.unlockRing(); - break; - case 2: - if(unlocked) flags.unlockHelm(); - break; - default: - break; - } - } - } - JSONArray removedTasks = (JSONArray) slayerData.get("removedTasks"); - if(removedTasks != null){ - for(int i = 0; i < removedTasks.size(); i++){ - flags.getRemoved().add(Tasks.values()[Integer.parseInt(removedTasks.get(i).toString())]); - } - } - Object completedTasks = slayerData.get("totalTasks").toString(); - if(completedTasks != null) flags.setCompletedTasks(Integer.parseInt(completedTasks.toString())); - if(flags.getCompletedTasks() >= 4) flags.flagCanEarnPoints(); - - //New system parsing - if(slayerData.containsKey("equipmentFlags")) - flags.setEquipmentFlags(Integer.parseInt(slayerData.get("equipmentFlags").toString())); - if(slayerData.containsKey("taskFlags")) - flags.setTaskFlags(Integer.parseInt(slayerData.get("taskFlags").toString())); - if(slayerData.containsKey("rewardFlags")) - flags.setRewardFlags(Integer.parseInt(slayerData.get("rewardFlags").toString())); - - } - - /** - * Called when a hunted creature dies. - * @param player The player. - * @param npc The NPC. You're currently - */ - public void finalizeDeath(Player player, NPC npc) { - if(hasTask()) { - player.getSkills().addExperience(Skills.SLAYER, npc.getSkills().getMaximumLifepoints()); - decrementAmount(1); - if(!hasTask()){ - flags.setTaskStreak(flags.getTaskStreak() + 1); - flags.setCompletedTasks(flags.getCompletedTasks() + 1); - if ((flags.getCompletedTasks() > 4 || flags.canEarnPoints() ) && flags.getMaster() != Master.TURAEL && flags.getPoints() < 64000) { - int points = flags.getMaster().getTaskPoints()[0]; - if (flags.getTaskStreak() % 50 == 0) { - points = flags.getMaster().getTaskPoints()[2]; - } else if (flags.getTaskStreak() % 10 == 0) { - points = flags.getMaster().getTaskPoints()[1]; - } - flags.incrementPoints(points); - if (flags.getPoints() > 64000) { - flags.setPoints(64000); - } - player.sendMessages("You've completed " + flags.getTaskStreak() + " tasks in a row and received " + points + " points, with a total of " + flags.getPoints(),"You have completed " + flags.getCompletedTasks() + " tasks in total. Return to a Slayer master."); - } else if(flags.getCompletedTasks() == 4){ - player.sendMessage("You've completed your task; you will start gaining points on your next task!"); - flags.flagCanEarnPoints(); - } else if (flags.getMaster() == Master.TURAEL){ - player.sendMessages("You've completed your task; Tasks from Turael do not award points.", "Return to a Slayer master."); - } - else - { - player.sendMessages("You've completed your task; Complete " + (4 - flags.getCompletedTasks()) + " more task(s) to start gaining points.", "Return to a Slayer master."); - } - } - } - } - - /** - * Assigns a task to the manager. - * @param task the task. - * @param master the master. - */ - public void assign(Tasks task, final Master master) { - setMaster(master); - setTask(task); - setAmount(getRandomAmount(master.assignment_range)); - if (master == Master.DURADEL) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 2, 8); - } else if (master == Master.VANNAKA) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 1, 14); - } - getPlayer().varpManager.get(2502).setVarbit(0, flags.getTaskFlags() >> 4).send(player); - } - - /** - * Method used to assign a new task for a player. - * @param master the master to give the task. - */ - public void generate(Master master) { - final List tasks = new ArrayList<>(10); - final int[] taskWeightSum = {0}; - master.tasks.stream().filter(task -> canBeAssigned(task.task) && task.task.combatCheck <= player.getProperties().getCurrentCombatLevel()).forEach(task -> { - taskWeightSum[0] += task.weight; - tasks.add(task); - }); - Collections.shuffle(tasks, RandomFunction.RANDOM); - int rnd = RandomFunction.random(taskWeightSum[0]); - for(Master.Task task : tasks){ - if(rnd < task.weight) - assign(task.task,master); - rnd -= task.weight; - } - } - - public boolean canBeAssigned(Tasks task){ - return player.getSkills().getLevel(Skills.SLAYER) >= task.levelReq && !flags.getRemoved().contains(task); - } - - /** - * Clears the task. - */ - public void clear() { - setAmount(0); - } - - /** - * Gets a random amount. - * @param ranges the ranges. - * @return the amt. - */ - private int getRandomAmount(int[] ranges) { - return RandomFunction.random(ranges[0], ranges[1]); - } - - /** - * Gets the task name. - * @return the name. - */ - public String getTaskName() { - Tasks task = flags.getTask(); - if (task.getNpcs() == null) { - return "no npcs report me"; - } - if (task.getNpcs().length < 1) { - return "npc length too small report me"; - } - return NPCDefinition.forId(task.getNpcs()[0]).getName().toLowerCase(); - } - - /** - * Gets the task. - * @return The task. - */ - public Tasks getTask() { - return flags.getTask(); - } - - /** - * Sets the task. - * @param task The task to set. - */ - public void setTask(Tasks task){ - flags.setTask(task); - } - - /** - * Gets the player. - * @return The player. - */ - public Player getPlayer() { - return player; - } - - /** - * Gets the master. - * @return The master. - */ - public Master getMaster() { - return flags.getMaster(); - } - - /** - * Sets the master. - * @param master The master to set. - */ - public void setMaster(Master master) { - flags.setMaster(master); - } - - /** - * Checks if a Player contains a task. - * @return {@code True} if so. - */ - public boolean hasTask() { - return getAmount() > 0; - } - - /** - * Method used to check if the task is completed. - * @return True if so. - */ - public boolean isCompleted() { - return flags.getTaskAmount() <= 0; - } - - /** - * Gets the amount. - * @return The amount. - */ - public int getAmount() { - return flags.getTaskAmount(); - } - - /** - * Sets the amount. - * @param amount The amount to set. - */ - public void setAmount(int amount) { - flags.setTaskAmount(amount); - } - - /** - * Method used to decrement an amount. - * @param amount the amount. - */ - public void decrementAmount(int amount) { - flags.decrementTaskAmount(amount); - player.varpManager.get(2502).setVarbit(0, flags.getTaskFlags() >> 4).send(player); - } - - /** - * Method used to check if the player has started slayer. - * @return {@code True} if so. - */ - public boolean hasStarted() { - return flags.getCompletedTasks() > 0 || flags.getTaskAmount() > 0; - } - - /** - * Gets the slayerPoints. - * @return the slayerPoints. - */ - public int getSlayerPoints() { - return flags.getPoints(); - } - - /** - * Sets the slayerPoints. - * @param slayerPoints the slayerPoints to set - */ - public void setSlayerPoints(int slayerPoints) { - flags.setPoints(slayerPoints); - } - - /** - * Gets the taskCount. - * @return the taskCount. - */ - public int getTaskCount() { - return flags.getTaskStreak(); - } - - /** - * Sets the taskCount. - * @param taskCount the taskCount to set - */ - public void setTaskCount(int taskCount) { - flags.setTaskStreak(taskCount); - } - - /** - * Gets the removed. - * @return the removed. - */ - public List getRemoved() { - return flags.getRemoved(); - } - - public int getTotalTasks() {return flags.getCompletedTasks();} - - public boolean isCanEarnPoints(){ - return flags.canEarnPoints(); - } - -} diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerMasterDialogue.java b/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerMasterDialogue.java index ecb1aed5f..fe45afcb5 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerMasterDialogue.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerMasterDialogue.java @@ -505,7 +505,7 @@ public final class SlayerMasterDialogue extends DialoguePlugin { interpreter.sendDialogues(master.getNpc(), getExpression(master), "You're still hunting something. But let me check something..."); stage = 847; } else { - player.getSlayer().setTaskCount(0); + player.getSlayer().flags.setTaskStreak(0); player.getSlayer().generate(master); if (player.getSlayer().getTask() == Tasks.JAD) { interpreter.sendDialogues(master.getNpc(), getExpression(master), "Excellent, you're doing great. Your new task is to", "defeat the almighty TzTok-Jad."); diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/TurothNPC.java b/Server/src/main/java/core/game/node/entity/skill/slayer/TurothNPC.java index 7299fae07..421e7730f 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/TurothNPC.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/TurothNPC.java @@ -43,7 +43,7 @@ public final class TurothNPC extends AbstractNPC { boolean effective = false; if (state.getAttacker() instanceof Player) { final Player player = (Player) state.getAttacker(); - effective = new SlayerUtils(player).hasBroadWeaponEquipped(state); + effective = SlayerUtils.hasBroadWeaponEquipped(player, state); } if (!effective) { state.setEstimatedHit(0); diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/LumbridgeDungeon.java b/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/LumbridgeDungeon.java index cfca0ec06..83b7344bd 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/LumbridgeDungeon.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/LumbridgeDungeon.java @@ -1,7 +1,7 @@ package core.game.node.entity.skill.slayer.dungeon; import core.cache.def.impl.NPCDefinition; -import core.game.node.entity.skill.slayer.SlayerEquipmentFlags; +import rs09.game.node.entity.skill.slayer.SlayerEquipmentFlags; import core.game.interaction.Option; import core.game.node.Node; import core.game.node.entity.Entity; diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/SmokeDungeon.java b/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/SmokeDungeon.java index 875a6cded..a0cdaa078 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/SmokeDungeon.java +++ b/Server/src/main/java/core/game/node/entity/skill/slayer/dungeon/SmokeDungeon.java @@ -2,7 +2,7 @@ package core.game.node.entity.skill.slayer.dungeon; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.game.node.entity.skill.slayer.SlayerEquipmentFlags; +import rs09.game.node.entity.skill.slayer.SlayerEquipmentFlags; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; diff --git a/Server/src/main/java/core/game/node/entity/skill/thieving/Stall.java b/Server/src/main/java/core/game/node/entity/skill/thieving/Stall.java index b61e22515..cd7a897c4 100644 --- a/Server/src/main/java/core/game/node/entity/skill/thieving/Stall.java +++ b/Server/src/main/java/core/game/node/entity/skill/thieving/Stall.java @@ -18,8 +18,8 @@ public enum Stall { SILK_STALL(new Integer[]{34383, 2560}, new Integer[] { 34381, 634 }, 20, new Item[]{new Item(950, 1)}, 24, 13,"silk"), WINE_STALL(new Integer[]{2046}, new Integer[] { 634 }, 22, new Item[]{new Item(1935, 1), new Item(1937, 1), new Item(1993, 1), new Item(7919, 1)}, 27, 27,"wine"), //OBJECT MISSING IN CACHE MARKET_SEED_STALL(new Integer[]{7053}, new Integer[] { 634 }, 27, new Item[]{new Item(5096, 1), new Item(5097, 1), new Item(5101, 1), new Item(5318, 1), new Item(5319, 1), new Item(5324, 1)}, 10, 19,"seeds"), - FUR_STALL(new Integer[]{34387, 2563}, new Integer[] { 34381, 634 }, 35, new Item[]{new Item(6814, 1), new Item(958, 1)}, 36, 25,"fur"), - FISH_STALL(new Integer[]{4277, 4705, 4707}, new Integer[] { 634 }, 42, new Item[]{new Item(331, 1), new Item(359, 1), new Item(377, 1)}, 42, 27,"fish"), + FUR_STALL(new Integer[]{ 34387, 2563, 4278}, new Integer[] { 34381, 634, 634 }, 35, new Item[]{new Item(6814, 1), new Item(958, 1)}, 36, 25,"fur"), + FISH_STALL(new Integer[]{ 4277, 4705, 4707 }, new Integer[] { 634, 634, 634 }, 42, new Item[]{new Item(331, 1), new Item(359, 1), new Item(377, 1)}, 42, 27,"fish"), CROSSBOW_STALL(new Integer[]{17031}, new Integer[] { 6984 }, 49, new Item[]{new Item(877, 3), new Item(9420, 1), new Item(9440, 1)}, 52, 19,"equipment"), SILVER_STALL(new Integer[]{2565, 6164, 34382}, new Integer[] { 634, 6984, 34381}, 50, new Item[]{new Item(442, 1)}, 54, 50,"jewellery"), SPICE_STALL(new Integer[]{34386, 6166}, new Integer[] { 34381, 6984 }, 65, new Item[]{new Item(2007, 1)}, 81, 134,"spices"), diff --git a/Server/src/main/java/core/game/node/entity/skill/thieving/StallThiefPulse.java b/Server/src/main/java/core/game/node/entity/skill/thieving/StallThiefPulse.java index a50388546..545e20d06 100644 --- a/Server/src/main/java/core/game/node/entity/skill/thieving/StallThiefPulse.java +++ b/Server/src/main/java/core/game/node/entity/skill/thieving/StallThiefPulse.java @@ -15,6 +15,8 @@ import core.game.world.update.flag.context.Animation; import core.tools.RandomFunction; import core.tools.StringUtils; +import static api.ContentAPIKt.*; + /** * Represents the pulse used to thieve a stall. * @author 'Vexia @@ -70,6 +72,15 @@ public final class StallThiefPulse extends SkillPulse { player.getPacketDispatch().sendMessage("You don't have enough inventory space."); return false; } + if (player.getLocation().isInRegion(10553) && !isQuestComplete(player, "Fremennik Trials") && stall.full_ids.contains(4278)) { + sendDialogue(player, "The fur trader is staring at you suspiciously. You cannot steal from his stall while he distrusts you."); + return false; + } + + if (player.getLocation().isInRegion(10553) && !isQuestComplete(player, "Fremennik Trials") && stall.full_ids.contains(4277)) { + sendDialogue(player, "The fishmonger is staring at you suspiciously. You cannot steal from his stall while he distrusts you."); + return false; + } return true; } @@ -100,7 +111,7 @@ public final class StallThiefPulse extends SkillPulse { player.getSavedData().getGlobalData().setSilkSteal(System.currentTimeMillis() + 1800000); } if (node.isActive()) { - SceneryBuilder.replace(((Scenery) node), ((Scenery) node).transform(stall.getEmpty(node.getId())), stall.getDelay()); + SceneryBuilder.replace(node, node.transform(stall.getEmpty(node.getId())), stall.getDelay()); } final Item item = stall.getRandomLoot(); player.getInventory().add(item); diff --git a/Server/src/main/java/core/game/node/item/Item.java b/Server/src/main/java/core/game/node/item/Item.java index 59bbcf8ea..b1f0be018 100644 --- a/Server/src/main/java/core/game/node/item/Item.java +++ b/Server/src/main/java/core/game/node/item/Item.java @@ -8,7 +8,6 @@ import core.game.interaction.Interaction; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.combat.equipment.DegradableEquipment; -import rs09.game.ge.OfferManager; /** * Represents an item. diff --git a/Server/src/main/java/core/game/system/SystemTermination.java b/Server/src/main/java/core/game/system/SystemTermination.java index 1ce55219b..2d7194599 100644 --- a/Server/src/main/java/core/game/system/SystemTermination.java +++ b/Server/src/main/java/core/game/system/SystemTermination.java @@ -8,7 +8,6 @@ import rs09.Server; import rs09.ServerConstants; import rs09.ServerStore; import rs09.game.content.global.GlobalKillCounter; -import rs09.game.ge.OfferManager; import rs09.game.system.SystemLogger; import rs09.game.world.repository.Repository; @@ -94,7 +93,6 @@ public final class SystemTermination { Repository.getDisconnectionQueue().update(); GlobalKillCounter.save(); GrandExchangeDatabase.save(); - OfferManager.save(); SystemLogger.flushLogs(); SystemLogger.logInfo("[SystemTerminator] Saved Grand Exchange databases!"); Repository.getDisconnectionQueue().clear(); diff --git a/Server/src/main/java/core/game/world/map/Location.java b/Server/src/main/java/core/game/world/map/Location.java index cf014d2bb..c6533582d 100644 --- a/Server/src/main/java/core/game/world/map/Location.java +++ b/Server/src/main/java/core/game/world/map/Location.java @@ -287,6 +287,23 @@ public final class Location extends Node { return locs; } + /** + * Gets a square of 3 x 3 tiles as an ArrayList + */ + public ArrayList get3x3Tiles() { + ArrayList locs = new ArrayList<>(); + locs.add(transform(0,0,0)); //Center + locs.add(transform(0,1,0)); //N + locs.add(transform(1,1,0)); //NE + locs.add(transform(1,0,0)); //E + locs.add(transform(1,-1,0)); //SE + locs.add(transform(0,-1,0)); //S + locs.add(transform(-1,-1,0));//SW + locs.add(transform(-1,0,0));//W + locs.add(transform(-1,1,0));//NW + return locs; + } + /** * Gets the x position on the region chunk. * @return The x position on the region chunk. diff --git a/Server/src/main/java/core/game/world/map/RegionManager.kt b/Server/src/main/java/core/game/world/map/RegionManager.kt index 9b1ec02f4..5d1d2912e 100644 --- a/Server/src/main/java/core/game/world/map/RegionManager.kt +++ b/Server/src/main/java/core/game/world/map/RegionManager.kt @@ -1,12 +1,10 @@ package core.game.world.map -import core.game.world.map.RegionManager -import core.game.world.map.RegionPlane -import core.game.node.scenery.Scenery 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.node.scenery.Scenery import core.game.world.map.zone.ZoneBorders import core.tools.RandomFunction import rs09.game.system.SystemLogger @@ -729,6 +727,27 @@ object RegionManager { return players } + /** + * Gets a list of local players. + * @param l The location. + * @param distance The distance to that location. + * @return The list of players. + */ + @JvmStatic + fun getLocalPlayersMaxNorm(l: Location, distance: Int): MutableList { + val players: MutableList = LinkedList() + for (regionX in ((l.regionX - 6) shr 3)..((l.regionX + 6) shr 3)) { + for (regionY in ((l.regionY - 6) shr 3)..((l.regionY + 6) shr 3)) { + for (player in forId((regionX shl 8) or regionY).planes[l.z].players) { + if (player.location.withinMaxnormDistance(l, distance)) { + players.add(player) + } + } + } + } + return players + } + /** * Gets a list of local players within 16 tile distance of the location. * @param l The location. diff --git a/Server/src/main/java/core/game/world/map/zone/impl/WildernessZone.java b/Server/src/main/java/core/game/world/map/zone/impl/WildernessZone.java index 38336d447..104b85204 100644 --- a/Server/src/main/java/core/game/world/map/zone/impl/WildernessZone.java +++ b/Server/src/main/java/core/game/world/map/zone/impl/WildernessZone.java @@ -194,9 +194,6 @@ public final class WildernessZone extends MapZone { if (e instanceof NPC) { e.asNpc().getDefinition().getDropTables().drop(e.asNpc(), killer); - if (((NPC) e).getTask() != null && ((Player) killer).getSlayer().getTask() == e.asNpc().getTask()) { - ((Player) killer).getSlayer().finalizeDeath(killer.asPlayer(), e.asNpc()); - } e.asNpc().setRespawnTick(GameWorld.getTicks() + e.asNpc().getDefinition().getConfiguration(NPCConfigParser.RESPAWN_DELAY, 17)); if (!e.asNpc().isRespawn()) { e.asNpc().clear(); diff --git a/Server/src/main/java/core/gui/tab/GrandExchangeTab.java b/Server/src/main/java/core/gui/tab/GrandExchangeTab.java index c6fe68e39..b0eeb6290 100644 --- a/Server/src/main/java/core/gui/tab/GrandExchangeTab.java +++ b/Server/src/main/java/core/gui/tab/GrandExchangeTab.java @@ -5,7 +5,6 @@ import core.game.node.entity.player.Player; import core.gui.ConsoleFrame; import core.gui.ConsoleTab; import rs09.game.ge.GrandExchangeOffer; -import rs09.game.ge.OfferManager; import javax.swing.*; import java.awt.*; @@ -142,12 +141,12 @@ public class GrandExchangeTab extends ConsoleTab { JOptionPane.showMessageDialog(null, "Error! No player found."); return; } - for (GrandExchangeOffer o : player.getPlayerGrandExchange().getOffers()) { +/* for (GrandExchangeOffer o : player.getPlayerGrandExchange().getOfferRecords()) { if (o == null) { continue; } addOffer(o); - } + }*/ } /** @@ -161,12 +160,12 @@ public class GrandExchangeTab extends ConsoleTab { } catch (NumberFormatException e) { } - for (GrandExchangeOffer o : OfferManager.getOffersForItem(itemId)) { +/* for (GrandExchangeOffer o : OfferManager.getOffersForItem(itemId)) { if (o == null) { continue; } addOffer(o); - } + }*/ } /** @@ -184,8 +183,8 @@ public class GrandExchangeTab extends ConsoleTab { JOptionPane.showMessageDialog(null, "Error! No data in DB yet. Press load."); return; } - for (GrandExchangeOffer offer : OfferManager.getOFFER_MAPPING().values()) { +/* for (GrandExchangeOffer offer : OfferManager.getOFFER_MAPPING().values()) { model.addElement(offer); - } + }*/ } } diff --git a/Server/src/main/java/core/net/packet/in/ExaminePacket.java b/Server/src/main/java/core/net/packet/in/ExaminePacket.java index 31e1486ed..47e916146 100644 --- a/Server/src/main/java/core/net/packet/in/ExaminePacket.java +++ b/Server/src/main/java/core/net/packet/in/ExaminePacket.java @@ -33,6 +33,9 @@ public final class ExaminePacket implements IncomingPacket { player.debug("Object id: " + id + ", models: " + (d.getModelIds() != null ? Arrays.toString(d.getModelIds()) : null) + ", anim: " + d.animationId + ", config: " + (d.getVarbitID() != -1 ? d.getVarbitID() + " (file)" : d.getConfigId()) + "."); player.debug("Varp config index: " + VarbitDefinition.forObjectID(d.getVarbitID()).getConfigId()); player.getPacketDispatch().sendMessage(""+name+""); + /*if { + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection("LandscapeParser.removeGameObject(new GameObject("+coords+"));//"+ d.getName() ), null); + }*/ break; case 235: case 92: // Item examine diff --git a/Server/src/main/java/core/net/packet/in/GrandExchangePacket.java b/Server/src/main/java/core/net/packet/in/GrandExchangePacket.java index 96a82b5e6..94736d723 100644 --- a/Server/src/main/java/core/net/packet/in/GrandExchangePacket.java +++ b/Server/src/main/java/core/net/packet/in/GrandExchangePacket.java @@ -1,8 +1,14 @@ package core.net.packet.in; +import api.ContentAPIKt; +import core.game.ge.GrandExchangeDatabase; +import core.game.ge.GrandExchangeEntry; import core.game.node.entity.player.Player; import core.net.packet.IncomingPacket; import core.net.packet.IoBuffer; +import rs09.game.ge.GrandExchange; +import rs09.game.ge.GrandExchangeOffer; +import rs09.game.interaction.inter.ge.StockMarket; /** * Represents the Incoming packet of the grand exchange. @@ -13,7 +19,23 @@ public class GrandExchangePacket implements IncomingPacket { @Override public void decode(Player player, int opcode, IoBuffer buffer) { int itemId = buffer.getShort(); - player.getPlayerGrandExchange().constructBuy(itemId); + GrandExchangeOffer offer = player.getAttribute("ge-temp", new GrandExchangeOffer()); + int index = player.getAttribute("ge-index", -1); + offer.setItemID(itemId); + offer.setSell(false); + GrandExchangeEntry entry = GrandExchangeDatabase.getDatabase().get(itemId); + if(entry == null || !ContentAPIKt.itemDefinition(itemId).isTradeable()) + { + ContentAPIKt.sendMessage(player, "That item is blacklisted from the grand exchange."); + return; + } + offer.setPlayer(player); + offer.setAmount(1); + offer.setOfferedValue(GrandExchange.getRecommendedPrice(itemId, false)); + offer.setIndex(index); + StockMarket.updateVarbits(player, offer, index, false); + player.setAttribute("ge-temp",offer); + player.getPacketDispatch().sendString(GrandExchange.getOfferStats(itemId, false), 105, 142); player.getInterfaceManager().closeChatbox(); } diff --git a/Server/src/main/java/core/net/packet/in/InteractionPacket.java b/Server/src/main/java/core/net/packet/in/InteractionPacket.java index fb97b1b8f..dfc79f208 100644 --- a/Server/src/main/java/core/net/packet/in/InteractionPacket.java +++ b/Server/src/main/java/core/net/packet/in/InteractionPacket.java @@ -1,5 +1,6 @@ package core.net.packet.in; +import api.events.InteractionEvent; import core.cache.def.impl.SceneryDefinition; import core.cache.def.impl.VarbitDefinition; import core.game.content.quest.PluginInteractionManager; @@ -29,6 +30,7 @@ import rs09.game.world.repository.Repository; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.function.IntPredicate; /** diff --git a/Server/src/main/java/core/tools/mysql/DatabaseManager.java b/Server/src/main/java/core/tools/mysql/DatabaseManager.java index d21ab3c6a..a3210ccaa 100644 --- a/Server/src/main/java/core/tools/mysql/DatabaseManager.java +++ b/Server/src/main/java/core/tools/mysql/DatabaseManager.java @@ -25,6 +25,7 @@ public class DatabaseManager { public DatabaseManager connect() { try { + Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection = DriverManager.getConnection("jdbc:mysql://" + db.host() + "/" + db.name() + "?useTimezone=true&serverTimezone=UTC", db.username(), db.password()); connections.put(db.name(), connection); @@ -37,6 +38,8 @@ public class DatabaseManager { SystemLogger.logErr("Couldn't connect to the database."); e.printStackTrace(); ServerConstants.MYSQL = false; + } catch (ClassNotFoundException e) { + e.printStackTrace(); } return this; } diff --git a/Server/src/main/kotlin/api/ContentAPI.kt b/Server/src/main/kotlin/api/ContentAPI.kt index 0894d2833..9b0016926 100644 --- a/Server/src/main/kotlin/api/ContentAPI.kt +++ b/Server/src/main/kotlin/api/ContentAPI.kt @@ -1379,11 +1379,17 @@ fun announceIfRare(player: Player, item: Item) { */ fun getMasteredSkillNames(player: Player): List { + val hasMastered = player.getSkills().masteredSkills > 0 val masteredSkills = ArrayList() - for ((skillId, skillName) in Skills.SKILL_NAME.withIndex()) { - if(hasLevelStat(player, skillId, 99)){ - masteredSkills.add(skillName) + 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)){ + masteredSkills.add(skillName) + } } } return masteredSkills diff --git a/Server/src/main/kotlin/api/LoginListener.kt b/Server/src/main/kotlin/api/LoginListener.kt new file mode 100644 index 000000000..844e7c629 --- /dev/null +++ b/Server/src/main/kotlin/api/LoginListener.kt @@ -0,0 +1,7 @@ +package api + +import core.game.node.entity.player.Player + +interface LoginListener { + fun login(player: Player) +} \ No newline at end of file diff --git a/Server/src/main/kotlin/api/events/Event.kt b/Server/src/main/kotlin/api/events/Event.kt new file mode 100644 index 000000000..5760e860d --- /dev/null +++ b/Server/src/main/kotlin/api/events/Event.kt @@ -0,0 +1,3 @@ +package api.events + +interface Event {} \ No newline at end of file diff --git a/Server/src/main/kotlin/api/events/EventHook.kt b/Server/src/main/kotlin/api/events/EventHook.kt new file mode 100644 index 000000000..ba274269e --- /dev/null +++ b/Server/src/main/kotlin/api/events/EventHook.kt @@ -0,0 +1,7 @@ +package api.events + +import core.game.node.entity.Entity + +interface EventHook { + fun process(entity: Entity, event: T) +} \ No newline at end of file diff --git a/Server/src/main/kotlin/api/events/Events.kt b/Server/src/main/kotlin/api/events/Events.kt new file mode 100644 index 000000000..ba0d2d51d --- /dev/null +++ b/Server/src/main/kotlin/api/events/Events.kt @@ -0,0 +1,11 @@ +package api.events + +import core.game.node.Node +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.link.TeleportManager.TeleportType + +data class ResourceGatheredEvent(val itemId: Int, val amount: Int, val source: Node) : Event +data class NPCKillEvent(val npc: NPC) : Event +data class TeleportEvent(val type: TeleportType, val source: Int = -1) : Event +data class LitFireEvent(val logId: Int) : Event +data class InteractionEvent(val target: Node, val option: String) : Event \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/Server.kt b/Server/src/main/kotlin/rs09/Server.kt index d030a0ee8..1ec1ed7af 100644 --- a/Server/src/main/kotlin/rs09/Server.kt +++ b/Server/src/main/kotlin/rs09/Server.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import rs09.game.content.global.GlobalKillCounter; import rs09.game.ge.GEAutoStock +import rs09.game.ge.GEDB import rs09.game.system.SystemLogger import rs09.game.system.config.ServerConfigParser import rs09.game.world.GameWorld @@ -34,8 +35,8 @@ object Server { /** * The time stamp of when the server started running. */ - @JvmField - var startTime: Long = 0 + @JvmField + var startTime: Long = 0 var lastHeartbeat = System.currentTimeMillis() diff --git a/Server/src/main/kotlin/rs09/ServerConstants.kt b/Server/src/main/kotlin/rs09/ServerConstants.kt index 82f13e538..7862c4552 100644 --- a/Server/src/main/kotlin/rs09/ServerConstants.kt +++ b/Server/src/main/kotlin/rs09/ServerConstants.kt @@ -167,7 +167,9 @@ class ServerConstants { arrayOf(Location.create(2722, 4886, 0), "quest the golem 1"), arrayOf(Location.create(2704, 5349, 0), "dorgeshuun", "dorg"), arrayOf(Location.create(2711, 10132, 0), "brine rats"), - arrayOf(Location.create(2328, 3677, 0), "piscatoris") + arrayOf(Location.create(2328, 3677, 0), "piscatoris"), + arrayOf(Location.create(2660, 3158, 0), "fishing trawler", "trawler"), + arrayOf(Location.create(2800, 3667, 0), "mountain camp") ) @JvmField diff --git a/Server/src/main/kotlin/rs09/game/Event.kt b/Server/src/main/kotlin/rs09/game/Event.kt new file mode 100644 index 000000000..c6c1e5615 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/Event.kt @@ -0,0 +1,11 @@ +package rs09.game + +import api.events.* + +object Event { + val ResourceGathered = ResourceGatheredEvent::class.java + val NPCKilled = NPCKillEvent::class.java + val Teleport = TeleportEvent::class.java + val FireLit = LitFireEvent::class.java + val Interaction = InteractionEvent::class.java +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt b/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt index 735a61b1e..0d39b9ebb 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt +++ b/Server/src/main/kotlin/rs09/game/ai/general/ScriptAPI.kt @@ -30,8 +30,8 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.rs09.consts.Items import rs09.game.ai.AIRepository +import rs09.game.ge.GrandExchange import rs09.game.ge.GrandExchangeOffer -import rs09.game.ge.OfferManager import rs09.game.interaction.InteractionListener import rs09.game.interaction.InteractionListeners import rs09.game.system.SystemLogger @@ -374,7 +374,9 @@ class ScriptAPI(private val bot: Player) { diffX /= 2 diffY /= 2 } - GameWorld.Pulser.submit(object : MovementPulse(bot, bot.location.transform(diffX, diffY, 0), Pathfinder.SMART) { // if this uses bot.pulseManager.run, it will get cleared when the bot dies and then the bot gets stuck in lumby + GameWorld.Pulser.submit(object : MovementPulse(bot, bot.location.transform(diffX, diffY, 0), Pathfinder.SMART) { + + override fun pulse(): Boolean { return true } @@ -448,10 +450,10 @@ class ScriptAPI(private val bot: Player) { if (ItemDefinition.forId(id).noteId == id){ actualId = Item(id).noteChange } - val canSell = OfferManager.addBotOffer(actualId, itemAmt) + val canSell = GrandExchange.addBotOffer(actualId, itemAmt) if (canSell && saleIsBigNews(actualId, itemAmt)) { SystemLogger.logAI("Offered $itemAmt of $actualId on the GE.") - Repository.sendNews("2009Scape just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") + Repository.sendNews("Fellerscape just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") } bot.bank.remove(Item(id, itemAmt)) bot.bank.refresh() @@ -479,9 +481,9 @@ class ScriptAPI(private val bot: Player) { if (ItemDefinition.forId(actualId).noteId == actualId){ actualId = Item(actualId).noteChange } - val canSell = OfferManager.addBotOffer(actualId, itemAmt) + val canSell = GrandExchange.addBotOffer(actualId, itemAmt) if (canSell && saleIsBigNews(actualId, itemAmt)) { - Repository.sendNews("2009Scape just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") + Repository.sendNews("FellerScape just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") } bot.bank.remove(item) bot.bank.refresh() @@ -508,7 +510,7 @@ class ScriptAPI(private val bot: Player) { if (ItemDefinition.forId(actualId).noteId == actualId){ actualId = Item(actualId).noteChange } - val canSell = OfferManager.addBotOffer(actualId, itemAmt) + val canSell = GrandExchange.addBotOffer(actualId, itemAmt) if (canSell && saleIsBigNews(actualId, itemAmt)) { when (actualId){ 1511 -> continue @@ -517,7 +519,7 @@ class ScriptAPI(private val bot: Player) { 1517 -> continue 1519 -> continue 1521 -> continue - else -> Repository.sendNews("2009Scape just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") + else -> Repository.sendNews("Fellerscape just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") } } bot.bank.remove(item).also { SystemLogger.logAI("$item has been listed on the GE.") } @@ -651,7 +653,7 @@ class ScriptAPI(private val bot: Player) { offer.offeredValue = checkPriceOverrides(itemID) ?: ItemDefinition.forId(itemID).value offer.amount = amount offer.player = bot - OfferManager.dispatch(bot, offer) + //GrandExchange.dispatch(bot, offer) AIRepository.addOffer(bot, offer) var bought: Boolean = false val latch = CountDownLatch(1) diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/Adventurer.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/Adventurer.kt index ad4bd2458..46ea0e1e1 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/Adventurer.kt +++ b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/Adventurer.kt @@ -179,7 +179,7 @@ class Adventurer(val style: CombatStyle): Script() { State.LOOT -> { val items = AIRepository.groundItems[bot] if (items?.isNotEmpty() == true && !bot.inventory.isFull) { - items.forEach { + items.toTypedArray().forEach { scriptAPI.takeNearestGroundItem(it.id) } return diff --git a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SharkCatcher.kt b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SharkCatcher.kt index f1a909d53..34366b032 100644 --- a/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SharkCatcher.kt +++ b/Server/src/main/kotlin/rs09/game/ai/general/scriptrepository/SharkCatcher.kt @@ -9,7 +9,6 @@ import core.tools.RandomFunction import org.rs09.consts.Items import rs09.game.ai.AIPlayer import rs09.game.ai.general.ScriptAPI -import rs09.game.ge.OfferManager import rs09.game.interaction.InteractionListener import rs09.game.interaction.InteractionListeners import kotlin.random.Random @@ -89,7 +88,7 @@ class SharkCatcher : Script() { State.STOP -> { val botAmount = bot.bank.getAmount(Items.RAW_SHARK_383) + 1 - var geAmount = OfferManager.getQuantitySoldForItem(Items.RAW_SHARK_383) + var geAmount = 0//OfferManager.getQuantitySoldForItem(Items.RAW_SHARK_383) val totalAmount = (geAmount + botAmount) + 1 if((totalAmount > limit) && myCounter++ == 300){ bot.randomWalk(5,5) @@ -174,7 +173,7 @@ class SharkCatcher : Script() { State.SELL_GE -> { val botAmount = bot.bank.getAmount(Items.RAW_SHARK_383) + 1 - var geAmount = OfferManager.getQuantitySoldForItem(Items.RAW_SHARK_383) + var geAmount = 0//OfferManager.getQuantitySoldForItem(Items.RAW_SHARK_383) val totalAmount = (geAmount + botAmount) + 1 if(totalAmount > limit){ scriptAPI.walkTo(Location.create(3164, 3487, 0)) diff --git a/Server/src/main/kotlin/rs09/game/content/activity/vinesweeper/Vinesweeper.kt b/Server/src/main/kotlin/rs09/game/content/activity/vinesweeper/Vinesweeper.kt index 4af905440..e9cf73158 100644 --- a/Server/src/main/kotlin/rs09/game/content/activity/vinesweeper/Vinesweeper.kt +++ b/Server/src/main/kotlin/rs09/game/content/activity/vinesweeper/Vinesweeper.kt @@ -305,9 +305,9 @@ class VinesweeperNPC : AbstractNPC { } override fun handleTickActions() { - val dest = seedDestinations.find({sd -> sd.loc == location}) + val dest = seedDestinations.find { sd -> sd.loc == location } if(dest != null) { - for(npc in findLocalNPCs(this, intArrayOf(RABBIT, *FARMERS), 30)) { + for(npc in RegionManager.getRegionPlane(location).npcs) { if(npc is VinesweeperNPC) { npc.seedDestinations.remove(dest) npc.resetWalk() @@ -343,8 +343,8 @@ class VinesweeperNPC : AbstractNPC { override fun getMovementDestination(): Location? { if(seedDestinations.size > 0) { - seedDestinations.sortBy({a -> a.loc.getDistance(location).toInt()}) - return seedDestinations.get(0).loc + seedDestinations.sortBy { a -> a.loc.getDistance(location).toInt() } + return seedDestinations.first().loc } else { return super.getMovementDestination() } diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/DumpContainer.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/DepositAllDialogue.kt similarity index 85% rename from Server/src/main/kotlin/rs09/game/content/dialogue/DumpContainer.kt rename to Server/src/main/kotlin/rs09/game/content/dialogue/DepositAllDialogue.kt index a8953dc05..a62cb2c10 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/DumpContainer.kt +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/DepositAllDialogue.kt @@ -1,13 +1,9 @@ package rs09.game.content.dialogue import api.* -import core.game.container.Container -import core.game.container.impl.EquipmentContainer import core.game.content.dialogue.DialoguePlugin import core.game.node.entity.player.Player -import core.game.node.item.Item import core.plugin.Initializable -import core.plugin.Plugin @Initializable /** @@ -15,11 +11,11 @@ import core.plugin.Plugin * @author Splinter * @author James Triantafylos */ -class DumpContainer(player: Player? = null) : DialoguePlugin(player) { +class DepositAllDialogue(player: Player? = null) : DialoguePlugin(player) { val ID = 628371 override fun newInstance(player: Player?): DialoguePlugin { - return DumpContainer(player) + return DepositAllDialogue(player) } override fun open(vararg args: Any?): Boolean { diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/apeatoll/marim/DagaDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/apeatoll/marim/DagaDialogue.kt index 981c9fe59..9f9efd6fc 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/apeatoll/marim/DagaDialogue.kt +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/apeatoll/marim/DagaDialogue.kt @@ -1,6 +1,6 @@ package rs09.game.content.dialogue.region.apeatoll.marim -import api.addItemOrDrop +import api.* import core.game.content.dialogue.DialoguePlugin import core.game.content.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -9,6 +9,7 @@ import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items import org.rs09.consts.NPCs +import rs09.tools.END_DIALOGUE /** * @author qmqz @@ -30,7 +31,7 @@ class DagaDialogue(player: Player? = null) : DialoguePlugin(player){ 1 -> when (buttonId) { 1 -> player(FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } - 2 -> player(FacialExpression.FRIENDLY, "No, thanks.").also { stage = 99 } + 2 -> player(FacialExpression.FRIENDLY, "No, thanks.").also { stage = END_DIALOGUE } 3 -> player(FacialExpression.HALF_ASKING, "Do you have any Dragon Scimitars in stock?").also { stage = 30 } } @@ -40,16 +41,15 @@ class DagaDialogue(player: Player? = null) : DialoguePlugin(player){ 31 -> options("Yes, please.", "No, thanks.").also { stage++ } 32 -> when (buttonId) { - 1 -> if (player.inventory.contains(Items.COINS_995, 100000)) { - player.inventory.remove(Item(995,100000)) - addItemOrDrop(player, Items.DRAGON_SCIMITAR_4587, 1) + 1 -> if (inInventory(player, Items.COINS_995, 100000)) { + end() + removeItem(player, Item(Items.COINS_995, 100000), Container.INVENTORY) + addItem(player, Items.DRAGON_SCIMITAR_4587, 1) } else { - npcl(FacialExpression.OLD_NORMAL, "Sorry but you don't have enough to buy one, at the moment it costs 100,000 gold coins.").also { stage = 99 } + npcl(FacialExpression.OLD_NORMAL, "Sorry but you don't have enough to buy one, at the moment it costs 100,000 gold coins.").also { stage = END_DIALOGUE } } - 2 -> player(FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } + 2 -> player(FacialExpression.FRIENDLY, "No thanks.").also { stage = END_DIALOGUE } } - - 99 -> end() } return true } diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/etceteria/FishmongerEtcDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/etceteria/FishmongerEtcDialogue.kt new file mode 100644 index 000000000..47c24565a --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/etceteria/FishmongerEtcDialogue.kt @@ -0,0 +1,38 @@ +package rs09.game.content.dialogue.region.etceteria + +import api.isQuestComplete +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.NPCs + +/** + * @author qmqz + */ + +@Initializable +class FishmongerEtcDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.FRIENDLY,"Welcome, ${player.getAttribute("fremennikname","fremmyname")}. My fish is fresher than any in Miscellania.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> end().also { npc.openShop(player) } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FishmongerEtcDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FISHMONGER_1369) + } +} \ No newline at end of file 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 0ee5d6356..68b49d0a0 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 @@ -37,7 +37,7 @@ class SirsalBankerDialogue(player: Player? = null) : DialoguePlugin(player){ 1 -> when (buttonId) { 1 -> end().also { player.bank.open() } 2 -> end().also { player.bankPinManager.openSettings() } - 3 -> end().also { player.playerGrandExchange.openCollectionBox() } + 3 -> end().also { player.exchangeRecords.openCollectionBox() } 4 -> player(FacialExpression.HALF_ASKING, "What is this place?").also { stage = 5 } } diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/miscellania/FishmongerMiscDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/miscellania/FishmongerMiscDialogue.kt index 7102e28c3..4455ed73a 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/miscellania/FishmongerMiscDialogue.kt +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/miscellania/FishmongerMiscDialogue.kt @@ -1,5 +1,6 @@ package rs09.game.content.dialogue.region.miscellania +import api.isQuestComplete import core.game.content.dialogue.DialoguePlugin import core.game.content.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -8,7 +9,6 @@ import core.plugin.Initializable import org.rs09.consts.NPCs /** - * Dialogue for the Fishmonger in Misc. * @author qmqz */ @@ -17,19 +17,17 @@ class FishmongerMiscDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(FacialExpression.FRIENDLY,"Greetings, Sir.", - "Get your fresh fish here!", - "I've heard that the Etceterian fish is stored in", - "a cow shed.") - stage = 0 + if (!isQuestComplete(player, "Throne of Miscellania")) { + npcl(FacialExpression.FRIENDLY,"Greetings, Sir. Get your fresh fish here! I've heard that the Etceterian fish is stored in a cow shed.").also { stage = 0 } + } else { + npcl(FacialExpression.FRIENDLY,"Greetings, Your Highness. Have some fresh fish! I've heard that the Etceterian fish is stored in a cow shed.").also { stage = 0 } + } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> { - end().also { npc.openShop(player) } - } + 0 -> end().also { npc.openShop(player) } } return true } diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/BjornAndEldgrimDialogues.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/BjornAndEldgrimDialogues.kt new file mode 100644 index 000000000..82257325a --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/BjornAndEldgrimDialogues.kt @@ -0,0 +1,52 @@ +package rs09.game.content.dialogue.region.rellekka + +import api.isQuestComplete +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.NPCs +import rs09.tools.END_DIALOGUE + +/** + * @author qmqz + */ + +@Initializable +class BjornAndEldgrimDialogues(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, "Fremennik Trials")) { + player(FacialExpression.FRIENDLY, "Hello there.").also { stage = 0 } + } else if (isQuestComplete(player, "Fremennik Trials")) { + npc(FacialExpression.DRUNK, "Hey! Itsh you again! Whatshyerfashe!").also { stage = 10 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> npcl(FacialExpression.DRUNK, "Hey! scheck it out! Itsh an outerlandub! Yer shud go shpeak to the chieftain!").also { stage++ } + 1 -> player(FacialExpression.ASKING, "The who?").also { stage++ } + 2 -> npcl(FacialExpression.DRUNK, "That guy over there by that stuff! (hic) Yeh, abshoultely! He's da bosh!").also { stage = END_DIALOGUE } + + 10 -> player(FacialExpression.ASKING, "${player.name}?").also { stage++ } + 11 -> npcl(FacialExpression.DRUNK, "Nah nah nah, not them, the other one, whatshyerfashe!").also { stage++ } + 12 -> player(FacialExpression.ASKING, "${player.getAttribute("fremennikname","fremmyname")}?").also { stage++ } + 13 -> npc(FacialExpression.DRUNK, "Thatsh what I said diddle I?").also { stage++ } + 14 -> player(FacialExpression.ASKING, "Um.... okay. I'll leave you to your drinking.").also { stage++ } + 15 -> npc(FacialExpression.DRUNK, "Thanksh pal! You're alright!").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BjornAndEldgrimDialogues(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BJORN_1284, NPCs.ELDGRIM_1285) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/BlaninDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/BlaninDialogue.kt new file mode 100644 index 000000000..690f222f0 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/BlaninDialogue.kt @@ -0,0 +1,44 @@ +package rs09.game.content.dialogue.region.rellekka + +import api.isQuestComplete +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.NPCs +import rs09.tools.END_DIALOGUE + +/** + * @author qmqz + */ + +@Initializable +class BlaninDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, "Fremennik Trials")) { + player(FacialExpression.FRIENDLY, "Good day.").also { stage = 0 } + } else { + player(FacialExpression.FRIENDLY, "That's one less thing to worry about.").also { stage = 10 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> npc(FacialExpression.FRIENDLY, "Good day to you, sir.").also { stage = END_DIALOGUE } + 10 -> npc(FacialExpression.FRIENDLY, "Glad I could help.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BlaninDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BLANIN_2940) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/CouncilWorkerDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/CouncilWorkerDialogue.kt index 72c109e30..a2e919d72 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/CouncilWorkerDialogue.kt +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/CouncilWorkerDialogue.kt @@ -1,6 +1,9 @@ package rs09.game.content.dialogue.region.rellekka +import api.questStage 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 rs09.game.content.quest.members.thefremenniktrials.CouncilWorkerFTDialogue @@ -8,14 +11,19 @@ import rs09.game.content.quest.members.thefremenniktrials.CouncilWorkerFTDialogu @Initializable class CouncilWorkerDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { - if(player.questRepository.getStage("Fremennik Trials") in 1..99){ + npc = args[0] as NPC + if(questStage(player, "Fremennik Trials") in 1..99){ loadFile(CouncilWorkerFTDialogue(1)) + } else { + npc(FacialExpression.FRIENDLY,"'Ello there.").also { stage = 0 } } - npc("'Ello there.") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> end() + } return true } diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/DronDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/DronDialogue.kt new file mode 100644 index 000000000..62c4a0c72 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/DronDialogue.kt @@ -0,0 +1,44 @@ +package rs09.game.content.dialogue.region.rellekka + +import api.isQuestComplete +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.NPCs +import rs09.tools.END_DIALOGUE + +/** + * @author qmqz + */ + +@Initializable +class DronDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, "Making History")) { + player(FacialExpression.FRIENDLY, "Excuse me.").also { stage = 0 } + } else { + player(FacialExpression.FRIENDLY, "Excuse me.").also { stage = 10 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> npc(FacialExpression.ANNOYED, "Leave me or I'll destroy you!").also { stage = END_DIALOGUE } + 10 -> npc(FacialExpression.ANNOYED, "You have your answers, now go away!").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DronDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DRON_2939) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/FishmongerRellekkaDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/FishmongerRellekkaDialogue.kt new file mode 100644 index 000000000..bfa966273 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/FishmongerRellekkaDialogue.kt @@ -0,0 +1,43 @@ +package rs09.game.content.dialogue.region.rellekka + +import api.isQuestComplete +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.NPCs +import rs09.tools.END_DIALOGUE + +/** + * @author qmqz + */ + +@Initializable +class FishmongerRellekkaDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, "Fremennik Trials")) { + npc(FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = END_DIALOGUE } + } else { + npcl(FacialExpression.FRIENDLY,"Hello there, ${player.getAttribute("fremennikname","fremmyname")}. Looking for fresh fish?").also { stage = 0 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> end().also { npc.openShop(player) } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FishmongerRellekkaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FISH_MONGER_1315) + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/FurTraderDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/FurTraderDialogue.kt index ab5e10455..be7c15a98 100644 --- a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/FurTraderDialogue.kt +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/FurTraderDialogue.kt @@ -1,16 +1,16 @@ package rs09.game.content.dialogue.region.rellekka +import api.isQuestComplete 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.NPCs +import rs09.tools.END_DIALOGUE /** * @author qmqz - * Needs the quest to be completed to really do anything else here - * Trade option shouldn't work either unless it's completed */ @Initializable @@ -18,20 +18,17 @@ class FurTraderDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!player.questRepository.isComplete("The Fremennik Trials")) { - npc(FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = 99 } + if (!isQuestComplete(player, "Fremennik Trials")) { + npc(FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = END_DIALOGUE } } else { - npc(FacialExpression.FRIENDLY,"Welcome back ! Have you seen the furs I have today?").also { stage = 10 } + npcl(FacialExpression.FRIENDLY,"Welcome back, ${player.getAttribute("fremennikname","fremmyname")}. Have you seen the furs I have today?").also { stage = 10 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = 99 } 10 -> end().also { npc.openShop(player) } - - 99 -> end() } return true } diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/IngridHradsonDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/IngridHradsonDialogue.kt new file mode 100644 index 000000000..885249b47 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/IngridHradsonDialogue.kt @@ -0,0 +1,49 @@ +package rs09.game.content.dialogue.region.rellekka + +import api.isQuestComplete +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.NPCs +import rs09.tools.END_DIALOGUE + +/** + * @author qmqz + */ + +@Initializable +class IngridHradsonDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, "Fremennik Trials")) { + npcl(FacialExpression.ANNOYED, "Outlander, I have work to be getting on with... Please stop bothering me.").also { stage = END_DIALOGUE } + } else if (isQuestComplete(player, "Fremennik Trials") && !isQuestComplete(player, "Olaf's Quest")) { + npc(FacialExpression.FRIENDLY, "Good afternoon! How do you like our village?").also { stage = 0 } + } else if (isQuestComplete(player, "Fremennik Trials") && isQuestComplete(player, "Olaf's Quest")) { + npc(FacialExpression.ASKING, "Hello again! Have you any word from my husband?").also { stage = 10 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> playerl(FacialExpression.FRIENDLY, "It's lovely. You have a fine collection of goats.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "We polish them every day to get them nice and clean.").also { stage = END_DIALOGUE } + + 10 -> playerl(FacialExpression.HALF_GUILTY, "Err, no, not yet. It takes a while for the messages to reach me you know.").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "Well, when you do, tell him we'll be more than happy to see him again.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return IngridHradsonDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.INGRID_HRADSON_3696) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/LonghallBouncerDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/LonghallBouncerDialogue.kt new file mode 100644 index 000000000..f505a2d6f --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/LonghallBouncerDialogue.kt @@ -0,0 +1,44 @@ +package rs09.game.content.dialogue.region.rellekka + +import api.isQuestComplete +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.NPCs +import rs09.tools.END_DIALOGUE + +/** + * @author qmqz + */ + +@Initializable +class LonghallBouncerDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, "Fremennik Trials")) { + npcl(FacialExpression.ANNOYED, "Hey, outerlander. You can't go through there. Talent only, backstage.").also { stage = END_DIALOGUE } + } else{ + npcl(FacialExpression.ANNOYED, "You can't go through there. Talent only, backstage.").also { stage = 0 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> player(FacialExpression.PANICKED, "But I'm a Bard!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "No you're not. I saw your performance. I was paid well to keep you from ever setting foot on stage here again.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return LonghallBouncerDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LONGHALL_BOUNCER_1278) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/ReesoDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/ReesoDialogue.kt new file mode 100644 index 000000000..439b66123 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/ReesoDialogue.kt @@ -0,0 +1,41 @@ +package rs09.game.content.dialogue.region.rellekka + +import api.isQuestComplete +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.NPCs +import rs09.tools.END_DIALOGUE + +/** + * @author qmqz + */ + +@Initializable +class ReesoDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, "Fremennik Trials")) { + npcl(FacialExpression.ANNOYED, "Please do not disturb me, outerlander. I have much to do.").also { stage = END_DIALOGUE } + } else { + npcl(FacialExpression.STRUGGLE, "Sorry, ${player.getAttribute("fremennikname","fremmyname")}, I must get on with my work.").also { stage = END_DIALOGUE } + } + + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ReesoDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.REESO_3116) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/TalkToChiefDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/TalkToChiefDialogue.kt new file mode 100644 index 000000000..50d4af42c --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/TalkToChiefDialogue.kt @@ -0,0 +1,47 @@ +package rs09.game.content.dialogue.region.rellekka + +import api.isQuestComplete +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.NPCs +import rs09.tools.END_DIALOGUE + +/** + * @author qmqz + * There is no available dialogue for after Fremennik Trials, + * only after Hero's Welcome which isn't in this revision. + */ + +@Initializable +class TalkToChiefDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, "Fremennik Trials")) { + npcl(FacialExpression.ANNOYED, "I cannot speak to you outerlander! Talk to Brundt, the Chieftain!").also { stage = END_DIALOGUE } + } else { + player(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> npc(FacialExpression.FRIENDLY, "Hello to you, too!").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return TalkToChiefDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BORROKAR_1307, NPCs.FREIDIR_1306, NPCs.INGA_1314, + NPCs.JENNELLA_1312, NPCs.LANZIG_1308, NPCs.PONTAK_1309, + NPCs.SASSILIK_1313) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/VolfOlasfsonDialogue.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/VolfOlasfsonDialogue.kt new file mode 100644 index 000000000..f7a2892fb --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/VolfOlasfsonDialogue.kt @@ -0,0 +1,48 @@ +package rs09.game.content.dialogue.region.rellekka + +import api.isQuestComplete +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.NPCs +import rs09.tools.END_DIALOGUE + +/** + * @author qmqz + */ + +@Initializable +class VolfOlasfsonDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (!isQuestComplete(player, "Fremennik Trials")) { + npc(FacialExpression.ANNOYED, "Sorry, outlander, but I have things to be doing.").also { stage = END_DIALOGUE } + } else if (isQuestComplete(player, "Fremennik Trials") && !isQuestComplete(player, "Olaf's Quest")) { + npc(FacialExpression.FRIENDLY, "Hello there. Enjoying the view?").also { stage = 0 } + } else if (isQuestComplete(player, "Fremennik Trials") && isQuestComplete(player, "Olaf's Quest")) { + npcl(FacialExpression.ASKING, "Hello again, friend! Does my father send any word... or treasures like before?").also { stage = 10 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> player(FacialExpression.FRIENDLY, "Yes I am. You have a lovely yurt.").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "Thanks! I exercise it regularly.").also { stage = END_DIALOGUE } + + 10 -> playerl(FacialExpression.HALF_GUILTY, "Not today, but if he does, you will be the first to know.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return VolfOlasfsonDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.VOLF_OLAFSON_3695) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/WaitingOnTheShowDialogues.kt b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/WaitingOnTheShowDialogues.kt new file mode 100644 index 000000000..a9547cbf0 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/content/dialogue/region/rellekka/WaitingOnTheShowDialogues.kt @@ -0,0 +1,35 @@ +package rs09.game.content.dialogue.region.rellekka + +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.NPCs +import rs09.tools.END_DIALOGUE + +/** + * @author qmqz + */ + +@Initializable +class WaitingOnTheShowDialogues(player: Player? = null) : DialoguePlugin(player){ + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npc(FacialExpression.ANNOYED, "Shhh! I'm waiting for the show!").also { stage = END_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return WaitingOnTheShowDialogues(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FRIDGEIR_1277, NPCs.OSPAK_1274, NPCs.STYRMIR_1275) + } +} diff --git a/Server/src/main/kotlin/rs09/game/content/global/action/EquipHandler.kt b/Server/src/main/kotlin/rs09/game/content/global/action/EquipHandler.kt index 691893fb3..df3f3ec32 100644 --- a/Server/src/main/kotlin/rs09/game/content/global/action/EquipHandler.kt +++ b/Server/src/main/kotlin/rs09/game/content/global/action/EquipHandler.kt @@ -6,15 +6,13 @@ import core.game.node.entity.combat.equipment.WeaponInterface import core.game.node.entity.player.Player import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.player.link.diary.DiaryType -import core.game.node.entity.skill.slayer.SlayerEquipmentFlags -import core.game.system.task.Pulse +import rs09.game.node.entity.skill.slayer.SlayerEquipmentFlags import core.game.world.map.zone.ZoneBorders import core.plugin.Plugin import org.rs09.consts.Items import rs09.game.interaction.InteractionListener import rs09.game.interaction.InteractionListeners import rs09.game.system.config.ItemConfigParser -import rs09.game.world.GameWorld /** * Represents the equipment equipping handler plugin. diff --git a/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt b/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt index c34ec6fde..c480faab5 100644 --- a/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt +++ b/Server/src/main/kotlin/rs09/game/content/global/action/PickupHandler.kt @@ -12,7 +12,6 @@ import core.game.node.item.Item import core.game.world.map.RegionManager import core.game.world.update.flag.context.Animation import org.rs09.consts.Items -import rs09.game.ai.AIRepository.Companion.getItems import rs09.game.system.SystemLogger import rs09.game.system.config.GroundSpawnLoader import rs09.game.world.GameWorld diff --git a/Server/src/main/kotlin/rs09/game/content/jobs/JobManager.kt b/Server/src/main/kotlin/rs09/game/content/jobs/JobManager.kt index d9703f8ad..fd64c16d4 100644 --- a/Server/src/main/kotlin/rs09/game/content/jobs/JobManager.kt +++ b/Server/src/main/kotlin/rs09/game/content/jobs/JobManager.kt @@ -38,18 +38,6 @@ object JobManager { player.setAttribute("jobs:amount",new) } - @JvmStatic - fun handleDeath(npc: Int, player: Player) { - val jobId = player.getAttribute("jobs:id",0) - val type = player.getAttribute("jobs:type",0) - if(type == 1) { - val job = SlayingJob.values()[jobId] - if (job.ids.contains(npc)) { - updateJobRemaining(player, 1) - } - } - } - @JvmStatic fun rewardPlayer(player: Player, npc: NPC){ val amt = player.getAttribute("jobs:original_amount",0) @@ -78,6 +66,7 @@ object JobManager { player.dialogueInterpreter.sendDialogue("You still need to kill $needed more ${NPC(SlayingJob.values()[jobId].ids[0]).name.toLowerCase()}.") return } + player.dialogueInterpreter.sendDialogue("Excellent work, thank you! Here's your reward.") } if(!player.inventory.add(Item(995,250 * amt))){ GroundItemManager.create(Item(995,250 * amt),player.centerLocation,player) diff --git a/Server/src/main/kotlin/rs09/game/content/jobs/WorkForInteractionListener.kt b/Server/src/main/kotlin/rs09/game/content/jobs/WorkForInteractionListener.kt index e31193293..81fcb824b 100644 --- a/Server/src/main/kotlin/rs09/game/content/jobs/WorkForInteractionListener.kt +++ b/Server/src/main/kotlin/rs09/game/content/jobs/WorkForInteractionListener.kt @@ -3,6 +3,9 @@ package rs09.game.content.jobs import GatheringJobs import SlayingJob import api.* +import api.events.EventHook +import api.events.NPCKillEvent +import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType @@ -11,15 +14,17 @@ import org.json.simple.JSONObject import org.rs09.consts.Items import rs09.ServerStore import rs09.ServerStore.getInt +import rs09.game.Event import rs09.game.interaction.InteractionListener import rs09.game.system.SystemLogger +import java.util.* import java.util.concurrent.TimeUnit /** * Handles the work-for actions for the NPCs * @author Ceikry */ -class WorkForInteractionListener : InteractionListener() { +class WorkForInteractionListener : InteractionListener(), LoginListener { val possibleWeaponLooks = arrayListOf( Items.BRONZE_SCIMITAR_1321, Items.STEEL_SCIMITAR_1325, @@ -57,6 +62,11 @@ class WorkForInteractionListener : InteractionListener() { val IDs = typeMap.keys.toIntArray() + override fun login(player: Player) { + if(getAttribute(player, "jobs:type", -1) == 1 && getAttribute(player, "jobs:amount", -1) > 0) + player.hook(Event.NPCKilled, JobKillHook) + } + override fun defineListeners() { on(IDs,NPC,"work-for"){ player,node -> @@ -93,6 +103,8 @@ class WorkForInteractionListener : InteractionListener() { 1 -> { val job = SlayingJob.values()[jobId] player.dialogueInterpreter.sendItemMessage(possibleWeaponLooks.random(),"You are assigned to kill $amount ${NPC(job.ids[0]).name.toLowerCase()}") + player.unhook(JobKillHook) //try to unhook just in case they already had it hooked, unlikely but possible. + player.hook(Event.NPCKilled, JobKillHook) } } @@ -113,4 +125,18 @@ class WorkForInteractionListener : InteractionListener() { } return true } + + private object JobKillHook : EventHook + { + override fun process(entity: Entity, event: NPCKillEvent) { + if(entity !is Player) return + + val job = getAttribute(entity, "jobs:id", -1) + val ids = SlayingJob.values()[job].ids + + if(event.npc.id in ids){ + JobManager.updateJobRemaining(entity, 1) + } + } + } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/ChieftanBrundtDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/ChieftanBrundtDialogue.kt index 960905bd6..84001c1a0 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/ChieftanBrundtDialogue.kt +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/ChieftanBrundtDialogue.kt @@ -6,6 +6,7 @@ import core.game.node.entity.player.Player import core.plugin.Initializable import core.game.content.dialogue.DialoguePlugin import core.game.content.dialogue.FacialExpression +import rs09.tools.END_DIALOGUE import kotlin.random.Random @Initializable @@ -39,7 +40,7 @@ class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ stage = 500 return true } - else if(player.getAttribute("fremtrials:votes",0) == 7){ + else if(player.getAttribute("fremtrials:votes",0) >= 7){ npcl(FacialExpression.HAPPY," Greetings again outerlander! How goes your attempts to gain votes with the council of elders?") stage = 545 return true @@ -101,7 +102,7 @@ class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ 302 -> {options("Yes, I am interested.","No, I'm not interested.");stage++} 303 -> when(buttonId){ 1 -> {player("Actually, I would be very interested to hear what you","have to offer.");stage = 310} - 2 -> {player("No, I'm not interested.");stage = 340} + 2 -> player("No, I'm not interested.").also { stage = END_DIALOGUE } } //Yes, I am interested in what you have to say. diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/CouncilWorkerFTDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/CouncilWorkerFTDialogue.kt index e86eb6e1a..8c3663740 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/CouncilWorkerFTDialogue.kt +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/CouncilWorkerFTDialogue.kt @@ -1,11 +1,12 @@ package rs09.game.content.quest.members.thefremenniktrials -import core.game.node.item.Item +import api.* +import org.rs09.consts.Items import rs09.game.content.dialogue.DialogueFile import rs09.tools.END_DIALOGUE import rs09.tools.START_DIALOGUE -val COUNCIL_WORKER = 1287 +const val COUNCIL_WORKER = 1287 class CouncilWorkerFTDialogue(val questStage: Int,var isBeerInteraction: Boolean = false) : DialogueFile(){ @@ -13,26 +14,30 @@ class CouncilWorkerFTDialogue(val questStage: Int,var isBeerInteraction: Boolean if(isBeerInteraction){ when(stage){ - START_DIALOGUE -> {npc(COUNCIL_WORKER,"Oh, thank you much ${if(player?.appearance?.isMale!!) "sir" else "miss"}");stage++} - 1 -> {npc(COUNCIL_WORKER,"Ta very much like. That'll hit the spot nicely.. Here,","You can have this. I picked it up as a souvenir on me","last holz");player?.inventory?.add( - Item(3713) - ); player?.inventory?.remove(Item(1917)); stage = END_DIALOGUE} + START_DIALOGUE -> {npc(COUNCIL_WORKER,"Oh, thank you much ${if(player!!.isMale) "sir" else "miss"}");stage++} + 1 -> { + npc(COUNCIL_WORKER,"Ta very much like. That'll hit the spot nicely.. Here,","You can have this. I picked it up as a souvenir on me","last holz.") + addItem(player!!, Items.STRANGE_OBJECT_3713) + removeItem(player!!, Items.BEER_1917, Container.INVENTORY) + stage = END_DIALOGUE + } } } else if(questStage in 1..99){ when(stage){ - START_DIALOGUE -> if(player?.questRepository?.getStage("Fremennik Trials")!! > 0){ - player("I know this is an odd question, but are you","a member of the elder council?"); stage = 1 - } else { - end() - } - 1 -> {npc(COUNCIL_WORKER,"'fraid not, ${if(player?.appearance?.isMale!!) "sir" else "miss"}"); stage++} + START_DIALOGUE -> + if(questStage(player!!, "Fremennik Trials") > 0) { + player("I know this is an odd question, but are you","a member of the elder council?"); stage = 1 + } else { + end() + } + 1 -> {npc(COUNCIL_WORKER,"'fraid not, ${if(player!!.isMale) "sir" else "miss"}."); stage++} 2 -> {npc(COUNCIL_WORKER,"Say, would you do me a favor? I'm quite parched.","If you bring me a beer, I'll make it worthwhile.");stage++} - 3 -> if(player?.inventory?.containsItem(Item(1917)) == true){ - player("Oh, I have one here! Take it.") - stage = 0 - isBeerInteraction = true + 3 -> if(inInventory(player!!,Items.BEER_1917)) { + player("Oh, I have one here! Take it.") + stage = 0 + isBeerInteraction = true } else { stage = END_DIALOGUE } diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/FishermanDialogue.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/FishermanDialogue.kt index c24ae8093..f77ae9121 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/FishermanDialogue.kt +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/FishermanDialogue.kt @@ -1,11 +1,13 @@ package rs09.game.content.quest.members.thefremenniktrials import api.addItem +import api.isQuestComplete import api.removeItem import core.game.content.dialogue.DialoguePlugin import core.game.content.dialogue.FacialExpression import core.game.node.entity.player.Player import core.plugin.Initializable +import rs09.tools.END_DIALOGUE @Initializable class FishermanDialogue(player: Player? = null) : DialoguePlugin(player) { @@ -35,6 +37,10 @@ class FishermanDialogue(player: Player? = null) : DialoguePlugin(player) { playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find an exotic and extremely odd fish, do you?") stage = 1 return true + } else if (isQuestComplete(player, "Fremennik Trials")){ + player(FacialExpression.FRIENDLY, "Hello there.").also { stage = 100 } + } else if (!isQuestComplete(player, "Fremennik Trials")) { + player(FacialExpression.FRIENDLY, "Hello there.").also { stage = 200 } } return true } @@ -63,6 +69,9 @@ class FishermanDialogue(player: Player? = null) : DialoguePlugin(player) { 17 -> npcl(FacialExpression.ANNOYED,"I don't think so.").also { stage = 1000 } + 100 -> npcl(FacialExpression.FRIENDLY, "Welcome back, ${if (player.isMale) "brother " else "sister "}${player.getAttribute("fremennikname","fremmyname")}. It has been too long since last we spoke! The fish are really biting today!").also { stage = END_DIALOGUE } + 200 -> npcl(FacialExpression.ANNOYED, "Don't talk to me outerlander. Anything you have to say to me, you can say to the Chieftain. Goodbye.").also { stage = END_DIALOGUE } + 1000 -> end() } return true 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 610110617..c5c7b3cb2 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 @@ -8,7 +8,7 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType import core.plugin.Initializable import core.tools.RandomFunction -import rs09.game.content.dialogue.DumpContainer +import rs09.tools.END_DIALOGUE import kotlin.random.Random @Initializable @@ -79,6 +79,9 @@ class PeerTheSeerDialogue(player: Player? = null) : DialoguePlugin(player) { stage = 50 return true } + if (questStage(player, "Fremennik Trials") == 0) { + npc(FacialExpression.SAD,"Uuuh... What was that dark presence I felt?").also { stage = 300 } + } return true } @@ -240,6 +243,9 @@ class PeerTheSeerDialogue(player: Player? = null) : DialoguePlugin(player) { stage = 1000 } + 300 -> npc(FacialExpression.NEUTRAL,"!").also { stage++ } + 301 -> npcl(FacialExpression.NEUTRAL,"Ahem, sorry about that. I have no interest in talking to you just now outerlander.").also { stage = END_DIALOGUE } + 1000 -> end() } return true diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/SeersHouseListeners.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/SeersHouseListeners.kt index 4ae59aa3a..4a78dd024 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/SeersHouseListeners.kt +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/SeersHouseListeners.kt @@ -101,6 +101,11 @@ class SeersHouseListeners : InteractionListener() { if(!player.getAttribute("PeerStarted",false)){ sendDialogue(player,"You should probably talk to the owner of this home.") } + if(getAttribute(player, "fremtrials:peer-vote", false)) + { + sendDialogue(player, "I don't need to go through that again.") + return@on true + } else if(player.getAttribute("PeerRiddle",5) < 5){ player.dialogueInterpreter.open(DoorRiddleDialogue(player), Scenery(WESTDOOR,node.location)) } diff --git a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/TFTInteractionListeners.kt b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/TFTInteractionListeners.kt index 07c75ae41..883141fa8 100644 --- a/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/TFTInteractionListeners.kt +++ b/Server/src/main/kotlin/rs09/game/content/quest/members/thefremenniktrials/TFTInteractionListeners.kt @@ -172,7 +172,7 @@ class TFTInteractionListeners : InteractionListener(){ if(player.getAttribute("onStage",false) && player.getAttribute("lyreConcertPlayed",false) == false){ Pulser.submit(LyreConcertPulse(player,lyre.id)) } - else if(player.questRepository?.getStage("Fremennik Trials")!! < 20 || !player.questRepository?.isComplete("Fremennik Trials")!!){ + else if(questStage(player, "Fremennik Trials")!! < 20 || !isQuestComplete(player, "Fremennik Trials")!!){ player.sendMessage("You lack the knowledge to play this.") return@on true } @@ -224,7 +224,7 @@ class TFTInteractionListeners : InteractionListener(){ } on(THORVALD_LADDER, SCENERY, "climb-down") { player, _ -> - if (player.questRepository.isComplete("Fremennik Trials") || player.getAttribute("fremtrials:thorvald-vote",false)!!) { + if (isQuestComplete(player, "Fremennik Trials") || player.getAttribute("fremtrials:thorvald-vote",false)!!) { player.sendMessage("You have no reason to go back down there.") return@on true } else if (!player.getAttribute("fremtrials:warrior-accepted",false)!!) { @@ -250,7 +250,7 @@ class TFTInteractionListeners : InteractionListener(){ } ClimbActionHandler.climb(player, Animation(828), Location.create(2671, 10099, 2)) - Pulser.submit(KoscheiPulse(player)) + player.pulseManager.run(KoscheiPulse(player)) return@on true } @@ -261,36 +261,25 @@ class TFTInteractionListeners : InteractionListener(){ on(SWAYING_TREE,SCENERY,"cut-branch"){ player, _ -> SkillingTool.getHatchet(player)?.let { Pulser.submit(ChoppingPulse(player)).also { return@on true } } - player.sendMessage("You need an axe which you have the woodcutting level to use to do this.") return@on true } on(SHOPNPCS,NPC,"Trade") { player, npc -> - if(player.questRepository.isComplete("Fremennik Trials")){ + if(isQuestComplete(player, "Fremennik Trials")){ npc.asNpc().openShop(player) } else { when(npc.id){ - NPCs.THORA_THE_BARKEEP_1300 -> sendMessage(player,"Only Fremenniks may buy drinks here.") - NPCs.SKULGRIMEN_1303 -> sendMessage(player,"Only Fremenniks may purchase weapons and armour here.") - NPCs.SIGMUND_THE_MERCHANT_1282 -> sendMessage(player,"Only Fremenniks may trade with this merchant.") - NPCs.YRSA_1301 -> sendMessage(player,"Only Fremenniks may buy clothes here.") - NPCs.FISH_MONGER_1315 -> sendMessage(player,"Only Fremenniks may purchase fish here.") + NPCs.THORA_THE_BARKEEP_1300 -> sendDialogue(player,"Only Fremenniks may buy drinks here.") + NPCs.SKULGRIMEN_1303 -> sendDialogue(player,"Only Fremenniks may purchase weapons and armour here.") + NPCs.SIGMUND_THE_MERCHANT_1282 -> sendDialogue(player,"Only Fremenniks may trade with this merchant.") + NPCs.YRSA_1301 -> sendDialogue(player,"Only Fremenniks may buy clothes here.") + NPCs.FISH_MONGER_1315 -> sendDialogue(player,"Only Fremenniks may purchase fish here.") } } return@on true } - - for(id in FREMENNIK_HELMS){ - onEquip(id){ player, _ -> - if(!player.questRepository.isComplete("Fremennik Trials")){ - sendMessage(player, "You must have completed The Fremennik Trials to equip this.") - return@onEquip false - } - return@onEquip true - } - } } class destRoom(val swx: Int, val swy: Int, val nex: Int, val ney: Int) diff --git a/Server/src/main/kotlin/rs09/game/ge/GEAutoStock.kt b/Server/src/main/kotlin/rs09/game/ge/GEAutoStock.kt index 113e57fa9..5a134a285 100644 --- a/Server/src/main/kotlin/rs09/game/ge/GEAutoStock.kt +++ b/Server/src/main/kotlin/rs09/game/ge/GEAutoStock.kt @@ -21,7 +21,7 @@ class GEAutoStock { val offers = botSave["offers"] as JSONArray for (offer in offers) { val o = offer as JSONObject - OfferManager.addBotOffer(o["item"].toString().toInt(), o["qty"].toString().toInt()) + GrandExchange.addBotOffer(o["item"].toString().toInt(), o["qty"].toString().toInt()) } } } diff --git a/Server/src/main/kotlin/rs09/game/ge/GEDB.kt b/Server/src/main/kotlin/rs09/game/ge/GEDB.kt new file mode 100644 index 000000000..c5676d22d --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/ge/GEDB.kt @@ -0,0 +1,165 @@ +package rs09.game.ge + +import core.cache.def.impl.ItemDefinition +import core.game.ge.OfferState +import core.game.node.entity.player.link.audio.Audio +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.json.simple.parser.JSONParser +import org.sqlite.SQLiteDataSource +import rs09.ServerConstants +import java.io.File +import java.io.FileReader +import java.lang.Integer.min +import java.sql.Connection +import java.sql.DriverManager + +/** + * Collection of methods for interacting with the grand exchange databases + * @author Ceikry + */ +object GEDB { + var pathString = "" + var connection: Connection? = null + + //This needs to be a separate method, so we can call it after the server config has been parsed + fun init() + { + pathString = File(ServerConstants.GRAND_EXCHANGE_DATA_PATH + "grandexchange.db").absolutePath + + //Check if the grandexchange.db file already exists. If not, create it and create the tables. + if(!File(pathString).exists()) + generateAndTransfer() + } + + fun connect(): Connection + { + if(connection == null || connection?.isClosed == true) + { + val ds = SQLiteDataSource() + ds.url = "jdbc:sqlite:$pathString" + connection = ds.connection + } + return connection!! + } + + private fun convertJsonArray(arr: JSONArray): String + { + val sb = StringBuilder() + for ((index, data) in arr.withIndex()) + { + val item = data as JSONObject + sb.append("$index") + sb.append(",") + sb.append(item["id"]) + sb.append(",") + sb.append(item["amount"]) + if(index + 1 < arr.size) + sb.append(":") + } + return sb.toString() + } + + private fun generateAndTransfer() + { + val conn = connect() //for sqlite jdbc, attempting to connect to a non-existing .db file creates it. + val statement = conn.createStatement() + + //table for tracking player offers - replaces offer_dispatch.json + statement.execute( + "CREATE TABLE player_offers(" + + "uid INTEGER PRIMARY KEY ASC," + + "player_uid INTEGER," + + "item_id INTEGER," + + "amount_total INTEGER," + + "amount_complete INTEGER," + + "offered_value INTEGER," + + "time_stamp INTEGER," + + "offer_state INTEGER," + + "is_sale INTEGER," + + "withdraw_items STRING ," + + "slot_index INTEGER," + + "total_coin_xc INTEGER)" + ) + + //table for tracking bot offers - replaces bot_offers.json + statement.execute( + "CREATE TABLE bot_offers(" + + "item_id INTEGER," + + "amount INTEGER)" + ) + + //table for tracking price index data - replaces gedb.xml + statement.execute( + "CREATE TABLE price_index(" + + "item_id INTEGER," + + "value INTEGER," + + "total_value INTEGER," + + "unique_trades INTEGER," + + "last_update INTEGER)" + ) + + //check if the old .json and .xml files exist, and if they do, read them into the sqlite database and remove them. + val playerJson = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "offer_dispatch.json" + val botJson = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "bot_offers.json" + + if(File(playerJson).exists()) + { + val parser = JSONParser() + val reader = FileReader(playerJson) + val saveFile = parser.parse(reader) as JSONObject + + if (saveFile.containsKey("offers")) { + val offers = saveFile["offers"] as JSONArray + + for (offer in offers) { + val o = offer as JSONObject + statement.execute("insert into player_offers(player_uid,item_id,amount_total,amount_complete,offered_value,time_stamp,offer_state,is_sale,withdraw_items,total_coin_xc,slot_index) " + + "values(" + + "${o["playerUID"]}," + + "${o["itemId"]}," + + "${o["amount"]}," + + "${o["completedAmount"]}," + + "${o["offeredValue"]}," + + "${o["timeStamp"]}," + + "${o["offerState"]}," + + "${if (o["sale"] as Boolean) 1 else 0}," + + "'" + convertJsonArray(o["withdrawItems"] as JSONArray) + "'," + + "${o["totalCoinExchange"]}," + + "-1)" + ) + + } + } + + reader.close() + //File(playerJson).delete() + } + + if(File(botJson).exists()) + { + val parser = JSONParser() + val reader = FileReader(botJson) + val saveFile = parser.parse(reader) as JSONObject + + if (saveFile.containsKey("offers")) { + val offers = saveFile["offers"] as JSONArray + + for (offer in offers) { + val o = offer as JSONObject + statement.execute("insert into bot_offers " + + "values(${o["item"]},${o["qty"]})") + } + } + reader.close() + } + + + //price index isn't worth transferring, so we're just going to make a new one. + ItemDefinition.getDefinitions().values.forEach { def -> + if(def.isTradeable){ + statement.execute("insert into price_index(item_id, value) values(${def.id},${def.value})") + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ge/GrandExchange.kt b/Server/src/main/kotlin/rs09/game/ge/GrandExchange.kt index b5e3a3ae5..7f6d954d1 100644 --- a/Server/src/main/kotlin/rs09/game/ge/GrandExchange.kt +++ b/Server/src/main/kotlin/rs09/game/ge/GrandExchange.kt @@ -1,9 +1,23 @@ package rs09.game.ge +import api.getItemName +import api.itemDefinition +import api.sendMessage +import core.game.ge.GrandExchangeDatabase +import core.game.ge.OfferState +import core.game.node.entity.player.Player +import core.game.node.entity.player.info.PlayerDetails +import core.game.node.entity.player.link.audio.Audio import core.game.world.callback.CallBack import rs09.game.system.SystemLogger -import rs09.tools.secondsToTicks +import rs09.game.system.config.ItemConfigParser +import rs09.game.world.repository.Repository +import java.lang.Integer.max +/** + * Handles the exchanging of offers, offer update thread, etc. + * @author Ceikry + */ object GrandExchange : CallBack { /** * Fallback safety check to make sure we don't start the GE twice under any circumstance @@ -14,15 +28,12 @@ object GrandExchange : CallBack { * Initializes the offer manager and spawns an update thread. * @param local whether or not the GE should be the local in-code server rather than some hypothetical remote implementation. */ - fun boot(local: Boolean){ + fun boot(){ if(isRunning) return - if(!local){ - TODO("Remote GE server stuff") - } SystemLogger.logGE("Initializing GE...") - OfferManager.init() + //OfferManager.init() SystemLogger.logGE("GE Initialized.") SystemLogger.logGE("Initializing GE Update Worker") @@ -30,22 +41,257 @@ object GrandExchange : CallBack { val t = Thread { Thread.currentThread().name = "GE Update Worker" while(true) { - SystemLogger.logGE("Updating offers...") - OfferManager.update() - if(OfferManager.dumpDatabase){ - SystemLogger.logGE("Saving GE...") - OfferManager.save() - OfferManager.dumpDatabase = false + val conn = GEDB.connect() + val stmt = conn.createStatement() + val buy_offer = stmt.executeQuery("SELECT * from player_offers where is_sale = 0") + val buyOffers = ArrayList() + + while(buy_offer.next()) + { + val offer = GrandExchangeOffer.fromQuery(buy_offer) + if(!offer.isActive) continue + buyOffers.add(offer) } - Thread.sleep(60_000) //sleep for 60 seconds + + for(offer in buyOffers) + processOffer(offer) + + Thread.sleep(15_000) //sleep for 15 seconds } }.start() isRunning = true } + fun processOffer(offer: GrandExchangeOffer) + { + val conn = GEDB.connect() + if(offer.isActive) + { + val sellStmt = conn.createStatement() + val sell_offer = sellStmt.executeQuery("SELECT * from player_offers where item_id = ${offer.itemID} AND is_sale = 1 AND offer_state < 4 AND NOT offer_state = 2") + var bestOffer: GrandExchangeOffer? = null + + while(sell_offer.next()) + { + val otherOffer = GrandExchangeOffer.fromQuery(sell_offer) + if(!otherOffer.isActive) continue + if(otherOffer.offeredValue > offer.offeredValue) continue + if(bestOffer == null) bestOffer = otherOffer + else if(otherOffer.offeredValue < bestOffer.offeredValue) bestOffer = otherOffer + } + + if(bestOffer != null) + { + val before = offer.amountLeft + exchange(offer,bestOffer) + if(offer.amountLeft != before) + SystemLogger.logGE("Purchased ${before - offer.amountLeft}x ${getItemName(offer.itemID)} @ B:${offer.offeredValue}/S:${bestOffer.offeredValue} gp each.") + } + + if(offer.amountLeft > 0) + { + val botStmt = conn.createStatement() + val bot_offer = botStmt.executeQuery("SELECT * from bot_offers where item_id = ${offer.itemID}") + if(bot_offer.next()) + { + val botOffer = GrandExchangeOffer.fromBotQuery(bot_offer) + val before = offer.amountLeft + exchange(offer, botOffer) + if(offer.amountLeft != before) + SystemLogger.logGE("Purchased FROM BOT ${offer.amountLeft - before}x ${getItemName(offer.itemID)}") + } + } + } + } + + @JvmStatic + fun getRecommendedPrice(itemID: Int, from_bot: Boolean = false): Int { + var base = max(GrandExchangeDatabase.getDatabase()[itemID]?.value ?: 0, getItemDefPrice(itemID)) + if (from_bot) base = (max(BotPrices.getPrice(itemID), base) * 1.10).toInt() + return base + } + + private fun getItemDefPrice(itemID: Int): Int { + return max(itemDefinition(itemID).getConfiguration(ItemConfigParser.GE_PRICE) ?: 0, itemDefinition(itemID).value) + } + + @JvmStatic + fun getOfferStats(itemID: Int, sale: Boolean) : String + { + val conn = GEDB.connect() + + var foundOffers = 0 + var totalAmount = 0 + var bestPrice = 0 + val sb = StringBuilder() + var stmt = conn.createStatement() + + if(!sale) + { + var botAmt = 0 + var botPrice = 0 + val player_offers = stmt.executeQuery("SELECT * from player_offers where item_id = $itemID AND is_sale = 1 AND offer_state < 4 AND NOT offer_state = 2") + + while(player_offers.next()) + { + val o = GrandExchangeOffer.fromQuery(player_offers) + ++foundOffers + totalAmount += o.amountLeft + if(o.offeredValue < bestPrice || bestPrice == 0) + bestPrice = o.offeredValue + } + + stmt = conn.createStatement() + val bot_offers = stmt.executeQuery("SELECT * from bot_offers where item_id = $itemID") + if(bot_offers.next()) + { + val o = GrandExchangeOffer.fromBotQuery(bot_offers) + botAmt = o.amount + botPrice = getRecommendedPrice(itemID, true) + } + + sb.append("Player Stock: $totalAmount ") + sb.append(" Lowest Price: $bestPrice
") + sb.append("-".repeat(50)) + sb.append("
Bot Stock: $botAmt ") + sb.append(" Bot Price: $botPrice") + } + else + { + val buy_offers = stmt.executeQuery("SELECT * from player_offers where item_id = $itemID AND is_sale = 0 AND offer_state < 4 AND NOT offer_state = 2") + + while(buy_offers.next()) + { + val o = GrandExchangeOffer.fromQuery(buy_offers) + ++foundOffers + totalAmount += o.amountLeft + if(o.offeredValue > bestPrice) + bestPrice = o.offeredValue + } + + sb.append("Buy Offers: $totalAmount ") + sb.append("Highest Offer: $bestPrice") + } + + return sb.toString() + } + + fun addBotOffer(itemID: Int, amount: Int): Boolean + { + if (GrandExchangeDatabase.getDatabase()[itemID] == null) + return false + + val offer = GrandExchangeOffer.createBotOffer(itemID, amount) + offer.writeNew() + + return true + } + + fun dispatch(player: Player, offer: GrandExchangeOffer) : Boolean + { + if ( offer.amount < 1 ) + sendMessage(player, "You must choose the quantity you wish to buy!").also { return false } + + if ( offer.offeredValue < 1 ) + sendMessage(player, "You must choose the price you wish to buy for!").also { return false } + + if ( offer.offerState != OfferState.PENDING || offer.uid != 0L ) + { + SystemLogger.logWarn("[GE] DISPATCH FAILURE: ${offer.offerState.name}, UID: ${offer.uid}") + return false + } + + if ( player.isArtificial ) + offer.playerUID = PlayerDetails.getDetails("2009scape").uid.also { offer.isBot = true } + else + offer.playerUID = player.details.uid + + offer.offerState = OfferState.REGISTERED + //player.exchangeRecords.update(offer) + + if (offer.sell) { + Repository.sendNews(player.username + " just offered " + offer.amount + " " + getItemName(offer.itemID) + " on the GE.") + } + + offer.writeNew() + return true + } + + fun exchange(offer: GrandExchangeOffer, other: GrandExchangeOffer) + { + if(offer.sell && other.sell) return //Don't exchange if they are both sell offers + val amount = Integer.min(offer.amount - offer.completedAmount, other.amount - other.completedAmount) + + val seller = if(offer.sell) offer else other + val buyer = if(offer == seller) other else offer + + //If the buyer is buying for less than the seller is selling for, don't exchange + if(seller.offeredValue > buyer.offeredValue) return + + seller.completedAmount += amount + buyer.completedAmount += amount + + if(seller.amountLeft < 1 && seller.player != null) + seller.player!!.audioManager.send(Audio(4042,1,1)) + + seller.addWithdrawItem(995, amount * seller.offeredValue) + buyer.addWithdrawItem(seller.itemID, amount) + + if(seller.offeredValue < buyer.offeredValue) + buyer.addWithdrawItem(995, amount * (buyer.offeredValue - seller.offeredValue)) + + if(seller.amountLeft < 1) + seller.offerState = OfferState.COMPLETED + if(buyer.amountLeft < 1) + buyer.offerState = OfferState.COMPLETED + + seller.totalCoinExchange += seller.offeredValue * amount + buyer.totalCoinExchange += seller.offeredValue * amount + + seller.update() + val sellerPlayer = Repository.uid_map[seller.playerUID] + sellerPlayer?.exchangeRecords?.visualizeRecords() + buyer.update() + val buyerPlayer = Repository.uid_map[buyer.playerUID] + buyerPlayer?.exchangeRecords?.visualizeRecords() + } + + fun getValidOffers(): List + { + val conn = GEDB.connect() + val stmt = conn.createStatement() + val offers = ArrayList() + + val results = stmt.executeQuery("SELECT * FROM player_offers WHERE offer_state < 4 AND NOT offer_state = 2") + while(results.next()) + { + val o = GrandExchangeOffer.fromQuery(results) + offers.add(o) + } + return offers + } + + fun getBotOffers(): List + { + val conn = GEDB.connect() + val stmt = conn.createStatement() + val offers = ArrayList() + + val results = stmt.executeQuery("SELECT item_id,amount FROM bot_offers WHERE amount > 0") + while(results.next()) + { + val o = GrandExchangeOffer.fromBotQuery(results) + offers.add(o) + } + return offers + } + + + override fun call(): Boolean { - boot(true) + GEDB.init() + boot() return true } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ge/GrandExchangeOffer.kt b/Server/src/main/kotlin/rs09/game/ge/GrandExchangeOffer.kt index 6e1038e38..c2c4f6da2 100644 --- a/Server/src/main/kotlin/rs09/game/ge/GrandExchangeOffer.kt +++ b/Server/src/main/kotlin/rs09/game/ge/GrandExchangeOffer.kt @@ -4,12 +4,20 @@ import core.cache.def.impl.ItemDefinition import core.game.ge.OfferState import core.game.node.entity.player.Player import core.game.node.item.Item +import core.net.packet.PacketRepository +import core.net.packet.context.ContainerContext +import core.net.packet.context.GrandExchangeContext +import core.net.packet.out.ContainerPacket +import core.net.packet.out.GrandExchangePacket +import rs09.game.system.SystemLogger +import rs09.game.world.repository.Repository +import java.sql.ResultSet /** - * A struct holding all the data for grand exchange offers as stored in json database. + * A struct holding all the data for grand exchange offers. * - * @author Angle + * @author Ceikry */ class GrandExchangeOffer() { @@ -27,6 +35,7 @@ class GrandExchangeOffer() { var player: Player? = null var playerUID = 0 var isLimitation = false + var isBot = false /** * Gets the total amount of money entered. @@ -49,7 +58,180 @@ class GrandExchangeOffer() { val isActive: Boolean get() = offerState != OfferState.ABORTED && offerState != OfferState.PENDING && offerState != OfferState.COMPLETED && offerState != OfferState.REMOVED + fun addWithdrawItem(id: Int, amount: Int) + { + //loop checking if the item is already present first + for(item in withdraw) + if(item != null && item.id == id) + { + item.amount += amount + return + } + + //if we make it to this point, the item was not present. Loop to find first null slot and stick item there. + for((index,item) in withdraw.withIndex()) + if(item == null) + { + withdraw[index] = Item(id, amount) + return + } + + //send container update packet to player if they exist (are online) + if ( player != null ) + visualize(player) + } + + fun visualize(player: Player?) + { + player ?: return + PacketRepository.send( + GrandExchangePacket::class.java, + GrandExchangeContext(player, index.toByte(), offerState.ordinal.toByte(), itemID.toShort(), + sell, offeredValue, amount, completedAmount, totalCoinExchange) + ) + PacketRepository.send(ContainerPacket::class.java, ContainerContext(player, -1, -1757, 523 + index, withdraw, false)) + } + + fun update() + { + val conn = GEDB.connect() + + if(isBot) + { + val stmt = conn.prepareStatement("UPDATE bot_offers SET amount = ? WHERE item_id = ?") + stmt.setInt(1, amountLeft) + stmt.setInt(2, itemID) + stmt.executeUpdate() + } + else + { + val stmt = conn.prepareStatement("UPDATE player_offers SET amount_complete = ?, offer_state = ?, total_coin_xc = ?, withdraw_items = ?, slot_index = ? WHERE uid = ?") + stmt.setInt(1, completedAmount) + stmt.setInt(2, offerState.ordinal) + stmt.setInt(3, totalCoinExchange) + stmt.setString(4, encodeWithdraw()) + stmt.setInt(5, index) + stmt.setLong(6, uid) + stmt.executeUpdate() + } + } + + /** Called when writing a brand new offer to the database. Should not be used under any other circumstance **/ + fun writeNew() + { + val conn = GEDB.connect() + + if(isBot) + { + val stmt = conn.createStatement() + val result = stmt.executeQuery("SELECT * from bot_offers where item_id = $itemID") + val isExists = result.next() + + if(isExists) + { + val oldAmount = result.getInt("amount") + stmt.executeUpdate("UPDATE bot_offers set amount = ${oldAmount + amount} where item_id = $itemID") + } + else + stmt.executeUpdate("INSERT INTO bot_offers(item_id,amount) values($itemID,$amount)") + } + else + { + val stmt = conn.createStatement() + stmt.executeUpdate("INSERT INTO player_offers(player_uid, item_id, amount_total, offered_value, time_stamp, offer_state, is_sale, slot_index) " + + "values($playerUID,$itemID,$amount,$offeredValue,${System.currentTimeMillis()},${offerState.ordinal},${if(sell) 1 else 0}, $index)") + val nowuid = stmt.executeQuery("SELECT last_insert_rowid()") + uid = nowuid.getLong(1) + visualize(player) + } + + } + + private fun encodeWithdraw() : String + { + val sb = StringBuilder() + for((index, item) in withdraw.withIndex()) + { + sb.append(index) + sb.append(",") + if(item == null) + sb.append("null") + else + sb.append(item.id) + sb.append(",") + if(item == null) + sb.append("null") + else + sb.append(item.amount) + + if(index + 1 < withdraw.size) + sb.append(":") + } + + return sb.toString() + } + override fun toString(): String { return "[name=" + ItemDefinition.forId(itemID).name + ", itemId=" + itemID + ", amount=" + amount + ", completedAmount=" + completedAmount + ", offeredValue=" + offeredValue + ", index=" + index + ", sell=" + sell + ", state=" + offerState + ", withdraw=" + withdraw.contentToString() + ", totalCoinExchange=" + totalCoinExchange + ", playerUID=" + playerUID + "]" } + + companion object { + fun fromQuery(result: ResultSet): GrandExchangeOffer + { + val o = GrandExchangeOffer() + o.itemID = result.getInt("item_id") + o.amount = result.getInt("amount_total") + o.completedAmount = result.getInt("amount_complete") + o.offeredValue = result.getInt("offered_value") + o.sell = result.getInt("is_sale") == 1 + o.offerState = OfferState.values()[result.getInt("offer_state")] + o.uid = result.getLong("uid") + o.timeStamp = result.getLong("time_stamp") + + val itemString = result.getString("withdraw_items") + if(itemString != null && itemString.isNotEmpty()) { + val items = itemString.split(":") + for (item in items) { + if(item.isEmpty()) continue + val tokens = item.split(",") + val index = tokens[0].toInt() + if (tokens[1] == "null") continue //Skip null slots + o.withdraw[index] = Item(tokens[1].toInt(), tokens[2].toInt()) + } + } + + o.totalCoinExchange = result.getInt("total_coin_xc") + o.playerUID = result.getInt("player_uid") + o.index = result.getInt("slot_index") + + if(Repository.uid_map[o.playerUID] != null) + o.player = Repository.uid_map[o.playerUID] + + return o + } + + fun fromBotQuery(result: ResultSet): GrandExchangeOffer + { + val o = GrandExchangeOffer() + o.sell = true + o.amount = result.getInt("amount") + o.offerState = OfferState.REGISTERED + o.itemID = result.getInt("item_id") + o.offeredValue = GrandExchange.getRecommendedPrice(o.itemID, true) + o.isBot = true + return o + } + + fun createBotOffer(itemId: Int, amount: Int, sale: Boolean = true) : GrandExchangeOffer + { + val o = GrandExchangeOffer() + o.sell = sale + o.itemID = itemId + o.amount = amount + o.offeredValue = GrandExchange.getRecommendedPrice(itemId, true) + o.offerState = OfferState.REGISTERED + o.isBot = true + return o + } + } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ge/GrandExchangeRecords.kt b/Server/src/main/kotlin/rs09/game/ge/GrandExchangeRecords.kt new file mode 100644 index 000000000..718ad4218 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/ge/GrandExchangeRecords.kt @@ -0,0 +1,242 @@ +package rs09.game.ge + +import api.* +import core.cache.def.impl.ItemDefinition +import core.game.component.CloseEvent +import core.game.component.Component +import core.game.component.InterfaceType +import core.game.container.Container +import core.game.container.ContainerEvent +import core.game.container.ContainerListener +import core.game.container.access.BitregisterAssembler +import core.game.container.access.InterfaceContainer +import core.game.ge.GEItemSet +import core.game.ge.GrandExchangeDatabase +import core.game.ge.GrandExchangeEntry +import core.game.ge.OfferState +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.audio.Audio +import core.game.node.item.Item +import core.game.system.monitor.PlayerMonitor +import core.net.packet.PacketRepository +import core.net.packet.context.ConfigContext +import core.net.packet.context.ContainerContext +import core.net.packet.out.Config +import core.net.packet.out.ContainerPacket +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.rs09.consts.Components +import org.rs09.consts.Items +import rs09.game.ai.AIPlayer +import rs09.game.system.SystemLogger +import java.text.DecimalFormat +import java.text.NumberFormat +import java.util.* + + +/** + * Handles the grand exchange interfaces for the player. + * + * @author Emperor + * @author Angle + */ + +class GrandExchangeRecords(private val player: Player) { + var history = arrayOfNulls(5) + val offerRecords = arrayOfNulls(6) + + /** + * Opens the collection box. + */ + fun openCollectionBox() { + if (!player.bankPinManager.isUnlocked) { + player.bankPinManager.openType(3) + return + } + player.interfaceManager.openComponent(Components.STOCKCOLLECT_109) + player.packetDispatch.sendAccessMask(6, 18, 109, 0, 2) + player.packetDispatch.sendAccessMask(6, 23, 109, 0, 2) + player.packetDispatch.sendAccessMask(6, 28, 109, 0, 2) + player.packetDispatch.sendAccessMask(6, 36, 109, 0, 2) + player.packetDispatch.sendAccessMask(6, 44, 109, 0, 2) + player.packetDispatch.sendAccessMask(6, 52, 109, 0, 2) + + visualizeRecords() + } + + fun visualizeRecords() + { + val conn = GEDB.connect() + val stmt = conn.createStatement() + + val sb = StringBuilder() + + for (record in offerRecords) { + if (record != null) { + val offer_raw = stmt.executeQuery("select * from player_offers where uid = ${record.uid}") + if(offer_raw.next()) + { + val offer = GrandExchangeOffer.fromQuery(offer_raw) + if(offer.offerState == OfferState.REMOVED) continue + offer.index = record.slot + offer.visualize(player) + } + } + } + SystemLogger.logInfo(sb.toString()) + } + + fun getOffer(index: Int) : GrandExchangeOffer? + { + if(index == -1) return getOffer(null) + return getOffer(offerRecords[index]) + } + + fun getOffer(record: OfferRecord?) : GrandExchangeOffer? + { + record ?: return null + val conn = GEDB.connect() + val stmt = conn.createStatement() + val offer_raw = stmt.executeQuery("select * from player_offers where uid = ${record.uid}") + if(offer_raw.next()) + { + val offer = GrandExchangeOffer.fromQuery(offer_raw) + offer.index = record.slot + return offer + } + return null + } + + /** + * Opens the history log. + * + * @param p The player to open it for. + */ + fun openHistoryLog(p: Player) { + p.interfaceManager.open(Component(643)) + for (i in history.indices) { + val o: GrandExchangeOffer? = history[i] + if (o == null) { + for (j in 0..3) { + p.packetDispatch.sendString("-", 643, 25 + i + j * 5) + } + continue + } + p.packetDispatch.sendString(if (o.sell) "You sold" else "You bought", 643, 25 + i) + p.packetDispatch.sendString( + NumberFormat.getNumberInstance(Locale.US).format(o.completedAmount.toLong()), + 643, + 30 + i + ) + p.packetDispatch.sendString(ItemDefinition.forId(o.itemID).name, 643, 35 + i) + p.packetDispatch.sendString( + NumberFormat.getNumberInstance(Locale.US).format(o.totalCoinExchange.toLong()) + " gp", 643, 40 + i + ) + } + } + + fun parse(geData: JSONObject) { + /** + * Read offers from the database + */ + val conn = GEDB.connect() + val stmt = conn.createStatement() + val offer_records = stmt.executeQuery("SELECT * from player_offers where player_uid = ${player.details.uid} AND offer_state < 6") + + val needsIndex = ArrayDeque() + + while (offer_records.next()) { + val offer = GrandExchangeOffer.fromQuery(offer_records) + if (offer.index == -1) //used to index old (converted from JSON) offers + needsIndex.push(offer) + else + offerRecords[offer.index] = OfferRecord(offer.uid, offer.index) + } + + if (needsIndex.isNotEmpty()) { + for ((index, offer) in offerRecords.withIndex()) { + if (offer == null) { + val o = needsIndex.pop() + o.index = index + offerRecords[o.index] = OfferRecord(o.uid, o.index) + o.update() //write the new index to the database + } + } + + while(needsIndex.isNotEmpty()) //If we enter this loop, there were more offers than can fit inside the 6 slots. This should never happen - at least, not anymore. Can't speak for JSON offers. + { + val o = needsIndex.pop() + SystemLogger.logGE("[WARN] PLAYER HAD EXTRA OFFER - RECOMMEND IMMEDIATE REFUND OF CONTENTS -> OFFER UID: ${o.uid}") + SystemLogger.logGE("[WARN] AS PER ABOVE MESSAGE, REFUND CONTENTS OF OFFER AND MANUALLY SET offer_state = 6") + } + } + + visualizeRecords() + + /** + * Parse history from JSON + */ + val historyRaw = geData["history"] + if(historyRaw != null){ + val history = historyRaw as JSONArray + for (i in history.indices) { + val offer = history[i] as JSONObject + var o = GrandExchangeOffer() + o.itemID = offer["itemId"].toString().toInt() + o.sell = offer["isSell"] as Boolean + o.totalCoinExchange = (offer["totalCoinExchange"].toString().toInt()) + o.completedAmount = (offer["completedAmount"].toString().toInt()) + history[i] = o + } + } + } + + /** + * Initializes the grand exchange. + */ + fun init() { + // Were trades made while gone? + var updated = false + for (record in offerRecords) { + if (record != null) { + val offer = getOffer(record) ?: continue + if (!updated && (offer.withdraw[0] != null || offer.withdraw[1] != null)) { + updated = true + } + offer.visualize(player) + } + } + if (updated) { + sendMessage(player, "You have items from the Grand Exchange waiting in your collection box.") + } + } + + fun hasActiveOffer(): Boolean { + for (i in offerRecords) { + if (i != null) + return true + } + return false + } + + /** + * Formats the grand exchange. + * + * @return the formatted offer for the SQL database. + */ + fun format(): String? { + var log = "" + for (record in offerRecords) { + if (record != null) { + val offer = getOffer(record) ?: continue + log += offer.itemID.toString() + "," + offer.amount + "," + offer.sell + "|" + } + } + if (log.isNotEmpty() && log[log.length - 1] == '|') { + log = log.substring(0, log.length - 1) + } + return log + } + + data class OfferRecord(val uid: Long, val slot: Int) +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/ge/OfferManager.kt b/Server/src/main/kotlin/rs09/game/ge/OfferManager.kt deleted file mode 100644 index fc722a6be..000000000 --- a/Server/src/main/kotlin/rs09/game/ge/OfferManager.kt +++ /dev/null @@ -1,526 +0,0 @@ -package rs09.game.ge - -import api.* -import rs09.ServerConstants -import core.cache.def.impl.ItemDefinition -import core.game.content.eco.EcoStatus -import core.game.content.eco.EconomyManagement -import core.game.ge.BuyingLimitation -import core.game.ge.GrandExchangeDatabase -import core.game.ge.OfferState -import core.game.node.entity.player.Player -import core.game.node.entity.player.info.PlayerDetails -import core.game.node.entity.player.link.audio.Audio -import core.game.node.item.Item -import rs09.game.system.SystemLogger -import core.game.system.task.Pulse -import rs09.game.world.GameWorld -import core.game.world.callback.CallBack -import rs09.game.world.repository.Repository -import core.net.packet.PacketRepository -import core.net.packet.context.ContainerContext -import core.net.packet.out.ContainerPacket -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import rs09.game.ai.AIPlayer -import rs09.game.system.config.ItemConfigParser -import java.io.File -import java.io.FileReader -import java.io.FileWriter -import java.io.IOException -import java.lang.Integer.max -import java.lang.Integer.min -import java.util.* -import java.util.concurrent.locks.ReentrantLock -import javax.script.ScriptEngineManager -import kotlin.collections.ArrayList - -object OfferManager { - /** - * The update notification. - */ - private const val UPDATE_NOTIFICATION = "One or more of your grand exchange offers have been updated." - - /** - * The database path. - */ - private val DB_PATH = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "offer_dispatch.json" - - /** - * Bot DB path - */ - private val BOT_DB_PATH = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "bot_offers.json" - - /** - * The offset of the offer UIDs. - */ - private var offsetUID: Long = 1 - - /** - * The mapping of all current offers. Stored in multiple maps. - * - * First map is offsetID, Offer - * Second is itemID, offsetID, Offer - * Final is playerID, offsetID, Offer - */ - @JvmStatic - val OFFER_MAPPING: MutableMap = HashMap() - val OFFERS_BY_ITEMID: MutableMap> = HashMap() - private val GE_OFFER_LOCK = ReentrantLock() - - /** - * Bot offers are sorted by itemID. - * the second int shows the offer amount. Negative is buying positive selling. - */ - public val BOT_OFFERS: HashMap = HashMap() - - /** - * If the database should be dumped. - */ - public var dumpDatabase = false - - /** - * Initializes the Grand Exchange. - */ - fun init() { - GE_OFFER_LOCK.lock() - val file = File(DB_PATH) - - if(file.exists() && file.length() != 0L) { - val parser = JSONParser() - val reader: FileReader = FileReader(DB_PATH) - val saveFile = parser.parse(reader) as JSONObject - - offsetUID = saveFile["offsetUID"].toString().toLong() - - if (saveFile.containsKey("offers")) { - val offers = saveFile["offers"] as JSONArray - for (offer in offers) { - val o = offer as JSONObject - val no = GrandExchangeOffer() - no.itemID = o["itemId"].toString().toInt() - no.sell = o["sale"] as Boolean - no.offeredValue = o["offeredValue"].toString().toInt() - no.amount = o["amount"].toString().toInt() - no.timeStamp = o["timeStamp"].toString().toLong() - no.uid = o["uid"].toString().toLong() - no.completedAmount = o["completedAmount"].toString().toInt() - no.playerUID = o["playerUID"].toString().toInt() - no.offerState = OfferState.values()[o["offerState"].toString().toInt()] - no.totalCoinExchange = o["totalCoinExchange"].toString().toInt() - val withdrawData = o["withdrawItems"] as JSONArray - for ((index, data) in withdrawData.withIndex()) { - val item = data as JSONObject - val it = Item(item["id"].toString().toInt(), item["amount"].toString().toInt()) - no.withdraw[index] = it - } - addEntry(no) - } - } - } - - if(File(BOT_DB_PATH).exists()) { - try { - val botReader: FileReader = FileReader(BOT_DB_PATH) - val botSave = JSONParser().parse(botReader) as JSONObject - if (botSave.containsKey("offers")) { - val offers = botSave["offers"] as JSONArray - for (offer in offers) { - val o = offer as JSONObject - addBotOffer(o["item"].toString().toInt(), o["qty"].toString().toInt()) - } - } - } catch (e: IOException) { - GE_OFFER_LOCK.unlock() - SystemLogger.logWarn("Unable to load bot offers. Perhaps it doesn't exist?") - } - } - GE_OFFER_LOCK.unlock() - } - - fun update(){ - for (offer in OFFER_MAPPING.values) { - if (offer.isActive) { - updateOffer(offer) - } - } - } - - fun buyFromBots(offer: GrandExchangeOffer) { - if (BOT_OFFERS[offer.itemID] == null) { - return - } - val botPrice = getRecommendedPrice(offer.itemID, true) - if (offer.offeredValue < botPrice) { - return - } - val amount = min(BOT_OFFERS[offer.itemID]!!, getBuylimitAmount(offer)) - val botOffer = GrandExchangeOffer() - botOffer.sell = true - botOffer.amount = amount - botOffer.offerState = OfferState.REGISTERED - botOffer.offeredValue = botPrice - exchange(offer, botOffer) - BOT_OFFERS[offer.itemID] = BOT_OFFERS[offer.itemID]!! - amount - } - - private fun buyFromBotsWithItem(itemID: Int) { - if (OFFERS_BY_ITEMID[itemID] == null || BOT_OFFERS[itemID] == null) { - return - } - for (trade in OFFERS_BY_ITEMID[itemID]!!) { - if (!trade.sell) { - buyFromBots(trade) - } - } - } - - fun addBotOffer(itemID: Int, qty: Int): Boolean { - if (GrandExchangeDatabase.getDatabase()[itemID] == null) { - SystemLogger.logWarn("Bot attempted to sell invalid item $itemID") - return false - } - - if (BOT_OFFERS[itemID] == null) { - BOT_OFFERS[itemID] = qty - } else { - BOT_OFFERS[itemID] = (qty + BOT_OFFERS[itemID]!!) - } - buyFromBotsWithItem(itemID) - return true - } - - fun amtBotsSelling(itemID: Int): Int { - if (BOT_OFFERS[itemID] == null) { - return 0 - } - if (BOT_OFFERS[itemID]!! <= 0) { - return 0 - } - return BOT_OFFERS[itemID]!! - } - - fun setIndex(offerID: Long, idx: Int) { - if (!OFFER_MAPPING.containsKey(offerID)) { - println("ERROR. GE Entry $offerID not found in database. Playerdata may be corrupted.") - return - } - OFFER_MAPPING[offerID]!!.index = idx - } - - fun removeEntry(offer: GrandExchangeOffer): Boolean{ - println("REMOVING ENTRY of ID " + offer.itemID) - GE_OFFER_LOCK.lock() - if (!OFFER_MAPPING.containsKey(offer.uid)){ - GE_OFFER_LOCK.unlock() - return false - } - OFFER_MAPPING.remove(offer.uid) - OFFERS_BY_ITEMID[offer.itemID]?.remove(offer) - GE_OFFER_LOCK.unlock() - return true - } - - @Synchronized fun addEntry(offer: GrandExchangeOffer){ - GE_OFFER_LOCK.lock() - OFFER_MAPPING[offer.uid] = offer - if (!OFFERS_BY_ITEMID.containsKey(offer.itemID)) { - OFFERS_BY_ITEMID[offer.itemID] = mutableListOf() - } - OFFERS_BY_ITEMID[offer.itemID]?.add(offer) - GE_OFFER_LOCK.unlock() - } - - fun getQuantitySoldForItem(item: Int): Int { - var qty = 0 - val offs = getOffersForItem(item) - for (o in offs) { - if (o.sell) { - qty += o.amountLeft - } - } - qty += amtBotsSelling(item) - return qty - } - - @JvmStatic - fun getOffersForItem(item: Int): MutableList { - if (OFFERS_BY_ITEMID.containsKey(item)) { - return OFFERS_BY_ITEMID[item]!! - } - return mutableListOf() - } - - @JvmStatic - fun save(){ - GE_OFFER_LOCK.lock() - val root = JSONObject() - val offers = JSONArray() - - if(OFFER_MAPPING.isEmpty() && BOT_OFFERS.isEmpty()){ - return - } - - for(entry in OFFER_MAPPING){ - val offer = entry.value - if (offer.offerState == OfferState.REMOVED) { - continue - } - val o = JSONObject() - o["uid"] = entry.key.toString() - o["itemId"] = offer.itemID.toString() - o["sale"] = offer.sell - o["amount"] = offer.amount.toString() - o["completedAmount"] = offer.completedAmount.toString() - o["offeredValue"] = offer.offeredValue.toString() - o["timeStamp"] = offer.timeStamp.toString() - o["offerState"] = offer.offerState.ordinal.toString() - o["totalCoinExchange"] = offer.totalCoinExchange.toString() - o["playerUID"] = offer.playerUID.toString() - val withdrawItems = JSONArray() - for(item in offer.withdraw){ - item ?: continue - val it = JSONObject() - it["id"] = item.id.toString() - it["amount"] = item.amount.toString() - withdrawItems.add(it) - } - o["withdrawItems"] = withdrawItems - offers.add(o) - } - root["offsetUID"] = offsetUID.toString() - root["offers"] = offers - - val manager = ScriptEngineManager() - val scriptEngine = manager.getEngineByName("JavaScript") - scriptEngine.put("jsonString", root.toJSONString()) - scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)") - val prettyPrintedJson = scriptEngine["result"] as String - - val botRoot = JSONObject() - val botOffers = JSONArray() - - for ((item, qty) in BOT_OFFERS) { - val o = JSONObject() - o["item"] = item - o["qty"] = qty - botOffers.add(o) - } - botRoot["offers"] = botOffers - - scriptEngine.put("jsonString", botRoot.toJSONString()) - scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)") - val botJson = scriptEngine["result"] as String - - - try { - FileWriter(DB_PATH).use { file -> - file.write(prettyPrintedJson) - file.flush() - file.close() - } - FileWriter(BOT_DB_PATH).use { file -> - file.write(botJson) - file.flush() - file.close() - } - } catch (e: Exception) { - e.printStackTrace() - GE_OFFER_LOCK.unlock() - } - GE_OFFER_LOCK.unlock() - } - - /** - * Dispatches an offer. - * @param player The player. - * @param offer The grand exchange offer. - * @return `True` if successful. - */ - @JvmStatic - fun dispatch(player: Player, offer: GrandExchangeOffer): Boolean { - if (offer.amount < 1) { - player.packetDispatch.sendMessage("You must choose the quantity you wish to buy!") - println("amountthing") - return false - } - if (offer.offeredValue < 1) { - player.packetDispatch.sendMessage("You must choose the price you wish to buy for!") - println("pricethng") - return false - } - if (offer.offerState != OfferState.PENDING || offer.uid != 0L) { - println("pendingthing") - return false - } - if (player.isArtificial) { - offer.playerUID = PlayerDetails.getDetails("2009scape").uid - // Repository.sendNews("2009scape wants " + offer.amount + " " + ItemDefinition.forId(offer.itemID).name.toLowerCase() + " for " + offer.offeredValue + "each.") - } else { - offer.playerUID = player.details.uid - } - offer.uid = nextUID() - offer.offerState = OfferState.REGISTERED - addEntry(offer) - offer.timeStamp = System.currentTimeMillis() - player.playerGrandExchange.update(offer) - if (offer.sell) { - Repository.sendNews(player.username + " just offered " + offer.amount + " " + ItemDefinition.forId(offer.itemID).name.toLowerCase() + " on the GE.") - } - if(player !is AIPlayer) { - SystemLogger.logTrade("[GE] ${player.username} ${if (offer.sell) "listed for sale" else "listed buy offer for"} ${offer.amount} ${ItemDefinition.forId(offer.itemID).name.toLowerCase()}") - } - dumpDatabase = true - return true - } - - /** - * Updates the offer. - * @param offer The G.E. offer to update. - */ - @JvmStatic - fun updateOffer(offer: GrandExchangeOffer) { - if (!offer.isActive) { - return - } - for (o in OFFERS_BY_ITEMID[offer.itemID]!!) { - if (o.sell != offer.sell && o.isActive) { - exchange(offer, o) - if (offer.offerState == OfferState.COMPLETED) { - break - } - } - } - buyFromBots(offer) - } - - private fun getBuylimitAmount(offer: GrandExchangeOffer): Int { - var left = offer.amountLeft - if (!offer.sell && left > 0) { - val maximum = BuyingLimitation.getMaximumBuy(offer.itemID, offer.playerUID) - if (left >= maximum) { - left = maximum - offer.isLimitation = true - } - } - return left - } - - /** - * Exchanges between 2 offers. - * @param offer The grand exchange offer to update. - * @param o The other offer to exchange with. - */ - private fun exchange(offer: GrandExchangeOffer, o: GrandExchangeOffer) { - if (o.sell == offer.sell) { - return - } - if (offer.sell && o.offeredValue < offer.offeredValue || !offer.sell && o.offeredValue > offer.offeredValue) { - return - } - var amount = min(getBuylimitAmount(offer), getBuylimitAmount(o)) - if (amount < 1) { - return - } - var coinDifference = if (offer.sell) o.offeredValue - offer.offeredValue else offer.offeredValue - o.offeredValue - if (coinDifference < 0) { - return - } - if (EconomyManagement.getEcoState() == EcoStatus.DRAINING) { - coinDifference *= (1.0 - EconomyManagement.getModificationRate()).toInt() - } - offer.completedAmount = offer.completedAmount + amount - o.completedAmount = o.completedAmount + amount - offer.offerState = if (offer.amountLeft < 1) OfferState.COMPLETED else OfferState.UPDATED - o.offerState = if (o.amountLeft < 1) OfferState.COMPLETED else OfferState.UPDATED - if (offer.sell) { - if (offer.amountLeft < 1 && offer.player != null) { - offer.player!!.audioManager.send(Audio(4042, 1, 1)) - } - addWithdraw(offer,995, amount * offer.offeredValue) - addWithdraw(o, o.itemID, amount) - BuyingLimitation.updateBoughtAmount(o.itemID, o.playerUID, amount) - } else { - if (o.amountLeft < 1 && o.player != null) { - o.player!!.audioManager.send(Audio(4042, 1, 1)) - } - addWithdraw(offer, offer.itemID, amount) - addWithdraw(o, 995, amount * o.offeredValue) - BuyingLimitation.updateBoughtAmount(offer.itemID, offer.playerUID, amount) - } - if (coinDifference > 0) { - if (offer.sell) { - addWithdraw(o, 995, coinDifference * amount) - } else { - addWithdraw(offer, 995, coinDifference * amount) - } - } - GrandExchangeDatabase.getDatabase()[o.itemID]?.influenceValue(o.offeredValue) - offer.player?.packetDispatch?.sendMessage(UPDATE_NOTIFICATION) - o.player?.packetDispatch?.sendMessage(UPDATE_NOTIFICATION) - o.player?.playerGrandExchange?.update(o) - offer.player?.playerGrandExchange?.update(offer) - dumpDatabase = true - } - - /** - * Adds a new item to withdraw. - * @param itemId The item id. - * @param amount The amount to add. - * @param abort If the item is added due to abort. - */ - fun addWithdraw(offer: GrandExchangeOffer, itemId: Int, amount: Int, abort: Boolean = false) { - if (!abort) { - if (offer.sell) { - if (itemId == 995) { - offer.totalCoinExchange += amount - } - } else { - if (itemId == 995) { - offer.totalCoinExchange -= amount - } else { - offer.totalCoinExchange += offer.offeredValue * amount - } - } - } - for (i in offer.withdraw.indices) { - if (offer.withdraw[i] == null) { - offer.withdraw[i] = Item(itemId, amount) - break - } - if (offer.withdraw[i]!!.id == itemId) { - offer.withdraw[i]!!.amount = offer.withdraw[i]!!.amount + amount - break - } - } - if (offer.player != null) { - PacketRepository.send( - ContainerPacket::class.java, - ContainerContext(offer.player, -1, -1757, 523 + offer.index, offer.withdraw, false) - ) - } - } - - /** - * Gets the next UID. - * @return The UID. - */ - private fun nextUID(): Long { - val id = offsetUID++ - return if (id == 0L) { - nextUID() - } else id - } - - private fun getItemDefPrice(itemID: Int): Int { - return max(itemDefinition(itemID).getConfiguration(ItemConfigParser.GE_PRICE) ?: 0, itemDefinition(itemID).value) - } - - @JvmStatic - fun getRecommendedPrice(itemID: Int, from_bot: Boolean = false): Int { - val base = max(GrandExchangeDatabase.getDatabase()[itemID]?.value ?: 0, getItemDefPrice(itemID)) - return if(from_bot) (base + (base * 0.1).toInt()) - else base - } -} diff --git a/Server/src/main/kotlin/rs09/game/ge/PlayerGrandExchange.kt b/Server/src/main/kotlin/rs09/game/ge/PlayerGrandExchange.kt deleted file mode 100644 index 25f262023..000000000 --- a/Server/src/main/kotlin/rs09/game/ge/PlayerGrandExchange.kt +++ /dev/null @@ -1,700 +0,0 @@ -package rs09.game.ge - -import api.* -import core.cache.def.impl.ItemDefinition -import core.game.component.CloseEvent -import core.game.component.Component -import core.game.component.InterfaceType -import core.game.container.Container -import core.game.container.ContainerEvent -import core.game.container.ContainerListener -import core.game.container.access.BitregisterAssembler -import core.game.container.access.InterfaceContainer -import core.game.ge.GEItemSet -import core.game.ge.GrandExchangeDatabase -import core.game.ge.GrandExchangeEntry -import core.game.ge.OfferState -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.audio.Audio -import core.game.node.item.Item -import core.game.system.monitor.PlayerMonitor -import core.net.packet.PacketRepository -import core.net.packet.context.ConfigContext -import core.net.packet.context.ContainerContext -import core.net.packet.context.GrandExchangeContext -import core.net.packet.out.Config -import core.net.packet.out.ContainerPacket -import core.net.packet.out.GrandExchangePacket -import org.json.simple.JSONArray -import org.json.simple.JSONObject -import org.rs09.consts.Components -import rs09.game.system.SystemLogger -import java.text.DecimalFormat -import java.text.NumberFormat -import java.util.* - - -/** - * Handles the grand exchange interfaces for the player. - * - * @author Emperor - * @author Angle - */ - -class PlayerGrandExchange(private val player: Player) { - - var history = arrayOfNulls(5) - - public val offers = arrayOfNulls(6) - - private var openedIndex = -1 - - public var temporaryOffer: GrandExchangeOffer? = null - - /** - * Opens the Grand Exchange menu. - * - * @NOTICE: The amount of GE boxes is limited at LoginWriteEvent.java (2 or 6 GE boxes, set it 0 for 2, 1 for 6) - */ - fun open() { - if (player.ironmanManager.checkRestriction()) { - return - } - if (!player.bankPinManager.isUnlocked) { - player.bankPinManager.openType(4) - return - } - player.interfaceManager.open(Component(Components.STOCKMARKET_105)).closeEvent = - CloseEvent { player, _ -> - temporaryOffer = null - player.packetDispatch.sendRunScript(571, "") - player.interfaceManager.closeChatbox() - player.interfaceManager.closeSingleTab() - true - } - player.packetDispatch.sendInterfaceConfig(105, 193, true) - player.packetDispatch.sendAccessMask(6, 211, 105, -1, -1) - player.packetDispatch.sendAccessMask(6, 209, 105, -1, -1) - toMainInterface() - } - - /** - * Opens the collection box. - */ - fun openCollectionBox() { - if (!player.bankPinManager.isUnlocked) { - player.bankPinManager.openType(3) - return - } - player.interfaceManager.openComponent(Components.STOCKCOLLECT_109) - player.packetDispatch.sendAccessMask(6, 18, 109, 0, 2) - player.packetDispatch.sendAccessMask(6, 23, 109, 0, 2) - player.packetDispatch.sendAccessMask(6, 28, 109, 0, 2) - player.packetDispatch.sendAccessMask(6, 36, 109, 0, 2) - player.packetDispatch.sendAccessMask(6, 44, 109, 0, 2) - player.packetDispatch.sendAccessMask(6, 52, 109, 0, 2) - for (offer in offers) { - if (offer != null) { - PacketRepository.send( - ContainerPacket::class.java, - ContainerContext(player, -1, -1757, 523 + offer.index, offer.withdraw, false) - ) - } - } - } - - /** - * Opens the history log. - * - * @param p The player to open it for. - */ - fun openHistoryLog(p: Player) { - p.interfaceManager.open(Component(643)) - for (i in history.indices) { - val o: GrandExchangeOffer? = history[i] - if (o == null) { - for (j in 0..3) { - p.packetDispatch.sendString("-", 643, 25 + i + j * 5) - } - continue - } - p.packetDispatch.sendString(if (o.sell) "You sold" else "You bought", 643, 25 + i) - p.packetDispatch.sendString( - NumberFormat.getNumberInstance(Locale.US).format(o.completedAmount.toLong()), - 643, - 30 + i - ) - p.packetDispatch.sendString(ItemDefinition.forId(o.itemID).name, 643, 35 + i) - p.packetDispatch.sendString( - NumberFormat.getNumberInstance(Locale.US).format(o.totalCoinExchange.toLong()) + " gp", 643, 40 + i - ) - } - } - - /** - * Opens the item sets interface. - */ - fun openItemSets() { - player.inventory.listeners.add(object : ContainerListener { - override fun update(c: Container, event: ContainerEvent) { - player.setAttribute( - "container-key", - InterfaceContainer.generateItems( - player, - player.inventory.toArray(), - arrayOf("Examine", "Exchange", "Components"), - 644, - 0, - 7, - 4 - ) - ) - InterfaceContainer.generateItems( - player, - GEItemSet.getItemArray(), - arrayOf("Examine", "Exchange", "Components"), - 645, - 16, - 15, - 10 - ) - } - - override fun refresh(c: Container) { - player.setAttribute( - "container-key", - InterfaceContainer.generateItems( - player, - player.inventory.toArray(), - arrayOf("Examine", "Exchange", "Components"), - 644, - 0, - 7, - 4 - ) - ) - InterfaceContainer.generateItems( - player, - GEItemSet.getItemArray(), - arrayOf("Examine", "Exchange", "Components"), - 645, - 16, - 15, - 10 - ) - } - }) - player.interfaceManager.open(Component(Components.EXCHANGE_ITEMSETS_645)).closeEvent = - CloseEvent { player, _ -> - player.inventory.listeners.removeAt(1) - player.interfaceManager.closeSingleTab() - player.removeAttribute("container-key") - true - } - player.interfaceManager.openSingleTab(Component(Components.EXCHANGE_SETS_SIDE_644)).open(player) - player.setAttribute( - "container-key", - InterfaceContainer.generateItems( - player, - player.inventory.toArray(), - arrayOf("Examine", "Exchange", "Components"), - 644, - 0, - 7, - 4 - ) - ) - InterfaceContainer.generateItems( - player, - GEItemSet.getItemArray(), - arrayOf("Examine", "Exchange", "Components"), - 645, - 16, - 15, - 10 - ) - } - - - /** - * Returns to the main interface. - */ - fun toMainInterface() { - PacketRepository.send(Config::class.java, ConfigContext(player, 1112, -1)) - PacketRepository.send(Config::class.java, ConfigContext(player, 1112, -1)) - player.interfaceManager.closeChatbox() - player.interfaceManager.closeSingleTab() - openedIndex = -1 - } - - fun parse(geData: JSONObject) { - val offersRaw = geData["offers"] - - if (offersRaw != null) { - val offersJSON = offersRaw as JSONArray - for (i in offersJSON.indices) { - val offer = offersJSON[i] as JSONObject - val index = offer["offerIndex"].toString().toInt() - if (index > offers.size - 1) { - SystemLogger.logAlert("Grand Exchange: INVALID OFFER INDEX FOR " + player.name + " INDEX: " + index + ", SKIPPING!") - SystemLogger.logAlert("IF YOU SEE THIS MESSAGE, THE GRAND EXCHANGE NEEDS TO BE FIXED.") - SystemLogger.logAlert("Check your logs, AVENGING ANGLE might have fucked up HARD and now " + player.name + "'s trade with index " + index + "is gone :(") - continue - } - OfferManager.setIndex(offer["offerUID"].toString().toLong(), index) - offers[index] = OfferManager.OFFER_MAPPING[offer["offerUID"].toString().toLong()] - update(offers[index]) - } - } - val historyRaw = geData["history"] - if(historyRaw != null){ - val history = historyRaw as JSONArray - for (i in history.indices) { - val offer = history[i] as JSONObject - var o = GrandExchangeOffer() - o.itemID = offer["itemId"].toString().toInt() - o.sell = offer["isSell"] as Boolean - o.totalCoinExchange = (offer["totalCoinExchange"].toString().toInt()) - o.completedAmount = (offer["completedAmount"].toString().toInt()) - history[i] = o - } - } - } - - /** - * Initializes the grand exchange. - */ - fun init() { - // Were trades made while gone? - var updated = false - for (offer in offers) { - if (offer != null) { - offer.player = player - if (!updated && (offer.withdraw[0] != null || offer.withdraw[1] != null)) { - updated = true - } - update(offer) - } - } - if (updated) { - player.packetDispatch.sendMessage("You have items from the Grand Exchange waiting in your collection box.") - } - } - - /** - * Updates the client with the grand exchange data. - */ - fun update() { - for (offer in offers) { - update(offer) - } - } - - /** - * Updates a grand exchange offer. - * - * @param offer The offer to update. - */ - fun update(offer: GrandExchangeOffer?) { - if (offer != null) { - PacketRepository.send( - GrandExchangePacket::class.java, - GrandExchangeContext(player, offer.index.toByte(), offer.offerState.ordinal.toByte(), offer.itemID.toShort(), - offer.sell, offer.offeredValue, offer.amount, offer.completedAmount, offer.totalCoinExchange) - ) - } - } - - /** - * Constructs a new buy offer. - * - * @param itemId The item id. - */ - fun constructBuy(itemId: Int) { - if (openedIndex < 0) { - return - } - temporaryOffer = GrandExchangeOffer() - temporaryOffer!!.itemID = itemId - temporaryOffer!!.sell = false - var itemDb = GrandExchangeDatabase.getDatabase()[itemId] - if (itemDb == null || !itemDefinition(itemId).isTradeable) { - player.packetDispatch.sendMessage("This item has been blacklisted from the Grand Exchange.") - return - } - temporaryOffer!!.player = player - temporaryOffer!!.amount = 1 - temporaryOffer!!.offeredValue = OfferManager.getRecommendedPrice(itemId) - temporaryOffer!!.index = openedIndex - sendConfiguration(temporaryOffer, false) - } - - // GrandExchangeDatabase.getDatabase()[offer?.itemID] - - /** - * Constructs a new sale offer. - * - * @param item The item to sell. - */ - fun constructSale(item: Item) { - if (openedIndex < 0 || offers[openedIndex] != null) { - return - } - if (item.id == 995) { - player.packetDispatch.sendMessage("You can't offer money!") - return - } - var id = item.id - if (!item.definition.isUnnoted) { - id = item.noteChange - } - var itemDb = GrandExchangeDatabase.getDatabase()[id] - if (itemDb == null || !item.definition.isTradeable) { - player.packetDispatch.sendMessage("This item can't be sold on the Grand Exchange.") - return - } - temporaryOffer = GrandExchangeOffer() - temporaryOffer!!.itemID = id - temporaryOffer!!.sell = true - temporaryOffer!!.player = player - temporaryOffer!!.offeredValue = OfferManager.getRecommendedPrice(id) - temporaryOffer!!.amount = item.amount - temporaryOffer!!.index = openedIndex - sendConfiguration(temporaryOffer, true) - } - - companion object { - /** - * Gets the total amount of this item in the inventory (including noted - * version). - * - * @param player The player. - * @param itemId the item id. - * @return The amount of items + notes in the inventory. - */ - @JvmStatic - fun getInventoryAmount(player: Player, itemId: Int): Int { - val item = Item(itemId) - var amount = player.inventory.getAmount(item) - if (item.definition.noteId > -1) { - amount += player.inventory.getAmount(Item(item.definition.noteId)) - } - return amount - } - } - - /** - * Confirms the current offer. - */ - fun confirmOffer() { - if (openedIndex < 0 || temporaryOffer == null || temporaryOffer!!.amount == 0) { - return - } - if (temporaryOffer!!.offeredValue < 1) { - player.audioManager.send(Audio(4039, 1, 1)) - player.packetDispatch.sendMessage("You can't make an offer for 0 coins.") - return - } - if (temporaryOffer!!.amount > Int.MAX_VALUE / temporaryOffer!!.offeredValue) { - player.audioManager.send(Audio(4039, 1, 1)) - player.packetDispatch.sendMessage("You can't " + (if (temporaryOffer!!.sell) "sell " else "buy ") + " this much!") - return - } - temporaryOffer!!.index = openedIndex - if (temporaryOffer!!.sell) { - val maxAmount = getInventoryAmount(player, temporaryOffer!!.itemID) - if (temporaryOffer!!.amount > maxAmount) { - player.audioManager.send(Audio(4039, 1, 1)) - player.packetDispatch.sendMessage("You do not have enough of this item in your inventory to cover the") - player.packetDispatch.sendMessage("offer.") - return - } - var item: Item - val amountLeft: Int = - temporaryOffer!!.amount - player.inventory.getAmount(Item(temporaryOffer!!.itemID)) - val remove = player.inventory.remove(Item(temporaryOffer!!.itemID, temporaryOffer!!.amount).also { - item = it - }) - var note: Int - if (amountLeft > 0) { - if (item.noteChange.also { note = it } > 0) { - player.inventory.remove(Item(note, amountLeft)) - } else if (remove) { - player.inventory.add(Item(temporaryOffer!!.itemID, temporaryOffer!!.amount - amountLeft)) - return - } - } - if (OfferManager.dispatch(player, temporaryOffer!!)) { - offers[openedIndex] = temporaryOffer - OfferManager.updateOffer(temporaryOffer!!) - } - } else { - val total: Int = temporaryOffer!!.amount * temporaryOffer!!.offeredValue - if (total > player.inventory.getAmount(Item(995))) { - player.audioManager.send(Audio(4039, 1, 1)) - player.packetDispatch.sendMessage("You do not have enough coins to cover the offer.") - return - } - if (OfferManager.dispatch(player, temporaryOffer!!) && player.inventory.remove(Item(995, total))) { - offers[openedIndex] = temporaryOffer - OfferManager.updateOffer(temporaryOffer!!) - } - } - player.monitor.log( - (if (temporaryOffer!!.sell) "selling" else "buying") + " offer => item => " + ItemDefinition.forId( - temporaryOffer!!.itemID - ).name + " => amount => " + temporaryOffer!!.amount + " => price => " + temporaryOffer!!.offeredValue, - PlayerMonitor.GRAND_EXCHANGE_LOG - ) - toMainInterface() - player.audioManager.send(Audio(4043, 1, 1)) - temporaryOffer = null - } - - /** - * Aborts an offer. - * - * @param index The offer index. - */ - fun abort(index: Int) { - val offer: GrandExchangeOffer? = offers[index] - player.packetDispatch.sendMessage("Abort request acknowledged. Please be aware that your offer may") - player.packetDispatch.sendMessage("have already been completed.") - if (offer == null || !offer.isActive) { - return - } - offer.offerState = OfferState.ABORTED - if (offer.sell) { - OfferManager.addWithdraw(offer, offer.itemID, offer.amountLeft, true) - } else { - OfferManager.addWithdraw(offer, 995, offer.amountLeft * offer.offeredValue, true) - } - update(offer) - player.monitor.log( - "aborted offer => item => " + ItemDefinition.forId(offer.itemID).name + " => amount => " + offer.amount + "", - PlayerMonitor.GRAND_EXCHANGE_LOG - ) - OfferManager.dumpDatabase = true - } - - /** - * Removes an offer. - * - * @param index The offer index. - */ - fun remove(index: Int): Boolean { - if (offers[index] == null) { - return false - } - var offer: GrandExchangeOffer = offers[index]!! - if (offer.completedAmount > 0) { - val newHistory = arrayOfNulls(5) - newHistory[0] = offer - System.arraycopy(history, 0, newHistory, 1, 4) - history = newHistory - player.monitor.log( - "offer removed => item => " + ItemDefinition.forId(offer.itemID).name + " => amount => " + offer.amount + " => amount_left => " + offer.amountLeft + " => completed_amount => " + offer.completedAmount + "", - PlayerMonitor.GRAND_EXCHANGE_LOG - ) - } - offer.withdraw = arrayOfNulls(2) - var didExist = OfferManager.removeEntry(offer) - offer.uid = 0 - offer.offerState = OfferState.REMOVED - offers[index] = null - update(offer) - toMainInterface() - return didExist - } - - fun hasActiveOffer(): Boolean { - for (i in offers) { - if (i != null) - return true - } - return false - } - - - /** - * Sends the configuration packets for the offer. - * - * @param offer The grand exchange offer. - * @param sell If it's a selling offer. - */ - fun sendConfiguration(offer: GrandExchangeOffer?, sell: Boolean) { - var entry: GrandExchangeEntry? = GrandExchangeDatabase.getDatabase()[offer?.itemID] - var examine: String? = "" - val formatter = DecimalFormat("###,###,###,###") - val text = StringBuilder() - var lowestOfferValue = 0 - var totalAmounts = 0 - val foundOffers: MutableList = ArrayList() - val foundAmounts: MutableList = ArrayList() - if (offer != null) { - examine = ItemDefinition.forId(offer.itemID).examine - var count = 0 - if (!offer.sell) { - for (o in OfferManager.getOffersForItem(offer.itemID)) { - if (o != null) if (o.sell && o.isActive) { - foundOffers.add(o.offeredValue) - foundAmounts.add(o.amountLeft) - count++ - } - } - val botSales = OfferManager.amtBotsSelling(offer.itemID) - if (botSales > 0) { - foundAmounts.add(botSales) - foundOffers.add(OfferManager.getRecommendedPrice(offer.itemID, true)) - count++ - } - if (foundOffers.isNotEmpty()) { - lowestOfferValue = Collections.min(foundOffers) - totalAmounts = foundAmounts.stream().mapToInt { obj: Int -> obj }.sum() - } - if (count == 0) text.append("There are currently no sell offers for this item.") - else text.append("Lowest price: ") - .append(formatter.format(lowestOfferValue.toLong())).append(" gp") - .append("
Quantity: ") - .append(formatter.format(totalAmounts.toLong())).append("
Users: ") - .append(formatter.format(count.toLong())) - } - } - player.packetDispatch.sendString(if (offer != null && !offer.sell) text.toString() else examine, 105, 142) - var lowPrice = 0 - var highPrice = 0 - val recommendedPrice = OfferManager.getRecommendedPrice(entry?.itemId ?: 0) - if (entry != null) { - lowPrice = (recommendedPrice * 0.95).toInt() - highPrice = (recommendedPrice * 1.05).toInt() - } - player.varpManager.get(1109).setVarbit(0, offer?.itemID ?: -1).send(player) - player.varpManager.get(1110).setVarbit(0, offer?.amount ?: 0).send(player) - player.varpManager.get(1111).setVarbit(0, offer?.offeredValue ?: 0).send(player) - player.varpManager.get(1112).setVarbit(0, openedIndex).send(player) - player.varpManager.get(1113).setVarbit(0, if (sell) 1 else 0).send(player) - player.varpManager.get(1114).setVarbit(0, recommendedPrice).send(player) - player.varpManager.get(1115).setVarbit(0, lowPrice).send(player) - player.varpManager.get(1116).setVarbit(0, highPrice).send(player) - if (offer != null) { - PacketRepository.send( - ContainerPacket::class.java, - ContainerContext(player, -1, -1757, 523 + offer.index, offer.withdraw, false) - ) - } - } - - /** - * Opens the search interface. - */ - fun openSearch() { - val c = Component(389) - c.definition.type = InterfaceType.CS_CHATBOX - c.closeEvent = CloseEvent { player, c -> - player.packetDispatch.sendRunScript(571, "") - true - } - player.packetDispatch.sendRunScript(570, "s", "Grand Exchange Item Search") - player.interfaceManager.openChatbox(c) - } - - /** - * Opens the buying screen. - * - * @param index The offer index. - */ - fun openBuy(index: Int) { - openedIndex = index - sendConfiguration(offers[index], false) - openSearch() - } - - /** - * Opens the selling screen. - */ - fun openSell(index: Int) { - openedIndex = index - sendConfiguration(offers[index], true) - player.interfaceManager.openSingleTab(Component(Components.STOCKSIDE_107)).open(player) - player.packetDispatch.sendRunScript( - 149, "IviiiIsssss", "", "", "", "Examine", "Offer", - -1, 0, 7, 4, 93, 7012370 - ) - BitregisterAssembler.send(player, 107, 18, 0, 27, BitregisterAssembler(0, 1)) - } - - /** - * Withdraws an item. - * - * @param offer The offer to withdraw from. - * @param index The item index. - */ - fun withdraw(offer: GrandExchangeOffer, index: Int) { - val item = offer.withdraw[index] ?: return - if (player.inventory.getMaximumAdd(item) < item.amount) { - val note = item.noteChange - if (note == -1 || player.inventory.getMaximumAdd(Item(note)) < item.amount) { - player.audioManager.send(Audio(4039, 1, 1)) - player.packetDispatch.sendMessage("You do not have enough room in your inventory.") - return - } - player.inventory.add(Item(note, item.amount)) - } else { - player.inventory.add(item) - } - offer.withdraw[index] = null - if (!offer.isActive && offer.withdraw[0] == null && offer.withdraw[1] == null) { - remove(offer.index) - } - player.audioManager.send(Audio(4040, 1, 1)) - PacketRepository.send( - ContainerPacket::class.java, - ContainerContext(player, -1, -1757, 523 + offer.index, offer.withdraw, false) - ) - OfferManager.dumpDatabase = true - } - - /** - * Gets the currently opened offer. - * - * @return The grand exchange offer currently opened. - */ - fun getOpenedOffer(): GrandExchangeOffer? { - return if (openedIndex < 0) { - null - } else offers[openedIndex] - } - - /** - * Views a registered offer. - * - * @param index The index. - */ - fun view(index: Int) { - if (offers[index] == null) { - return - } - openedIndex = index - sendConfiguration(offers[index], false) - } - - /** - * Formats the grand exchange. - * - * @return the formatted offer for the SQL database. - */ - fun format(): String? { - var log = "" - for (offer in offers) { - if (offer != null) { - log += offer.itemID.toString() + "," + offer.amount + "," + offer.sell + "|" - } - } - if (log.isNotEmpty() && log[log.length - 1] == '|') { - log = log.substring(0, log.length - 1) - } - return log - } - - -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt b/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt index beeaec6b3..76113bef4 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/InteractionListener.kt @@ -42,6 +42,13 @@ abstract class InteractionListener : Listener{ InteractionListeners.addUnequip(id,handler) } + fun onEquip(ids: IntArray, handler: (player: Player, node: Node) -> Boolean){ + ids.forEach { id -> InteractionListeners.addEquip(id,handler) } + } + fun onUnequip(ids:IntArray, handler: (player: Player, node: Node) -> Boolean){ + ids.forEach{ id -> InteractionListeners.addUnequip(id,handler) } + } + open fun defineDestinationOverrides(){} fun setDest(type: Int, id: Int,handler: (Entity, Node) -> Location){ diff --git a/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt index 0bcbbb7e6..4031deb01 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/InteractionListeners.kt @@ -1,5 +1,6 @@ package rs09.game.interaction +import api.events.InteractionEvent import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse import core.game.interaction.Option @@ -216,12 +217,14 @@ object InteractionListeners { override fun pulse(): Boolean { if(player.zoneMonitor.interact(node, Option(option, 0))) return true player.faceLocation(node.location) + player.dispatch(InteractionEvent(node, option)) method.invoke(player,node) return true } }) } else { method.invoke(player,node) + player.dispatch(InteractionEvent(node, option)) } return true } diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/ge/ExchangeItemSets.kt b/Server/src/main/kotlin/rs09/game/interaction/inter/ge/ExchangeItemSets.kt new file mode 100644 index 000000000..4c12bcb40 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/inter/ge/ExchangeItemSets.kt @@ -0,0 +1,82 @@ +package rs09.game.interaction.inter.ge + +import api.getAttribute +import api.openInterface +import api.setAttribute +import core.game.component.Component +import core.game.container.Container +import core.game.container.ContainerEvent +import core.game.container.ContainerListener +import core.game.container.access.InterfaceContainer +import core.game.ge.GEItemSet +import core.game.node.entity.player.Player +import org.rs09.consts.Components +import rs09.game.interaction.InterfaceListener + +class ExchangeItemSets : InterfaceListener() { + override fun defineListeners() { + onOpen(Components.EXCHANGE_ITEMSETS_645) { player, _ -> + val listener: InventoryListener + setAttribute(player, "ge-listener", InventoryListener(player).also { listener = it }) + player.inventory.listeners.add(listener) + } + + onClose(Components.EXCHANGE_ITEMSETS_645){player, _ -> + val listener = getAttribute(player, "ge-listener", null) + player.inventory.listeners.remove(listener) + player.interfaceManager.closeSingleTab() + player.removeAttribute("container-key") + player.removeAttribute("ge-listener") + return@onClose true + } + } + + companion object { + @JvmStatic + fun openFor(player: Player) + { + openInterface(player, Components.EXCHANGE_ITEMSETS_645) + player.interfaceManager.openSingleTab(Component(Components.EXCHANGE_SETS_SIDE_644)) + } + } + + private class InventoryListener(val player: Player) : ContainerListener { + init { + createContainers(player) + } + + override fun update(c: Container?, event: ContainerEvent?) + { + createContainers(player) + } + + override fun refresh(c: Container?) { + createContainers(player) + } + + private fun createContainers(player: Player) + { + setAttribute(player, "container-key", + InterfaceContainer.generateItems( + player, + player.inventory.toArray(), + arrayOf("Examine", "Exchange", "Components"), + Components.EXCHANGE_SETS_SIDE_644, + 16, + 15, + 10 + ) + ) + + InterfaceContainer.generateItems( + player, + GEItemSet.getItemArray(), + arrayOf("Examine", "Exchange", "Components"), + Components.EXCHANGE_ITEMSETS_645, + 16, + 15, + 10 + ) + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/inter/ge/StockMarket.kt b/Server/src/main/kotlin/rs09/game/interaction/inter/ge/StockMarket.kt new file mode 100644 index 000000000..eeecedd03 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/inter/ge/StockMarket.kt @@ -0,0 +1,396 @@ +package rs09.game.interaction.inter.ge + +import api.* +import core.game.component.Component +import core.game.container.access.BitregisterAssembler +import core.game.ge.GrandExchangeDatabase +import core.game.ge.GrandExchangeEntry +import core.game.ge.OfferState +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.audio.Audio +import core.game.node.item.Item +import core.net.packet.PacketRepository +import core.net.packet.context.ConfigContext +import core.net.packet.context.ContainerContext +import core.net.packet.out.Config +import core.net.packet.out.ContainerPacket +import org.rs09.consts.Components +import rs09.game.ge.GrandExchange +import rs09.game.ge.GrandExchangeOffer +import rs09.game.ge.GrandExchangeRecords +import rs09.game.interaction.InterfaceListener +import rs09.game.system.SystemLogger + +/** + * Handles the grand exchange interface (Stock Market) + * @author Ceikry + */ +class StockMarket : InterfaceListener() { + override fun defineListeners() { + onOpen(Components.STOCKMARKET_105){player, _ -> + player.packetDispatch.sendInterfaceConfig(105, 193, true) + player.packetDispatch.sendAccessMask(6, 211, 105, -1, -1) + player.packetDispatch.sendAccessMask(6, 209, 105, -1, -1) + player.varpManager.get(1112).setVarbit(0, -1).send(player) + return@onOpen true + } + + onClose(Components.STOCKMARKET_105){player, _ -> + player.packetDispatch.sendRunScript(571, "") + player.interfaceManager.closeChatbox() + player.interfaceManager.closeSingleTab() + } + + onOpen(Components.OBJDIALOG_389){player, _ -> + player.packetDispatch.sendRunScript(570, "s", "Grand Exchange Item Search") + return@onOpen true + } + + onClose(Components.OBJDIALOG_389){player, _ -> + player.packetDispatch.sendRunScript(5781, "") + return@onClose true + } + + on(Components.STOCKSIDE_107){ player, _, op, button, slot, _ -> + if (button != 18 || slot < 0 || slot > 27) { + return@on false + } + val item = player.inventory[slot] ?: return@on false + when (op) { + 196 -> player.packetDispatch.sendMessage(item.definition.examine) + 155 -> { + val offer = getAttribute(player, "ge-temp", GrandExchangeOffer()) + val index = getAttribute(player, "ge-index", -1) + if(item.id == 995) + { + sendMessage(player, "You can't sell money!") + return@on true + } + var id = item.id + if(!item.definition.isUnnoted) + id = item.noteChange + val itemDb = GrandExchangeDatabase.getDatabase()[id] + if(itemDb == null || !item.definition.isTradeable) + { + sendMessage(player, "This item can't be sold on the Grand Exchange.") + return@on true + } + offer.itemID = id + offer.sell = true + offer.player = player + offer.offeredValue = GrandExchange.getRecommendedPrice(id) + offer.amount = item.amount + offer.index = index + updateVarbits(player, offer, index, true) + setAttribute(player, "ge-temp", offer) + player.packetDispatch.sendString(GrandExchange.getOfferStats(id, true), 105, 142) + } + } + return@on true + } + + on(Components.STOCKMARKET_105){player, _, op, button, _, _ -> + val tempOffer = getAttribute(player, "ge-temp", GrandExchangeOffer()) + var openedIndex = getAttribute(player, "ge-index", -1) + var openedOffer = player.exchangeRecords.getOffer(openedIndex) + + when(button) + { + 209,211 -> if (openedOffer == null){ + SystemLogger.logGE("[WARN] Player tried to withdraw item with null openedOffer!") + return@on false + } else withdraw(player, openedOffer, (button - 209) shr 1) + 190 -> confirmOffer(player, tempOffer, openedIndex).also { return@on true } + 194 -> player.interfaceManager.openChatbox(Components.OBJDIALOG_389) + 203 -> abortOffer(player, openedOffer) + 18,34,50,69,88,107 -> { + openedIndex = (button - 18) shr 4 + openedOffer = player.exchangeRecords.getOffer(openedIndex) + if(op == 205) abortOffer(player, openedOffer) + else updateVarbits(player, openedOffer, openedIndex) + } + 30,46,62,81,100,119 -> { + openedIndex = (button - 30) shr 4 + openedOffer = player.exchangeRecords.getOffer(openedIndex) + updateVarbits(player, openedOffer, openedIndex) + player.interfaceManager.openChatbox(Components.OBJDIALOG_389) + } + 31,47,63,82,101,120 -> { + openedIndex = (button - 31) shr 4 + openedOffer = player.exchangeRecords.getOffer(openedIndex) + updateVarbits(player, openedOffer, openedIndex, true) + player.interfaceManager.openSingleTab(Component(Components.STOCKSIDE_107)).open(player) + player.packetDispatch.sendRunScript( + 149, "IviiiIsssss", "", "", "", "Examine", "Offer", + -1, 0, 7, 4, 93, 7012370 + ) + BitregisterAssembler.send(player, 107, 18, 0, 27, BitregisterAssembler(0, 1)) + } + 157 -> updateOfferAmount(player, tempOffer, tempOffer.amount - 1) + 159 -> updateOfferAmount(player, tempOffer, tempOffer.amount + 1) + 162 -> updateOfferAmount(player, tempOffer, if(tempOffer.sell) 1 else tempOffer.amount + 1) + 164 -> updateOfferAmount(player, tempOffer, if(tempOffer.sell) 10 else tempOffer.amount + 10) + 166 -> updateOfferAmount(player, tempOffer, if(tempOffer.sell) 100 else tempOffer.amount + 100) + 168 -> { + val amt = + if(tempOffer.sell) + getInventoryAmount(player, tempOffer.itemID) + else + tempOffer.amount + 1000 + + updateOfferAmount(player, tempOffer, amt) + } + 170 -> sendInputDialogue(player, false, "Enter the amount:") { value -> + if (player.interfaceManager.chatbox.id == 389) { + player.interfaceManager.openChatbox(Components.OBJDIALOG_389) + } + var s = value.toString() + s = s.replace("k", "000") + s = s.replace("K", "000") + s = s.replace("m", "000000") + s = s.replace("M", "000000") + updateOfferAmount(player, tempOffer, s.toInt()) + setAttribute(player, "ge-temp", tempOffer) + } + 180 -> updateOfferValue(player, tempOffer, GrandExchange.getRecommendedPrice(tempOffer.itemID)) + 177 -> updateOfferValue(player, tempOffer, (GrandExchange.getRecommendedPrice(tempOffer.itemID) * 0.95).toInt()) + 183 -> updateOfferValue(player, tempOffer, (GrandExchange.getRecommendedPrice(tempOffer.itemID) * 1.05).toInt()) + 171 -> updateOfferValue(player, tempOffer, tempOffer.offeredValue - 1) + 173 -> updateOfferValue(player, tempOffer, tempOffer.offeredValue + 1) + 185 -> sendInputDialogue(player, false, "Enter the amount:"){value -> + if (player.interfaceManager.chatbox.id == 389) { + player.interfaceManager.openChatbox(Components.OBJDIALOG_389) + } + var s = value.toString() + s = s.replace("k", "000") + s = s.replace("K", "000") + s = s.replace("m", "000000") + s = s.replace("M", "000000") + updateOfferValue(player, tempOffer, s.toInt()) + setAttribute(player, "ge-temp", tempOffer) + } + 195 -> closeInterface(player) + 127 -> { + player.interfaceManager.closeSingleTab() + player.interfaceManager.closeChatbox() + } + } + + setAttribute(player, "ge-index", openedIndex) + setAttribute(player, "ge-temp", tempOffer) + return@on true + } + } + + fun updateOfferValue(player: Player, offer: GrandExchangeOffer, newAmt: Int) + { + offer.offeredValue = newAmt + player.varpManager.get(1111).setVarbit(0, newAmt).send(player) + } + + fun updateOfferAmount(player: Player, offer: GrandExchangeOffer, newAmt: Int) + { + offer.amount = newAmt + player.varpManager.get(1110).setVarbit(0, newAmt).send(player) + } + + fun abortOffer(player: Player, offer: GrandExchangeOffer?) + { + if(offer == null) + { + SystemLogger.logWarn("Opened offer was null and was attempted to be aborted!") + return + } + sendMessage(player, "Abort request acknowledged. Please be aware that your offer may") + sendMessage(player, "have already been completed.") + + if(!offer.isActive) + { + SystemLogger.logWarn("Offer ${offer.uid}[${offer.index}]: ${if(offer.sell) "s" else "b"} ${offer.itemID}x ${offer.amount} was NO LONGER active when abort attempted") + return + } + offer.offerState = OfferState.ABORTED + if(offer.sell) + offer.addWithdrawItem(offer.itemID, offer.amountLeft) + else + offer.addWithdrawItem(995, offer.amountLeft * offer.offeredValue) + offer.update() + offer.visualize(player) + } + + fun confirmOffer(player: Player, offer: GrandExchangeOffer, index: Int) + { + if(offer.offeredValue < 1) + { + playAudio(player, Audio(4039, 1, 1)) + sendMessage(player, "You can't make an offer for 0 coins.") + return + } + if(offer.amount > Int.MAX_VALUE / offer.offeredValue) + { + playAudio(player, Audio(4039, 1, 1)) + sendMessage(player, "You can't ${if(offer.sell) "sell" else "buy"} this much!") + return + } + offer.index = index + if(offer.sell) + { + val maxAmt = getInventoryAmount(player, offer.itemID) + if(offer.amount > maxAmt) + { + playAudio(player, Audio(4039, 1, 1)) + sendMessage(player, "You do not have enough of this item in your inventory to cover the") + sendMessage(player, "offer.") + return + } + var item: Item + val amountLeft: Int = offer.amount - amountInInventory(player, offer.itemID) + val remove = removeItem(player, Item(offer.itemID, offer.amount).also { item = it }) + var note: Int + if (amountLeft > 0) { + if (item.noteChange.also { note = it } > 0) { + removeItem(player, Item(note, amountLeft)) + } else if (remove) { + addItem(player, offer.itemID, offer.amount - amountLeft) + return + } + } + if(GrandExchange.dispatch(player, offer)) + { + player.exchangeRecords.offerRecords[offer.index] = GrandExchangeRecords.OfferRecord(offer.uid,offer.index) + player.removeAttribute("ge-temp") + } + else + { + addItem(player, item.id, item.amount) + } + } + else + { + val total = offer.amount * offer.offeredValue + if(!inInventory(player, 995, total)) + { + playAudio(player, Audio(4039, 1, 1)) + sendMessage(player, "You do not have enough coins to cover the offer.") + return + } + if(GrandExchange.dispatch(player, offer) && removeItem(player, Item(995, total))) + { + player.exchangeRecords.offerRecords[offer.index] = GrandExchangeRecords.OfferRecord(offer.uid, offer.index) + player.removeAttribute("ge-temp") + } + } + playAudio(player, Audio(4043, 1, 1)) + offer.visualize(player) + toMainInterface(player) + } + + fun getInventoryAmount(player: Player, itemId: Int): Int { + val item = Item(itemId) + var amount = player.inventory.getAmount(item) + if (item.definition.noteId > -1) { + amount += player.inventory.getAmount(Item(item.definition.noteId)) + } + return amount + } + + companion object { + @JvmStatic + fun openFor(player: Player) + { + if(player.ironmanManager.checkRestriction()) + return + if(!player.bankPinManager.isUnlocked) + { + player.bankPinManager.openType(4) + return + } + + openInterface(player, Components.STOCKMARKET_105) + } + + @JvmStatic + fun updateVarbits(player: Player, offer: GrandExchangeOffer?, index: Int, sale: Boolean? = null) + { + val isSale = sale ?: offer?.sell ?: false + val entry: GrandExchangeEntry? = GrandExchangeDatabase.getDatabase()[offer?.itemID ?: -1] + var lowPrice = 0 + var highPrice = 0 + val recommendedPrice = GrandExchange.getRecommendedPrice(entry?.itemId ?: 0) + if (entry != null) { + lowPrice = (recommendedPrice * 0.95).toInt() + highPrice = (recommendedPrice * 1.05).toInt() + } + player.varpManager.get(1109).setVarbit(0, offer?.itemID ?: -1).send(player) + player.varpManager.get(1110).setVarbit(0, offer?.amount ?: 0).send(player) + player.varpManager.get(1111).setVarbit(0, offer?.offeredValue ?: 0).send(player) + player.varpManager.get(1112).setVarbit(0, index).send(player) + player.varpManager.get(1113).setVarbit(0, if (isSale) 1 else 0).send(player) + player.varpManager.get(1114).setVarbit(0, recommendedPrice).send(player) + player.varpManager.get(1115).setVarbit(0, lowPrice).send(player) + player.varpManager.get(1116).setVarbit(0, highPrice).send(player) + if (offer != null) { + PacketRepository.send( + ContainerPacket::class.java, + ContainerContext(player, -1, -1757, 523 + offer.index, offer.withdraw, false) + ) + } + } + + @JvmStatic + fun withdraw(player: Player, offer: GrandExchangeOffer, index: Int) + { + val item = offer.withdraw[index] + if(item == null) + { + SystemLogger.logWarn("Offer withdraw[$index] is null!") + return + } + + if(hasSpaceFor(player, item)) + { + addItem(player, item.id, item.amount) + } + else + { + val note = item.noteChange + if(note == -1 || !hasSpaceFor(player, Item(note, item.amount))) + { + playAudio(player, Audio(4039, 1, 1)) + sendMessage(player, "You do not have enough room in your inventory.") + return + } + else addItem(player, note, item.amount) + } + + offer.withdraw[index] = null + if (!offer.isActive && offer.withdraw[0] == null && offer.withdraw[1] == null) + { + offer.offerState = OfferState.REMOVED + if(offer.completedAmount > 0) + { + val newHistory = arrayOfNulls(5) + newHistory[0] = offer + System.arraycopy(player.exchangeRecords.history, 0, newHistory, 1, 4) + player.exchangeRecords.history = newHistory + } + player.exchangeRecords.offerRecords[offer.index] = null + toMainInterface(player) + } + offer.update() + offer.visualize(player) + } + + /** + * Returns to the main interface. + */ + fun toMainInterface(player: Player) { + PacketRepository.send(Config::class.java, ConfigContext(player, 1112, -1)) + PacketRepository.send(Config::class.java, ConfigContext(player, 1112, -1)) + player.interfaceManager.closeChatbox() + player.interfaceManager.closeSingleTab() + player.setAttribute("ge-index", -1) + } + + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/BraceletOfClayPlugin.kt b/Server/src/main/kotlin/rs09/game/interaction/item/BraceletOfClayPlugin.kt index 35d3c3ed1..7090cebad 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/item/BraceletOfClayPlugin.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/item/BraceletOfClayPlugin.kt @@ -1,6 +1,7 @@ package rs09.game.interaction.item import api.* +import org.rs09.consts.Items import rs09.game.interaction.InteractionListener /** @@ -9,14 +10,12 @@ import rs09.game.interaction.InteractionListener */ class BraceletOfClayPlugin : InteractionListener() { - val BRACELET = 11074 - override fun defineListeners() { - on(BRACELET,ITEM,"operate"){player,node -> + on(Items.BRACELET_OF_CLAY_11074, ITEM, "operate"){ player, node -> var charge = getCharge(node) if (charge > 28) setCharge(node, 28).also { charge = 28 } - player.sendMessage("You have $charge uses left.") + sendMessage(player, "You have $charge uses left.") return@on true } diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/ItemQuestRequirementListener.kt b/Server/src/main/kotlin/rs09/game/interaction/item/ItemQuestRequirementListener.kt new file mode 100644 index 000000000..4828ad515 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/item/ItemQuestRequirementListener.kt @@ -0,0 +1,118 @@ +package rs09.game.interaction.item + +import api.* +import core.game.node.entity.player.link.quest.QuestRepository +import org.rs09.consts.Items +import rs09.game.interaction.InteractionListener +import rs09.game.interaction.InteractionListeners.run + +class ItemQuestRequirementListener : InteractionListener() { + + private val fremennikIslesEquipment = intArrayOf(Items.HELM_OF_NEITIZNOT_10828, Items.HELM_OF_NEITIZNOT_E_12680, Items.HELM_OF_NEITIZNOT_CHARGED_12681) + private val fremennikTrialsEquipment = intArrayOf(Items.BERSERKER_HELM_3751, Items.BERSERKER_HELM_13408, Items.BERSERKER_HELM_E_12674, Items.BERSERKER_HELM_CHARGED_12675, + Items.FREMENNIK_HELM_3748, Items.WARRIOR_HELM_3753, Items.WARRIOR_HELM_13409, Items.WARRIOR_HELM_CHARGED_12677, Items.WARRIOR_HELM_E_12676, + Items.ARCHER_HELM_3749, Items.ARCHER_HELM_13407, Items.ARCHER_HELM_CHARGED_12673, Items.ARCHER_HELM_E_12672, + Items.ROCK_SHELL_HELM_6128, Items.ROCK_SHELL_HELM_10613, Items.ROCK_SHELL_HELM_13411, + Items.ROCK_SHELL_LEGS_6130, Items.ROCK_SHELL_LEGS_13413, + Items.ROCK_SHELL_PLATE_6129, Items.ROCK_SHELL_PLATE_13412, + Items.ROCK_SHELL_BOOTS_6145, Items.ROCK_SHELL_BOOTS_13421, + Items.ROCK_SHELL_GLOVES_6151, Items.ROCK_SHELL_GLOVES_13424, + Items.SPINED_HELM_6131, Items.SPINED_HELM_10614, Items.SPINED_HELM_13414, + Items.SPINED_BODY_6133, Items.SPINED_BODY_13415, + Items.SPINED_CHAPS_6135, Items.SPINED_CHAPS_13416, + Items.SPINED_GLOVES_6149, Items.SPINED_GLOVES_13423, + Items.SPINED_BOOTS_6143, Items.SPINED_BOOTS_13420, + Items.SKELETAL_HELM_6137, Items.SKELETAL_HELM_10604, Items.SKELETAL_HELM_13417, + Items.SKELETAL_TOP_6139, Items.SKELETAL_TOP_13418, + Items.SKELETAL_BOTTOMS_6141, Items.SKELETAL_BOTTOMS_13419, + Items.SKELETAL_GLOVES_6153, Items.SKELETAL_GLOVES_13425, + Items.SKELETAL_BOOTS_6147, Items.SKELETAL_BOOTS_13422, + Items.FREMENNIK_HAT_3798, Items.FREMENNIK_ROBE_3793, Items.FREMENNIK_SKIRT_3795, Items.GLOVES_3799, Items.FREMENNIK_BOOTS_3791, + Items.FREMENNIK_CLOAK_3759, Items.FREMENNIK_CLOAK_3761, Items.FREMENNIK_CLOAK_3763, Items.FREMENNIK_CLOAK_3765, Items.FREMENNIK_CLOAK_3777, Items.FREMENNIK_CLOAK_3779, + Items.FREMENNIK_CLOAK_3781, Items.FREMENNIK_CLOAK_3783, Items.FREMENNIK_CLOAK_3785, Items.FREMENNIK_CLOAK_3787, Items.FREMENNIK_CLOAK_3789, + Items.FREMENNIK_SHIRT_3767, Items.FREMENNIK_SHIRT_3769, Items.FREMENNIK_SHIRT_3771, Items.FREMENNIK_SHIRT_3773, Items.FREMENNIK_SHIRT_3775, + Items.FREMENNIK_BLADE_3757, Items.FREMENNIK_SHIELD_3758) + + private val fremennikIslesDuringQuestEquipment = intArrayOf(Items.YAK_HIDE_ARMOUR_10822, Items.YAK_HIDE_ARMOUR_10824, Items.FREMENNIK_ROUND_SHIELD_10826) + + private val avasBackpacks = intArrayOf(Items.AVAS_ACCUMULATOR_10499, Items.AVAS_ATTRACTOR_10498) + + private val questCapes = intArrayOf(9813,9814) + + override fun defineListeners() { + onEquip(fremennikIslesEquipment) { player, _ -> + if (!isQuestComplete(player, "Fremennik Isles")) { + sendMessage(player, "You must have completed The Fremennik Isles to equip this.") + return@onEquip false + } + return@onEquip true + } + + onEquip(fremennikTrialsEquipment) { player, _ -> + if (!isQuestComplete(player, "Fremennik Trials")) { + sendMessage(player, "You must have completed The Fremennik Trials to equip this.") + return@onEquip false + } + return@onEquip true + } + + onEquip(fremennikIslesDuringQuestEquipment){ player, _ -> + if (questStage(player, "Fremennik Isles") > 0) { + sendMessage(player, "You must have started The Fremennik Isles to equip this.") + return@onEquip false + } + return@onEquip true + } + + onEquip(avasBackpacks){ player, _ -> + if (!isQuestComplete(player, "Animal Magnetism")) { + sendMessage(player, "You must have completed Animal Magnetism to equip this.") + return@onEquip false + } + return@onEquip true + } + + onEquip(Items.CAPE_OF_LEGENDS_1052) { player, _ -> + if(!isQuestComplete(player, "Legends' Quest")) { + player.packetDispatch.sendMessage("You must have completed Legends' Quest to equip this.") + return@onEquip false + } + return@onEquip true + } + + onEquip(questCapes) { player, _ -> + val maxQP = QuestRepository.getQuests().values.sumBy { it.questPoints } + if (player.questRepository.points < maxQP) { + player.packetDispatch.sendMessage("You cannot equip the Quest cape without completing all available quests.") + return@onEquip false + } + return@onEquip true + } + + onEquip(Items.WOLFBANE_2952){ player, _ -> + if(!isQuestComplete(player, "Priest in Peril")) { + sendMessage(player, "You must have completed Priest in Peril to equip this.") + return@onEquip false + } + return@onEquip true + } + + onEquip(Items.ANCIENT_MACE_11061){ player, _ -> + if(!isQuestComplete(player, "Another Slice of H.A.M.")) { + sendMessage(player, "You must have completed Another Slice of H.A.M. to equip this.") + return@onEquip false + } + return@onEquip true + } + + onEquip(Items.ANCIENT_STAFF_4675){ player, _ -> + if(!isQuestComplete(player, "Desert Treasure")) { + sendMessage(player, "You must have completed Desert Treasure to equip this.") + return@onEquip false + } + return@onEquip true + } + + + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/LegendsCapeLock.kt b/Server/src/main/kotlin/rs09/game/interaction/item/LegendsCapeLock.kt deleted file mode 100644 index d4f0d5cf7..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/LegendsCapeLock.kt +++ /dev/null @@ -1,15 +0,0 @@ -package rs09.game.interaction.item - -import api.* -import org.rs09.consts.Items -import rs09.game.interaction.InteractionListener - -class LegendsCapeLock : InteractionListener() { - override fun defineListeners() { - onEquip(Items.CAPE_OF_LEGENDS_1052){player, node -> - val points = getQP(player) - if(points < 40) sendDialogue(player, "You need 40 QP to equip that.") - return@onEquip points >= 40 - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/item/QuestCapeListener.kt b/Server/src/main/kotlin/rs09/game/interaction/item/QuestCapeListener.kt deleted file mode 100644 index ed198eea1..000000000 --- a/Server/src/main/kotlin/rs09/game/interaction/item/QuestCapeListener.kt +++ /dev/null @@ -1,21 +0,0 @@ -package rs09.game.interaction.item - -import core.game.node.entity.player.link.quest.QuestRepository -import rs09.game.interaction.InteractionListener -import rs09.game.interaction.InteractionListeners.run - -class QuestCapeListener : InteractionListener() { - - val IDs = intArrayOf(9813,9814) - - override fun defineListeners() { - on(IDs,ITEM,"wear"){ player, item -> - val MAX_QP = QuestRepository.getQuests().values.sumBy { it.questPoints } - if (player.questRepository.points < MAX_QP) { - player.packetDispatch.sendMessage("You cannot wear this " + item.name.toLowerCase() + " yet.") - return@on true - } - return@on run(item.id, 0, "equip", player, item) - } - } -} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/npc/NPCDepositListener.kt b/Server/src/main/kotlin/rs09/game/interaction/npc/NPCDepositListener.kt new file mode 100644 index 000000000..8ba51e5e9 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/interaction/npc/NPCDepositListener.kt @@ -0,0 +1,29 @@ +package rs09.game.interaction.npc + +import api.isEquipped +import api.sendNPCDialogue +import api.setInterfaceText +import core.game.content.dialogue.FacialExpression +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import rs09.game.interaction.InteractionListener +import rs09.tools.END_DIALOGUE + +class NPCDepositListener : InteractionListener() { + + override fun defineListeners() { + on(NPCs.PEER_THE_SEER_1288, NPC,"deposit") { player, _ -> + if (isEquipped(player, Items.FREMENNIK_SEA_BOOTS_1_14571) || + isEquipped(player, Items.FREMENNIK_SEA_BOOTS_2_14572) || + isEquipped(player, Items.FREMENNIK_SEA_BOOTS_3_14573)) { + player.bank.openDepositBox() + setInterfaceText(player, "Peer the Seer's Deposits", 11, 12) + } else { + sendNPCDialogue(player, NPCs.PEER_THE_SEER_1288, + "Do not pester me, outerlander! I will only deposit items into the banks of those who have earned Fremennik sea boots!", + FacialExpression.ANNOYED).also { END_DIALOGUE } + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/RellekkaListeners.kt b/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/RellekkaListeners.kt index b330b22c6..069389363 100644 --- a/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/RellekkaListeners.kt +++ b/Server/src/main/kotlin/rs09/game/interaction/region/rellekka/RellekkaListeners.kt @@ -1,6 +1,7 @@ package rs09.game.interaction.region.rellekka import core.game.world.map.Location +import core.plugin.Initializable import org.rs09.consts.NPCs import rs09.game.interaction.InteractionListener import rs09.game.util.region.rellekka.RellekkaDestination diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MeleeSwingHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MeleeSwingHandler.kt index f70117506..3ccc67f8c 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MeleeSwingHandler.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/MeleeSwingHandler.kt @@ -15,7 +15,7 @@ import core.game.node.entity.combat.equipment.WeaponInterface import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills -import core.game.node.entity.skill.slayer.SlayerEquipmentFlags +import rs09.game.node.entity.skill.slayer.SlayerEquipmentFlags import core.game.node.entity.state.EntityState import core.game.world.map.path.Pathfinder import core.tools.RandomFunction diff --git a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/RangeSwingHandler.kt b/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/RangeSwingHandler.kt index 7e6d53fbe..e52f2c707 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/RangeSwingHandler.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/combat/handlers/RangeSwingHandler.kt @@ -60,7 +60,7 @@ open class RangeSwingHandler } if (entity is Player) { val rw = RangeWeapon.get(entity.getEquipment().getNew(EquipmentContainer.SLOT_WEAPON).getId()); - if(rw.weaponType == WeaponType.DOUBLE_SHOT || rw.weaponType == WeaponType.DEGRADING) { + if(rw != null && (rw.weaponType == WeaponType.DOUBLE_SHOT || rw.weaponType == WeaponType.DEGRADING)) { // Dark bow and crystal bow have a 10-square range, independent of whether longrange stance is used distance = 10 } diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/LoginParser.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/LoginParser.kt index 1bd578c62..881d369bd 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/LoginParser.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/LoginParser.kt @@ -106,7 +106,7 @@ class LoginParser( return } if(!PlayerParser.parse(player)){ - Repository.players.remove(player) + Repository.removePlayer(player) Repository.LOGGED_IN_PLAYERS.remove(player.username) Repository.lobbyPlayers.remove(player) Repository.playerNames.remove(player.name) @@ -124,10 +124,10 @@ class LoginParser( p.clear() Repository.playerNames.remove(p.name) Repository.lobbyPlayers.remove(p) - Repository.players.remove(p) + Repository.removePlayer(p) } if (!Repository.players.contains(player)) { - Repository.players.add(player) + Repository.addPlayer(player) } player.details.session.setObject(player) flag(Response.SUCCESSFUL) @@ -179,7 +179,7 @@ class LoginParser( GameWorld.Pulser.submit(object : Pulse(1) { override fun pulse(): Boolean { if (!Repository.players.contains(player)) { - Repository.players.add(player) + Repository.addPlayer(player) } return true } diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaveParser.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaveParser.kt index f3c1c1a35..c972d77e0 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaveParser.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaveParser.kt @@ -8,7 +8,6 @@ import core.game.node.entity.player.link.SpellBookManager import core.game.node.entity.player.link.emote.Emotes import core.game.node.entity.player.link.grave.GraveType import core.game.node.entity.player.link.music.MusicEntry -import core.game.node.entity.skill.slayer.SlayerEquipmentFlags import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.json.simple.JSONArray @@ -18,11 +17,8 @@ import rs09.ServerConstants import rs09.game.node.entity.skill.farming.CompostBins import rs09.game.node.entity.skill.farming.FarmingPatch import rs09.game.system.SystemLogger -import rs09.game.world.GameWorld import java.io.FileReader import java.util.* -import core.game.node.item.Item; -import org.rs09.consts.Items; /** * Class used for parsing JSON player saves. @@ -307,7 +303,7 @@ class PlayerSaveParser(val player: Player) { fun parseGrandExchange() { val geData: Any? = saveFile!!["grand_exchange"] if (geData != null) { - player.playerGrandExchange.parse(geData as JSONObject) + player.exchangeRecords.parse(geData as JSONObject) } } @@ -382,7 +378,6 @@ class PlayerSaveParser(val player: Player) { bBars?.let{player.blastBars.parse(it)} bOre?.let{player.blastOre.parse(bOre)} bCoal?.let{player.blastCoal.parse(bCoal)} - migrateBlastCoal() player.location = rs09.JSONUtils.parseLocation(location) } @@ -413,10 +408,5 @@ class PlayerSaveParser(val player: Player) { player.settings.parse(settingsData) } - fun migrateBlastCoal() { - val amount = player.blastOre.getAmount(Items.COAL_453) - if(amount > 0 && player.blastOre.remove(Item(Items.COAL_453, amount))) { - player.blastCoal.add(Item(Items.COAL_453, amount)) - } - } -} + +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt index 69a88c522..c297f0573 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/player/info/login/PlayerSaver.kt @@ -223,7 +223,7 @@ class PlayerSaver (val player: Player){ } val rooms = JSONArray() var z = 0 - for(room in player.houseManager.rooms){ + for(room in player.houseManager.rooms){ var x = 0 for(xr in room){ var y = 0 @@ -634,20 +634,20 @@ class PlayerSaver (val player: Player){ fun saveGrandExchangeData(root: JSONObject){ val grandExchange = JSONObject() - if(player.playerGrandExchange.hasActiveOffer()){ + if(player.exchangeRecords.hasActiveOffer()){ val offers = JSONArray() - player.playerGrandExchange.offers.map { + player.exchangeRecords.offerRecords.map { if(it != null){ val offer = JSONObject() - offer["offerIndex"] = it.index.toString() - offer["offerUID"] = it.uid.toString() + offer["index"] = it.slot.toString() + offer["uid"] = it.uid.toString() offers.add(offer) } } grandExchange["offers"] = offers } val history = JSONArray() - player.playerGrandExchange.history.map { + player.exchangeRecords.history.map { if(it != null){ val historyEntry = JSONObject() historyEntry["isSell"] = it.sell @@ -707,8 +707,8 @@ class PlayerSaver (val player: Player){ } slayer.put("removedTasks",removedTasks) } - slayer.put("taskStreak",slayerManager.taskCount.toString()) - slayer.put("totalTasks",slayerManager.totalTasks.toString()) + slayer.put("taskStreak",slayerManager.flags.taskStreak.toString()) + slayer.put("totalTasks",slayerManager.flags.completedTasks.toString()) slayer.put("equipmentFlags",slayerManager.flags.equipmentFlags) slayer.put("taskFlags", slayerManager.flags.taskFlags) slayer.put("rewardFlags", slayerManager.flags.rewardFlags) 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 new file mode 100644 index 000000000..8f34b7c06 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/node/entity/player/link/diary/DiaryEventHook.kt @@ -0,0 +1,229 @@ +package rs09.game.node.entity.player.link.diary + +import api.* +import api.events.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +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.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import rs09.game.Event + +class DiaryEventHook : LoginListener +{ + override fun login(player: Player) { + player.hook(Event.ResourceGathered, DiaryGatherHooks) + player.hook(Event.Teleport, DiaryTeleportHooks) + player.hook(Event.NPCKilled, DiaryCombatHooks) + player.hook(Event.FireLit, DiaryFireHooks) + player.hook(Event.Interaction, DiaryInteractionEvents) + } + + companion object { + private fun finishTask(entity: Player, diary: DiaryType, index: Int, task: Int) { + entity.achievementDiaryManager.finishTask(entity, diary, index, task) + } + } + + private object DiaryInteractionEvents : EventHook + { + override fun process(entity: Entity, event: InteractionEvent) { + 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) + 16149 -> finishTask(entity, DiaryType.VARROCK, 0, 4) + //2112 -> if() + } + } + } + + private object DiaryFireHooks : EventHook + { + val lumCastleBorders = ZoneBorders(3216, 3207, 3225, 3233, 0) + override fun process(entity: Entity, event: LitFireEvent) { + 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) + + else -> { + if(event.logId == Items.WILLOW_LOGS_1519 && lumCastleBorders.insideBorder(entity)) + finishTask(entity, DiaryType.LUMBRIDGE,2 ,3) + } + } + } + } + + private object DiaryGatherHooks : EventHook + { + override fun process(entity: Entity, event: ResourceGatheredEvent) { + if(entity !is Player) return + val regionId = entity.viewport.region.id + 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) + 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 (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.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_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_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_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) + } + + 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.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.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.COAL_453 -> if(regionId == 12593) finishTask(entity, DiaryType.LUMBRIDGE, 2, 11) + } + + if(getAttribute(entity, "diary:seers:shark-caught", 0) >= 5) + finishTask(entity, DiaryType.SEERS_VILLAGE, 2, 7) + + if((regionId == 12593 || regionId == 12849) && event.source.name.startsWith("dead", true)) + finishTask(entity, DiaryType.LUMBRIDGE, 1, 8) + + if(event.source.id == 333 && entity.zoneMonitor.isInZone("karamja")) + finishTask(entity, DiaryType.KARAMJA, 0, 6) + } + } + + private object DiaryTeleportHooks : EventHook + { + override fun process(entity: Entity, event: TeleportEvent) { + TODO("Not yet implemented") + } + } + + 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 lumZombies = intArrayOf(NPCs.ZOMBIE_73, NPCs.ZOMBIE_74) + + override fun process(entity: Entity, event: NPCKillEvent) { + if(entity !is Player) return + when(event.npc.id) + { + //FALADOR LEVEL 0 TASK 10 + in faladorDucks -> { + if(entity.location.withinDistance(Location(2991, 3383, 0))) + finishTask(entity, DiaryType.FALADOR, 0, 9) + } + + in lumbridgeCows -> { + // Obtain a cow-hide from a cow in the field north-east of Lumbridge + if (entity.viewport.region.id == 12850 || entity.viewport.region.id == 12851) { + finishTask(entity, DiaryType.LUMBRIDGE, 1, 1) + } + } + + NPCs.JOGRE_113 -> { + if(entity.viewport.region.id == 11412) + finishTask(entity, DiaryType.KARAMJA, 0, 9) + } + + in elementalNPCs -> { + if(!entity.location.withinDistance(Location(2719,9889,0), 100)) + return + 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)) + return + finishTask(entity, DiaryType.SEERS_VILLAGE, 1, 4) + } + + 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) + 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) + finishTask(entity, DiaryType.LUMBRIDGE, 1, 18) + } + + NPCs.GIANT_RAT_86 -> { + 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)) + + } + + if(getAttribute(entity,"diary:seers:tower-archers", 0) == 0xF) + finishTask(entity, DiaryType.SEERS_VILLAGE, 1, 6) + } + } +} diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/questhall/MountedGlory.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/questhall/MountedGlory.kt index 4e19fb457..e8de88232 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/questhall/MountedGlory.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/construction/decoration/questhall/MountedGlory.kt @@ -2,6 +2,7 @@ package rs09.game.node.entity.skill.construction.decoration.questhall import api.teleport import core.game.node.entity.player.Player +import core.game.node.entity.player.link.audio.Audio; import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.update.flag.context.Animation @@ -51,7 +52,7 @@ class MountedGlory : InteractionListener() { 1 -> { player.lock(5) player.visualize(Animation(714), Graphics(308, 100, 50)) - player.audioManager.send(200) + player.getAudioManager().send(Audio(200), true); } 4 -> player.animator.reset().also { teleport(player, TELEPORTS[int]) } } diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/FishingPulse.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/FishingPulse.kt index 30b3bd953..b17a482ef 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/FishingPulse.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/fishing/FishingPulse.kt @@ -1,5 +1,6 @@ package rs09.game.node.entity.skill.gather.fishing +import api.events.ResourceGatheredEvent import core.game.content.global.SkillingPets import core.game.content.quest.tutorials.tutorialisland.TutorialSession import core.game.content.quest.tutorials.tutorialisland.TutorialStage @@ -119,7 +120,7 @@ class FishingPulse(player: Player?, npc: NPC, private val option: FishingOption? if (player.skillTasks.hasTask()) { updateSkillTask() } - updateDiary() + player.dispatch(ResourceGatheredEvent(fish!!.item.id, fish!!.item.amount, node!!)) SkillingPets.checkPetDrop(player, SkillingPets.HERON) val item = fish!!.item if (isActive(SkillcapePerks.GREAT_AIM, player) && RandomFunction.random(100) <= 5) { @@ -141,145 +142,6 @@ class FishingPulse(player: Player?, npc: NPC, private val option: FishingOption? return player.inventory.freeSlots() == 0 } - fun updateDiary() { - when (fish) { - Fish.MACKEREL -> { - if (player.viewport.region.id == 11317) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 0, 11) - } - if (player.viewport.region.id == 11317 - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 1, 11) - ) { - player.setAttribute("/save:diary:seers:bass-caught", true) - } - if (player.viewport.region.id == 11317 - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 7) - ) { - player.setAttribute( - "/save:diary:seers:caught-shark", - 1 + player.getAttribute("diary:seers:caught-shark", 0) - ) - if (player.getAttribute("diary:seers:caught-shark", 0) >= 5) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 7) - } - } - // Catch some shrimp in the Fishing spot to the east of

Lumbridge Swamp - if (player.location.withinDistance(Location.create(3241, 3149, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 13) - } - // Catch a pike in the river to the east of Lumbridge Castle - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 4) - } - // Catch a salmon in the river to the east of Lumbridge Castle - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - // Catch a trout in the river to the east of Barbarian Village - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.BASS -> { - if (player.viewport.region.id == 11317 - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 1, 11) - ) { - player.setAttribute("/save:diary:seers:bass-caught", true) - } - if (player.viewport.region.id == 11317 - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 7) - ) { - player.setAttribute( - "/save:diary:seers:caught-shark", - 1 + player.getAttribute("diary:seers:caught-shark", 0) - ) - if (player.getAttribute("diary:seers:caught-shark", 0) >= 5) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 7) - } - } - if (player.location.withinDistance(Location.create(3241, 3149, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 13) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 4) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.SHARK -> { - if (player.viewport.region.id == 11317 - && !player.achievementDiaryManager.hasCompletedTask(DiaryType.SEERS_VILLAGE, 2, 7) - ) { - player.setAttribute( - "/save:diary:seers:caught-shark", - 1 + player.getAttribute("diary:seers:caught-shark", 0) - ) - if (player.getAttribute("diary:seers:caught-shark", 0) >= 5) { - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 2, 7) - } - } - if (player.location.withinDistance(Location.create(3241, 3149, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 13) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 4) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.SHRIMP -> { - if (player.location.withinDistance(Location.create(3241, 3149, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 13) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 4) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.PIKE -> { - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 4) - } - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.SALMON -> { - if (player.location.withinDistance(Location.create(3240, 3247, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 9) - } - if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - Fish.TROUT -> if (player.location.withinDistance(Location.create(3105, 3431, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 16) - } - } - // Use the Fishing spots north of the banana plantation - if (node!!.id == 333 && player.zoneMonitor.isInZone("karamja") - && player.location.withinDistance(Location(2924, 3178, 0), 10) - ) { - player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 0, 6) - } - } - fun updateSkillTask() { when (fish) { Fish.ANCHOVIE -> { diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/mining/MiningSkillPulse.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/mining/MiningSkillPulse.kt index 313b1fa27..a26c969a8 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/mining/MiningSkillPulse.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/gather/mining/MiningSkillPulse.kt @@ -1,6 +1,7 @@ package rs09.game.node.entity.skill.gather.mining import api.* +import api.events.ResourceGatheredEvent import core.cache.def.impl.ItemDefinition import core.game.container.impl.EquipmentContainer import core.game.content.dialogue.FacialExpression @@ -116,19 +117,6 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul return false } - //Handle tutorial stuff - val tutorialStage = TutorialSession.getExtension(player).stage - if (tutorialStage == 36 && node.id == 3042) { - TutorialStage.load(player, 38, false) - } else if (tutorialStage == 36 && node.id == 3043) { - TutorialStage.load(player, 37, false) - } - if (tutorialStage == 38 && node.id == 3043) { - TutorialStage.load(player, 39, false) - } else if (tutorialStage == 37 && node.id == 3042) { - TutorialStage.load(player, 39, false) - } - //actual reward calculations var reward = resource!!.getReward() var rewardAmount = 0 @@ -136,7 +124,7 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul reward = calculateReward(reward) // calculate rewards rewardAmount = calculateRewardAmount(reward) // calculate amount - applyAchievementTask(reward) // apply achievements + player.dispatch(ResourceGatheredEvent(reward, rewardAmount, node)) SkillingPets.checkPetDrop(player, SkillingPets.GOLEM) // roll for pet //add experience @@ -248,56 +236,6 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul return reward } - /** - * Checks if the has completed any achievements from their diary - */ - private fun applyAchievementTask(reward: Int) { - // Mine some Iron in the south east mining patch near Varrock - if (reward == Items.IRON_ORE_440 && player.location.withinDistance(Location.create(3285, 3363, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 2) - } - - // Mine some limestone near Paterdomus, the temple to the east

of Varrock - if (reward == Items.LIMESTONE_3211 && player.location.withinDistance(Location.create(3372, 3500, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 15) - } - - // Mine some gold from the rocks on the north-west

peninsula of Karamja - if (reward == Items.GOLD_ORE_444 && player.location.withinDistance(Location.create(2733, 3225, 0))) { - player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 0, 2) - } - - // Mine a red topaz from a gem rock - if (reward == Items.UNCUT_RED_TOPAZ_1629 && (player.viewport.region.id == 11310 || player.viewport.region.id == 11410)) { - player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 1, 18) - } - - // Mine some clay in the Mining patch north of the Champions'

Guild - if (reward == Items.CLAY_434 && player.viewport.region.id == 12596) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 5) - } - - // Mine some copper in the Mining spot to the south-east of

Lumbridge Swamp - if (reward == Items.COPPER_ORE_436 && player.viewport.region.id == 12849) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 12) - } - - // Mine some iron ore from the Al Kharid Mining spot - if (reward == Items.IRON_ORE_440 && player.viewport.region.id == 13107) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 1, 0) - } - - // Mine some silver from the mining spot north of Al Kharid - if (reward == Items.SILVER_ORE_442 && player.viewport.region.id == 13107) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 10) - } - - // Mine some coal in the Mining spot south-west of Lumbridge

Swamp - if (reward == Items.COAL_453 && player.viewport.region.id == 12593) { - player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 2, 11) - } - } - /** * Checks if the player gets rewarded. * @return `True` if so. diff --git a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerEquipmentFlags.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerEquipmentFlags.kt similarity index 94% rename from Server/src/main/java/core/game/node/entity/skill/slayer/SlayerEquipmentFlags.kt rename to Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerEquipmentFlags.kt index 55bfbc788..8d7c25ae9 100644 --- a/Server/src/main/java/core/game/node/entity/skill/slayer/SlayerEquipmentFlags.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerEquipmentFlags.kt @@ -1,7 +1,8 @@ -package core.game.node.entity.skill.slayer +package rs09.game.node.entity.skill.slayer -import api.* import api.EquipmentSlot +import api.getAttribute +import api.getItemFromEquipment import core.game.node.entity.player.Player import org.rs09.consts.Items import rs09.game.node.entity.skill.skillcapeperks.SkillcapePerks @@ -18,7 +19,7 @@ object SlayerEquipmentFlags { @JvmStatic fun updateFlags(player: Player){ var flags = 0 - if(SkillcapePerks.isActive(SkillcapePerks.TRICKS_OF_THE_TRADE,player) && getAttribute(player, "cape_perks:tott:helmet-stored", false)) flags = 0x3F + if(SkillcapePerks.isActive(SkillcapePerks.TRICKS_OF_THE_TRADE, player) && getAttribute(player, "cape_perks:tott:helmet-stored", false)) flags = 0x3F else if(hasItem(player, Items.SLAYER_HELMET_13263)) flags = 0x1F else if(hasItem(player, Items.NOSE_PEG_4168)) flags = 1 else if(hasItem(player, Items.EARMUFFS_4166)) flags = flags or (1 shl 1) diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerFlags.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerFlags.kt index 5619c87c7..23b22005b 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerFlags.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerFlags.kt @@ -18,7 +18,7 @@ class SlayerFlags(val player: Player) { /** * The removed tasks. */ - val removed: List = ArrayList(4) + val removed: ArrayList = ArrayList(4) /** * Get/set master flags diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerKillHook.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerKillHook.kt new file mode 100644 index 000000000..adb77b979 --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerKillHook.kt @@ -0,0 +1,46 @@ +package rs09.game.node.entity.skill.slayer + +import api.events.EventHook +import api.events.NPCKillEvent +import api.rewardXP +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.entity.skill.slayer.Master + +object SlayerKillHook : EventHook +{ + override fun process(entity: Entity, event: NPCKillEvent) { + val npc = event.npc + val player = entity as? Player ?: return + val slayer = player.slayer + val flags = slayer.flags + + if (slayer.hasTask()) { + rewardXP(player, Skills.SLAYER, npc.skills.maximumLifepoints.toDouble()) + slayer.decrementAmount(1) + flags.taskStreak = flags.taskStreak + 1 + flags.completedTasks = flags.completedTasks + 1 + if ((flags.completedTasks > 4 || flags.canEarnPoints()) && flags.getMaster() != Master.TURAEL && flags.getPoints() < 64000) { + var points = flags.getMaster().taskPoints[0] + if (flags.taskStreak % 50 == 0) { + points = flags.getMaster().taskPoints[2] + } else if (flags.taskStreak % 10 == 0) { + points = flags.getMaster().taskPoints[1] + } + flags.incrementPoints(points) + if (flags.getPoints() > 64000) { + flags.setPoints(64000) + } + player.sendMessages("You've completed " + flags.taskStreak + " tasks in a row and received " + points + " points, with a total of " + flags.getPoints(), "You have completed " + flags.completedTasks + " tasks in total. Return to a Slayer master.") + } else if (flags.completedTasks == 4) { + player.sendMessage("You've completed your task; you will start gaining points on your next task!") + flags.flagCanEarnPoints() + } else if (flags.getMaster() == Master.TURAEL) { + player.sendMessages("You've completed your task; Tasks from Turael do not award points.", "Return to a Slayer master.") + } else { + player.sendMessages("You've completed your task; Complete " + (4 - flags.completedTasks) + " more task(s) to start gaining points.", "Return to a Slayer master.") + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerManager.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerManager.kt new file mode 100644 index 000000000..21cdca8ed --- /dev/null +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerManager.kt @@ -0,0 +1,172 @@ +package rs09.game.node.entity.skill.slayer + +import core.cache.def.impl.NPCDefinition +import core.game.node.entity.player.Player +import core.game.node.entity.skill.slayer.Master +import core.game.node.entity.skill.slayer.Tasks +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import rs09.game.Event +import java.util.* + +/** + * Manages the players slayer task. + * @author Ceikry + */ +class SlayerManager(val player: Player) { + + init { + player.hook(Event.NPCKilled, SlayerKillHook) + } + + /** + * The player's slayer flags + */ + @JvmField + val flags: SlayerFlags = SlayerFlags(player) + + fun parse(slayerData: JSONObject) { + val m = slayerData["master"] + if (m != null) { + flags.setMaster(Master.forId(m.toString().toInt())) + } + val t = slayerData["taskId"] + if (t != null) flags.setTask(Tasks.values()[t.toString().toInt()]) + val a = slayerData["taskAmount"] + if (a != null) flags.setTaskAmount(a.toString().toInt()) + val points = slayerData["points"] + if (points != null) { + flags.setPoints(points.toString().toInt()) + } + val taskStreak = slayerData["taskStreak"] + if (taskStreak != null) { + flags.taskStreak = taskStreak.toString().toInt() + } + val la = slayerData["learned_rewards"] + if (la != null) { + val learnedArray = slayerData["learned_rewards"] as JSONArray? + for (i in learnedArray!!.indices) { + val unlocked = learnedArray[i] as Boolean + when (i) { + 0 -> if (unlocked) flags.unlockBroads() + 1 -> if (unlocked) flags.unlockRing() + 2 -> if (unlocked) flags.unlockHelm() + else -> {} + } + } + } + val removedTasks = slayerData["removedTasks"] as JSONArray? + if (removedTasks != null) { + for (i in removedTasks.indices) { + flags.removed.add(Tasks.values()[removedTasks[i].toString().toInt()]) + } + } + val completedTasks: Any = slayerData["totalTasks"].toString() + flags.completedTasks = completedTasks.toString().toInt() + if (flags.completedTasks >= 4) flags.flagCanEarnPoints() + + //New system parsing + if (slayerData.containsKey("equipmentFlags")) flags.equipmentFlags = slayerData["equipmentFlags"].toString().toInt() + if (slayerData.containsKey("taskFlags")) flags.taskFlags = slayerData["taskFlags"].toString().toInt() + if (slayerData.containsKey("rewardFlags")) flags.rewardFlags = slayerData["rewardFlags"].toString().toInt() + } + + /** + * Method used to assign a new task for a player. + * @param master the master to give the task. + */ + fun generate(master: Master) { + val task = SlayerUtils.generate(player, master) ?: return + SlayerUtils.assign(player, task, master) + } + + /** + * Clears the task. + */ + fun clear() { + amount = 0 + } + + /** + * Gets the task name. + * @return the name. + */ + val taskName: String + get() { + val task = flags.getTask() + if (task.npcs == null) { + return "no npcs report me" + } + return if (task.npcs.isEmpty()) { + "npc length too small report me" + } else NPCDefinition.forId(task.npcs[0]).name.toLowerCase() + } + + var task: Tasks? + get() = flags.getTask() + set(task) { + flags.setTask(task!!) + } + + var master: Master? + get() = flags.getMaster() + set(master) { + flags.setMaster(master!!) + } + + /** + * Checks if a **Player** contains a task. + * @return `True` if so. + */ + fun hasTask(): Boolean { + return amount > 0 + } + + /** + * Method used to check if the task is completed. + * @return `True` if so. + */ + val isCompleted: Boolean + get() = flags.getTaskAmount() <= 0 + + var amount: Int + get() = flags.getTaskAmount() + set(amount) { + flags.setTaskAmount(amount) + } + + fun decrementAmount(amount: Int) { + flags.decrementTaskAmount(amount) + player.varpManager.get(2502).setVarbit(0, flags.taskFlags shr 4).send(player) + } + + /** + * Method used to check if the player has started slayer. + * @return `True` if so. + */ + fun hasStarted(): Boolean { + return flags.completedTasks > 0 || flags.getTaskAmount() > 0 + } + /** + * Gets the slayerPoints. + * @return the slayerPoints. + */ + /** + * Sets the slayerPoints. + * @param slayerPoints the slayerPoints to set + */ + var slayerPoints: Int + get() = flags.getPoints() + set(slayerPoints) { + flags.setPoints(slayerPoints) + } + + /** + * Gets the removed. + * @return the removed. + */ + val removed: List + get() = flags.removed + val isCanEarnPoints: Boolean + get() = flags.canEarnPoints() +} \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerUtils.kt b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerUtils.kt index cf4cd4170..f2a57eebd 100644 --- a/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerUtils.kt +++ b/Server/src/main/kotlin/rs09/game/node/entity/skill/slayer/SlayerUtils.kt @@ -3,11 +3,52 @@ package rs09.game.node.entity.skill.slayer import core.game.node.entity.combat.BattleState import core.game.node.entity.player.Player import core.game.node.entity.player.link.SpellBookManager.SpellBook +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import core.game.node.entity.skill.slayer.Master +import core.game.node.entity.skill.slayer.Tasks +import core.tools.RandomFunction import org.rs09.consts.Items +import java.util.ArrayList -class SlayerUtils(val player: Player) { +object SlayerUtils { + fun generate(player: Player, master: Master): Tasks? + { + val tasks: MutableList = ArrayList(10) + val taskWeightSum = intArrayOf(0) + master.tasks.stream().filter { task: Master.Task -> canBeAssigned(player, task.task) && task.task.combatCheck <= player.properties.currentCombatLevel }.forEach { task: Master.Task -> + taskWeightSum[0] += task.weight + tasks.add(task) + } + tasks.shuffle(RandomFunction.RANDOM) + var rnd = RandomFunction.random(taskWeightSum[0]) + for (task in tasks) { + if (rnd < task!!.weight) return task.task + rnd -= task.weight + } + return null + } - fun hasBroadWeaponEquipped(state: BattleState): Boolean { + fun canBeAssigned(player: Player, task: Tasks): Boolean + { + return player.getSkills().getLevel(Skills.SLAYER) >= task.levelReq && !player.slayer.flags.removed.contains(task) + } + + fun assign(player: Player, task: Tasks, master: Master) + { + player.slayer.master = master + player.slayer.task = task + player.slayer.amount = RandomFunction.random(master.assignment_range[0], master.assignment_range[1]) + if (master == Master.DURADEL) { + player.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 2, 8) + } else if (master == Master.VANNAKA) { + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 1, 14) + } + player.varpManager.get(2502).setVarbit(0, player.slayer.flags.taskFlags shr 4).send(player) + } + + @JvmStatic + fun hasBroadWeaponEquipped(player: Player, state: BattleState): Boolean { return (state.weapon != null && state.weapon.id == Items.LEAF_BLADED_SPEAR_4158 || state.weapon != null && state.weapon.id == Items.LEAF_BLADED_SWORD_13290 || state.ammunition != null && (state.ammunition.itemId == Items.BROAD_ARROW_4160 || state.ammunition.itemId == Items.BROAD_TIPPED_BOLTS_13280) || diff --git a/Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt b/Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt index 343c2da4e..ad483fb35 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt +++ b/Server/src/main/kotlin/rs09/game/system/command/oldsys/VisualCommand.kt @@ -4,14 +4,14 @@ import api.* import core.cache.Cache import core.game.container.access.InterfaceContainer import core.game.content.quest.tutorials.tutorialisland.CharacterDesign +import core.game.node.scenery.Scenery +import core.game.node.scenery.SceneryBuilder import core.game.node.entity.combat.ImpactHandler.HitsplatType import core.game.node.entity.impl.Projectile import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.audio.Audio import core.game.node.item.Item -import core.game.node.scenery.Scenery -import core.game.node.scenery.SceneryBuilder import core.game.system.command.CommandSet import core.game.system.task.Pulse import core.game.world.map.Location @@ -25,6 +25,8 @@ import core.plugin.Initializable import core.plugin.Plugin import rs09.game.system.command.CommandPlugin import rs09.game.world.GameWorld +import java.awt.Toolkit +import java.awt.datatransfer.StringSelection /** * Represents the the command plugin used for visual commands. @@ -131,6 +133,8 @@ class VisualCommand : CommandPlugin() { npc.init() npc.isWalks = if (args.size > 2) true else false val npcString = "{" + npc.location.x + "," + npc.location.y + "," + npc.location.z + "," + (if (npc.isWalks) "1" else "0") + "," + npc.direction.ordinal + "}" + val clpbrd = Toolkit.getDefaultToolkit().systemClipboard + clpbrd.setContents(StringSelection(npcString), null) println(npcString) return true } 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 359e17a72..06979958f 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 @@ -1,10 +1,14 @@ package rs09.game.system.command.sets +import core.cache.def.impl.NPCDefinition import core.game.node.entity.combat.ImpactHandler.HitsplatType +import core.game.node.entity.npc.drop.NPCDropTables +import core.game.node.entity.player.Player import core.game.node.entity.player.link.SpellBookManager import core.game.node.item.Item import core.plugin.Initializable import org.rs09.consts.Items +import rs09.game.content.global.NPCDropTable import rs09.game.system.command.Command @Initializable @@ -35,5 +39,22 @@ class DevelopmentCommandSet : CommandSet(Command.Privilege.ADMIN) { player.impactHandler.manualHit(player, player.skills.lifepoints, HitsplatType.NORMAL) } + define("rolldrops") { player: Player, args: Array -> + if(args.size < 2){ + reject(player,"Usage: ::rolldrops npcid amount") + } + + val container = player.dropLog + val npcId = args[1].toInt() + val amount = args[2].toInt() + + for(i in 0..amount) + { + val drops = NPCDefinition.forId(npcId).dropTables.table.roll() + for(drop in drops) container.add(drop) + } + + container.open(player) + } } } \ No newline at end of file diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt index e126e164a..7a08708ff 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt +++ b/Server/src/main/kotlin/rs09/game/system/command/sets/MiscCommandSet.kt @@ -1,12 +1,11 @@ package rs09.game.system.command.sets import api.* -import core.cache.def.impl.ItemDefinition +import api.InputType import core.cache.def.impl.NPCDefinition import core.cache.def.impl.SceneryDefinition import core.cache.def.impl.VarbitDefinition import core.game.component.Component -import core.game.ge.OfferState import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.info.Rights @@ -24,13 +23,16 @@ import org.rs09.consts.Components import rs09.ServerConstants import rs09.game.content.activity.fishingtrawler.TrawlerLoot import rs09.game.content.ame.RandomEvents -import rs09.game.ge.OfferManager +import rs09.game.ge.GrandExchange import rs09.game.node.entity.state.newsys.states.FarmingState import rs09.game.system.SystemLogger import rs09.game.system.command.Command import rs09.game.system.command.CommandMapping import rs09.game.world.repository.Repository import rs09.tools.stringtools.colorize +import java.awt.Toolkit +import java.awt.datatransfer.StringSelection +import java.util.* @Initializable class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ @@ -72,6 +74,9 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ SystemLogger.logInfo("Viewport: " + l.getSceneX(player.playerFlags.lastSceneGraph) + "," + l.getSceneY(player.playerFlags.lastSceneGraph)) val loc = "Location.create(" + l.x + ", " + l.y + ", " + l.z + ")" SystemLogger.logInfo(loc + "; " + player.playerFlags.lastSceneGraph + ", " + l.localX + ", " + l.localY) + val stringSelection = StringSelection(loc) + val clpbrd = Toolkit.getDefaultToolkit().systemClipboard + clpbrd.setContents(stringSelection, null) } /** @@ -150,7 +155,7 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ */ define("ge", Command.Privilege.STANDARD) { player, args -> if(args.size < 2){ - reject(player, "Usage: ::ge mode", "Available modes: buying, selling, search") + reject(player, "Usage: ::ge mode", "Available modes: buying, selling, search, bots, botsearch") } val mode = args[1] @@ -160,6 +165,10 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ "search" -> sendInputDialogue(player, InputType.STRING_LONG, "Enter search term:"){value -> showOffers(player, value as String) } + "bots" -> showGeBots(player) + "botsearch" -> sendInputDialogue(player, InputType.STRING_LONG, "Enter search term:"){value -> + showGeBotsearch(player, value as String) + } else -> reject(player, "Invalid mode used. Available modes are: buying, selling, search") } } @@ -281,6 +290,10 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ first = false } + val stringSelection = StringSelection(sb.toString()) + val clpbrd = Toolkit.getDefaultToolkit().systemClipboard + clpbrd.setContents(stringSelection, null) + log.clear() player.setAttribute("loc-log",log) } @@ -447,10 +460,15 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ } define("testlady",Command.Privilege.ADMIN){player,_ -> - player.antiMacroHandler.event = RandomEvents.EVIL_CHICKEN.npc.create(player,null,"sexam") + player.antiMacroHandler.event = RandomEvents.RIVER_TROLL.npc.create(player) player.antiMacroHandler.event!!.init() } + define("revent",Command.Privilege.ADMIN){player,_ -> + println(player.pulseManager.current) + player.antiMacroHandler.fireEvent() + } + define("addcredits",Command.Privilege.ADMIN){player,_ -> player.details.credits += 100 } @@ -510,37 +528,84 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ } } + fun showGeBotsearch(player: Player, searchTerm: String) + { + val offerAmounts = HashMap() + val offerPrice = HashMap() + + val offers = GrandExchange.getBotOffers().filter { getItemName(it.itemID).contains(searchTerm, true) } + + for(offer in offers) + { + offerAmounts[offer.itemID] = offer.amount + offerPrice[offer.itemID] = offer.offeredValue + } + + val entries = offerAmounts.entries + var lineId = 11 + setScrollTitle(player, "Bot Stock - \"$searchTerm\"") + for(i in 0..299) { + val offer = entries.elementAtOrNull(i) + if (offer != null) + setInterfaceText(player, "${getItemName(offer.key)} (x${offer.value}) -> Price: ${offerPrice[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) + else + setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) + } + openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + } + + fun showGeBots(player: Player) + { + val offerAmounts = HashMap() + val offerPrice = HashMap() + + val offers = GrandExchange.getBotOffers() + + for(offer in offers) + { + offerAmounts[offer.itemID] = offer.amount + offerPrice[offer.itemID] = offer.offeredValue + } + + val entries = offerAmounts.entries + var lineId = 11 + setScrollTitle(player, "Bot Stock") + for(i in 0..299) { + val offer = entries.elementAtOrNull(i) + if (offer != null) + setInterfaceText(player, "${getItemName(offer.key)} (x${offer.value}) -> Price: ${offerPrice[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) + else + setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) + } + openInterface(player, Components.QUESTJOURNAL_SCROLL_275) + } + fun showGeSell(player: Player){ - val offers = HashMap() - for (offerIDs in OfferManager.OFFERS_BY_ITEMID) { - var totalOffered = 0 - for (offer in offerIDs.value) { - if (offer.offerState != OfferState.PENDING && offer.sell) { - totalOffered += offer.amountLeft - } - } - if (totalOffered != 0) { - offers[offerIDs.key] = totalOffered - } - } - for (offerIDs in OfferManager.BOT_OFFERS) { - if (offerIDs.value > 0) { - if (offers[offerIDs.key] == null) { - offers[offerIDs.key] = offerIDs.value - } else { - offers[offerIDs.key] = offers[offerIDs.key]!! + offerIDs.value - } - } + val offerAmounts = HashMap() + val lowestPrice = HashMap() + + val offers = GrandExchange.getValidOffers() + + for(offer in offers) + { + if(!offer.sell) continue + var amount = offerAmounts[offer.itemID] ?: 0 + amount += offer.amountLeft + + var price = lowestPrice[offer.itemID] ?: Integer.MAX_VALUE + if(offer.offeredValue < price) price = offer.offeredValue + + offerAmounts[offer.itemID] = amount + lowestPrice[offer.itemID] = price } - val offerList = offers.keys.map { "${ItemDefinition.forId(it).name} - ${offers[it]}" }.sorted() - + val entries = offerAmounts.entries var lineId = 11 setScrollTitle(player, "Active Sell Offers") for(i in 0..299) { - val offer = offerList.elementAtOrNull(i) + val offer = entries.elementAtOrNull(i) if (offer != null) - setInterfaceText(player, offer, Components.QUESTJOURNAL_SCROLL_275, lineId++) + setInterfaceText(player, "${getItemName(offer.key)} (x${offer.value}) -> Lowest: ${lowestPrice[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) else setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) } @@ -548,26 +613,31 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ } fun showGeBuy(player: Player){ - val offers = HashMap() - for (offerIDs in OfferManager.OFFERS_BY_ITEMID) { - var totalOffered = 0 - for (offer in offerIDs.value) { - if (offer.offerState != OfferState.PENDING && !offer.sell) { - totalOffered += offer.amountLeft - } - } - if (totalOffered != 0) { - offers[offerIDs.key] = totalOffered - } - } - val offerList = offers.keys.map { "${itemDefinition(it).name} - ${offers[it]}" }.sorted() + val offerAmounts = HashMap() + val highestPrice = HashMap() + val offers = GrandExchange.getValidOffers() + + for(offer in offers) + { + if(offer.sell) continue + var amount = offerAmounts[offer.itemID] ?: 0 + amount += offer.amountLeft + + var price = highestPrice[offer.itemID] ?: 0 + if(offer.offeredValue > price) price = offer.offeredValue + + offerAmounts[offer.itemID] = amount + highestPrice[offer.itemID] = price + } + + val entries = offerAmounts.entries var lineId = 11 setScrollTitle(player, "Active Buy Offers") for(i in 0..299) { - val offer = offerList.elementAtOrNull(i) + val offer = entries.elementAtOrNull(i) if (offer != null) - setInterfaceText(player, offer, Components.QUESTJOURNAL_SCROLL_275, lineId++) + setInterfaceText(player, "${getItemName(offer.key)} (x${offer.value}) -> Highest: ${highestPrice[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) else setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) } @@ -575,59 +645,64 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ } fun showOffers(player: Player, searchTerm: String){ - val fakeOffers = ArrayList() + val offers = GrandExchange.getValidOffers().filter { getItemName(it.itemID).contains(searchTerm, true) } + val buyingAmount = HashMap() + val buyingHighest = HashMap() + val sellingAmount = HashMap() + val sellingLowest = HashMap() - OfferManager.OFFERS_BY_ITEMID.forEach { (id, offers) -> - if(searchTerm.toLowerCase().contains(itemDefinition(id).name.toLowerCase()) || itemDefinition(id).name.toLowerCase().contains(searchTerm.toLowerCase())){ - offers.forEach { - fakeOffers.add(FakeOffer(it.sell, itemDefinition(id).name, it.amount)) - } + for(offer in offers) + { + if(offer.sell) + { + var price = sellingLowest[offer.itemID] ?: Int.MAX_VALUE + if(offer.offeredValue < price) price = offer.offeredValue + + var amount = sellingAmount[offer.itemID] ?: 0 + amount += offer.amountLeft + + sellingAmount[offer.itemID] = amount + sellingLowest[offer.itemID] = price + } + else + { + var price = buyingHighest[offer.itemID] ?: 0 + if(offer.offeredValue > price) price = offer.offeredValue + + var amount = buyingAmount[offer.itemID] ?: 0 + amount += offer.amountLeft + + buyingAmount[offer.itemID] = amount + buyingHighest[offer.itemID] = price } } - OfferManager.BOT_OFFERS.forEach{ (id, amount) -> - val name = getItemName(id) - if(searchTerm.toLowerCase().contains(name) || name.toLowerCase().contains(searchTerm.toLowerCase())) - fakeOffers.add(FakeOffer(true, getItemName(id), amount)) - } - - val buyingList = fakeOffers.filter { !it.sell } - val sellingList = fakeOffers.filter { it.sell } - - val buyingMap = HashMap() - val sellingMap = HashMap() - - buyingList.forEach { - if(buyingMap[it.name] == null) buyingMap[it.name] = it.amount - else buyingMap[it.name] = buyingMap[it.name]?.plus(it.amount) ?: 0 - } - - sellingList.forEach { - if(sellingMap[it.name] == null) sellingMap[it.name] = it.amount - else sellingMap[it.name] = sellingMap[it.name]?.plus(it.amount) ?: 0 - } - - val buyList = buyingMap.map { (name,amount) -> "[Buying] $name - $amount" }.sortedBy { it.length }.toMutableList() - val sellList = sellingMap.map { (name, amount) -> "[Selling] $name - $amount" }.sortedBy { it.length }.toMutableList() - - sellList.reverse() - sellList.add(" ") - sellList.reverse() - - SystemLogger.logInfo("bl: ${buyList.size} sl: ${sellList.size}") - setScrollTitle(player, "Results for \"$searchTerm\"") - if(buyList.isEmpty()){ - buyList.add("[Buying] Nothing!") - } - if(sellList.size == 1){ - sellList.add("[Selling] Nothing!") - } var lineId = 11 for(i in 0..299) { - val offer = if(i < buyList.size) buyList.getOrNull(i) else sellList.getOrNull(i - (buyList.size)) - setInterfaceText(player, offer ?: "", Components.QUESTJOURNAL_SCROLL_275, lineId++) + if(i > buyingAmount.keys.size) + { + val offer = sellingAmount.entries.elementAtOrNull(i - buyingAmount.keys.size) + if(offer != null) { + setInterfaceText(player, "[SELLING] ${getItemName(offer.key)} (x${offer.value}) -> Lowest: ${sellingLowest[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) + continue + } + } + else if(i < buyingAmount.keys.size) + { + val offer = buyingAmount.entries.elementAtOrNull(i) + if(offer != null) { + setInterfaceText(player, "[BUYING] ${getItemName(offer.key)} (x${offer.value}) -> Highest: ${buyingHighest[offer.key]}gp", Components.QUESTJOURNAL_SCROLL_275, lineId++) + continue + } + } + else { + setInterfaceText(player, " ", Components.QUESTJOURNAL_SCROLL_275, lineId++) + continue + } + + setInterfaceText(player, "", Components.QUESTJOURNAL_SCROLL_275, lineId++) } openInterface(player, Components.QUESTJOURNAL_SCROLL_275) } @@ -636,5 +711,4 @@ class MiscCommandSet : CommandSet(Command.Privilege.ADMIN){ setInterfaceText(player, text, Components.QUESTJOURNAL_SCROLL_275, 2) } - class FakeOffer(val sell: Boolean,val name: String,val amount: Int) } diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/SlayerCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/SlayerCommandSet.kt index c841b30bd..9ff6790bd 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/SlayerCommandSet.kt +++ b/Server/src/main/kotlin/rs09/game/system/command/sets/SlayerCommandSet.kt @@ -13,7 +13,7 @@ class SlayerCommandSet : CommandSet(Command.Privilege.ADMIN){ define("finishtask"){player,_ -> notify(player, "Kill the npc that spawned to finish your task.") player.slayer.amount = 1 - val finisher = NPC(player.slayer.task.npcs[0], player.location) + val finisher = NPC(player.slayer.task?.npcs?.get(0) ?: 0, player.location) finisher.isRespawn = false finisher.init() } diff --git a/Server/src/main/kotlin/rs09/game/system/command/sets/SpawnCommandSet.kt b/Server/src/main/kotlin/rs09/game/system/command/sets/SpawnCommandSet.kt index f2c8c30ee..df371d8b2 100644 --- a/Server/src/main/kotlin/rs09/game/system/command/sets/SpawnCommandSet.kt +++ b/Server/src/main/kotlin/rs09/game/system/command/sets/SpawnCommandSet.kt @@ -1,14 +1,16 @@ package rs09.game.system.command.sets import core.cache.Cache -import core.game.node.entity.npc.NPC -import core.game.node.item.Item import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder -import core.plugin.Initializable +import core.game.node.entity.npc.NPC +import core.game.node.item.Item import rs09.game.system.SystemLogger import rs09.game.system.command.Command import rs09.game.system.command.CommandPlugin +import core.plugin.Initializable +import java.awt.Toolkit +import java.awt.datatransfer.StringSelection @Initializable class SpawnCommandSet : CommandSet(Command.Privilege.ADMIN){ @@ -27,7 +29,9 @@ class SpawnCommandSet : CommandSet(Command.Privilege.ADMIN){ npc.direction = player.direction npc.init() npc.isWalks = args.size > 2 - val npcString = "{" + player!!.username + "," + npc.id + "," + npc.location.x + "," + npc.location.y + "," + npc.location.z + "}" + val npcString = "{" + npc.location.x + "," + npc.location.y + "," + npc.location.z + "," + (if (npc.isWalks) "1" else "0") + "," + npc.direction.ordinal + "}" + val clpbrd = Toolkit.getDefaultToolkit().systemClipboard + clpbrd.setContents(StringSelection(npcString), null) println(npcString) } diff --git a/Server/src/main/kotlin/rs09/game/world/GameWorld.kt b/Server/src/main/kotlin/rs09/game/world/GameWorld.kt index 89324cc40..c0982bc92 100644 --- a/Server/src/main/kotlin/rs09/game/world/GameWorld.kt +++ b/Server/src/main/kotlin/rs09/game/world/GameWorld.kt @@ -1,5 +1,6 @@ package rs09.game.world +import api.LoginListener import core.cache.Cache import core.cache.def.impl.SceneryDefinition import core.game.ge.GrandExchangeDatabase @@ -41,6 +42,12 @@ object GameWorld { @JvmStatic val majorUpdateWorker = MajorUpdateWorker() + /** + * Login listeners + */ + @JvmStatic + val loginListeners = ArrayList() + @JvmStatic val STARTUP_PLUGINS: List = ArrayList() private val configParser = ConfigParser() diff --git a/Server/src/main/kotlin/rs09/game/world/callback/CallbackHub.kt b/Server/src/main/kotlin/rs09/game/world/callback/CallbackHub.kt index c04369b71..aff6edd83 100644 --- a/Server/src/main/kotlin/rs09/game/world/callback/CallbackHub.kt +++ b/Server/src/main/kotlin/rs09/game/world/callback/CallbackHub.kt @@ -4,7 +4,6 @@ import core.game.node.entity.skill.hunter.ImpetuousImpulses import core.game.world.callback.CallBack import core.game.world.map.zone.ZoneBuilder import rs09.game.ge.GrandExchange -import rs09.game.ge.OfferManager import rs09.game.system.SystemLogger import java.util.* diff --git a/Server/src/main/kotlin/rs09/game/world/repository/DisconnectionQueue.kt b/Server/src/main/kotlin/rs09/game/world/repository/DisconnectionQueue.kt index 5ee8ecab6..5ba79b7b2 100644 --- a/Server/src/main/kotlin/rs09/game/world/repository/DisconnectionQueue.kt +++ b/Server/src/main/kotlin/rs09/game/world/repository/DisconnectionQueue.kt @@ -55,7 +55,7 @@ class DisconnectionQueue { } Repository.playerNames.remove(player.name) Repository.lobbyPlayers.remove(player) - Repository.players.remove(player) + Repository.removePlayer(player) Repository.LOGGED_IN_PLAYERS.remove(player.details.username) SystemLogger.logInfo("Player cleared. Removed ${player.details.username}") try { diff --git a/Server/src/main/kotlin/rs09/game/world/repository/Repository.kt b/Server/src/main/kotlin/rs09/game/world/repository/Repository.kt index b2450e94d..8f4fe3f8b 100644 --- a/Server/src/main/kotlin/rs09/game/world/repository/Repository.kt +++ b/Server/src/main/kotlin/rs09/game/world/repository/Repository.kt @@ -19,6 +19,8 @@ object Repository { */ @JvmStatic val players = NodeList(ServerConstants.MAX_PLAYERS) + val uid_map = HashMap(ServerConstants.MAX_PLAYERS) + /** * Represents the repository of active npcs. */ @@ -129,6 +131,18 @@ object Repository { return null } + @JvmStatic + fun addPlayer(player: Player){ + players.add(player) + uid_map[player.details.uid] = player + } + + @JvmStatic + fun removePlayer(player: Player){ + players.remove(player) + uid_map.remove(player.details.uid) + } + /** * Find a non-player character. * @param npcId The non-player character's id.