From e404eadb3b366d5fa38a1172591b0ecd2ee76e5f Mon Sep 17 00:00:00 2001 From: ipkpjersi <33754783+ipkpjersi@users.noreply.github.com> Date: Fri, 20 Sep 2024 21:40:58 -0400 Subject: [PATCH] Added NPC kill counts with message display (#644) * Added kill counts with message display * Fixed barrows chest count * Preserve insertion order of NPC IDs for Constants * Added KC command and interfaces * Added more robustness to KC command * Added alias and documentation to new commands * Added more aliases for new commands * Merged slayer KC interface names * Preserve slayerkc nameToKills insertion order and formatting fix * Updated comment * Added missing boss NPC id * Moved giant mole up in set * Emptied out quest interface properly for kc * Fixed whitespace * Fixed formatting * Hide boss KC messages by default --- .../src/main/java/com/rs2/Constants.java | 90 +++++++++++ .../rs2/game/content/minigames/Barrows.java | 4 + .../src/main/java/com/rs2/game/npcs/Npc.java | 4 + .../java/com/rs2/game/npcs/NpcHandler.java | 20 ++- .../java/com/rs2/game/players/Player.java | 18 +++ .../java/com/rs2/game/players/PlayerSave.java | 37 +++++ .../com/rs2/net/packets/impl/Commands.java | 150 +++++++++++++++++- 7 files changed, 318 insertions(+), 5 deletions(-) diff --git a/2006Scape Server/src/main/java/com/rs2/Constants.java b/2006Scape Server/src/main/java/com/rs2/Constants.java index 0d83e38e..d04a2d91 100644 --- a/2006Scape Server/src/main/java/com/rs2/Constants.java +++ b/2006Scape Server/src/main/java/com/rs2/Constants.java @@ -1,6 +1,11 @@ package com.rs2; +import com.rs2.game.content.StaticNpcList; + import java.math.BigInteger; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashSet; public class Constants { @@ -105,4 +110,89 @@ public class Constants { CRAFTING = 12, SMITHING = 13, MINING = 14, HERBLORE = 15, AGILITY = 16, THIEVING = 17, SLAYER = 18, FARMING = 19, RUNECRAFTING = 20; + + public static final LinkedHashSet BOSS_NPC_IDS = new LinkedHashSet<>(Arrays.asList( + StaticNpcList.CHAOS_ELEMENTAL, + StaticNpcList.DAGANNOTH_REX, + StaticNpcList.DAGANNOTH_PRIME, + StaticNpcList.DAGANNOTH_SUPREME, + StaticNpcList.GIANT_MOLE, + StaticNpcList.KING_BLACK_DRAGON, + StaticNpcList.KALPHITE_QUEEN, + StaticNpcList.TZTOKJAD + )); + + public static final LinkedHashSet SLAYER_NPC_IDS = new LinkedHashSet<>(Arrays.asList( + StaticNpcList.CRAWLING_HAND, + StaticNpcList.CRAWLING_HAND_1649, + StaticNpcList.CRAWLING_HAND_1650, + StaticNpcList.CRAWLING_HAND_1651, + StaticNpcList.CRAWLING_HAND_1652, + StaticNpcList.CRAWLING_HAND_1653, + StaticNpcList.CRAWLING_HAND_1654, + StaticNpcList.CRAWLING_HAND_1655, + StaticNpcList.CRAWLING_HAND_1656, + StaticNpcList.CRAWLING_HAND_1657, + StaticNpcList.CAVE_BUG, + StaticNpcList.CAVE_CRAWLER, + StaticNpcList.CAVE_CRAWLER_1601, + StaticNpcList.CAVE_CRAWLER_1602, + StaticNpcList.CAVE_CRAWLER_1603, + StaticNpcList.BANSHEE, + StaticNpcList.CAVE_SLIME, + StaticNpcList.ROCKSLUG, + StaticNpcList.ROCKSLUG_1623, + StaticNpcList.DESERT_LIZARD, + StaticNpcList.DESERT_LIZARD_2805, + StaticNpcList.DESERT_LIZARD_2806, + StaticNpcList.COCKATRICE, + StaticNpcList.COCKATRICE_1621, + StaticNpcList.PYREFIEND, + StaticNpcList.PYREFIEND_1634, + StaticNpcList.PYREFIEND_1635, + StaticNpcList.PYREFIEND_1636, + StaticNpcList.MOGRE, + StaticNpcList.HARPIE_BUG_SWARM, + StaticNpcList.WALL_BEAST, + StaticNpcList.KILLERWATT, + StaticNpcList.KILLERWATT_3202, + StaticNpcList.BASILISK, + StaticNpcList.BASILISK_1617, + StaticNpcList.FEVER_SPIDER, + StaticNpcList.INFERNAL_MAGE, + StaticNpcList.INFERNAL_MAGE_1644, + StaticNpcList.INFERNAL_MAGE_1645, + StaticNpcList.INFERNAL_MAGE_1646, + StaticNpcList.INFERNAL_MAGE_1647, + StaticNpcList.JELLY, + StaticNpcList.JELLY_1638, + StaticNpcList.JELLY_1639, + StaticNpcList.JELLY_1640, + StaticNpcList.JELLY_1641, + StaticNpcList.JELLY_1642, + StaticNpcList.TUROTH, + StaticNpcList.TUROTH_1627, + StaticNpcList.TUROTH_1628, + StaticNpcList.TUROTH_1629, + StaticNpcList.TUROTH_1630, + StaticNpcList.TUROTH_1631, + StaticNpcList.TUROTH_1632, + StaticNpcList.ABERRANT_SPECTER, + StaticNpcList.ABERRANT_SPECTER_1605, + StaticNpcList.ABERRANT_SPECTER_1606, + StaticNpcList.ABERRANT_SPECTER_1607, + StaticNpcList.DUST_DEVIL, + StaticNpcList.KURASK, + StaticNpcList.KURASK_1609, + StaticNpcList.SKELETAL_WYVERN, + StaticNpcList.SKELETAL_WYVERN_3069, + StaticNpcList.SKELETAL_WYVERN_3070, + StaticNpcList.SKELETAL_WYVERN_3071, + StaticNpcList.GARGOYLE, + StaticNpcList.GARGOYLE_1611, + StaticNpcList.NECHRYAEL, + StaticNpcList.ABYSSAL_DEMON, + StaticNpcList.DARK_BEAST, + StaticNpcList.SMOKEDEVIL + )); } \ No newline at end of file diff --git a/2006Scape Server/src/main/java/com/rs2/game/content/minigames/Barrows.java b/2006Scape Server/src/main/java/com/rs2/game/content/minigames/Barrows.java index 95c556c0..d01bfb11 100644 --- a/2006Scape Server/src/main/java/com/rs2/game/content/minigames/Barrows.java +++ b/2006Scape Server/src/main/java/com/rs2/game/content/minigames/Barrows.java @@ -230,6 +230,10 @@ public class Barrows { } if (c.barrowsKillCount > 5 && checkBarrows()) { if (c.getItemAssistant().freeSlots() >= 4) { + c.incrementNpcKillCount(100000, 1); + if (c.displayBossKcMessages || c.displayRegularKcMessages) { + c.getPacketSender().sendMessage("Your Barrows Chest count is now: " + c.getNpcKillCount(100000)); + } reward(); resetBarrows(); } else { diff --git a/2006Scape Server/src/main/java/com/rs2/game/npcs/Npc.java b/2006Scape Server/src/main/java/com/rs2/game/npcs/Npc.java index bd5c4f12..fc0b2e26 100644 --- a/2006Scape Server/src/main/java/com/rs2/game/npcs/Npc.java +++ b/2006Scape Server/src/main/java/com/rs2/game/npcs/Npc.java @@ -65,6 +65,10 @@ public class Npc { transformUpdateRequired = true; updateRequired = true; } + + public String name() { + return NpcHandler.getNpcListName(this.npcType); + } public void shearSheep(Player player, int itemNeeded, int itemGiven, int animation, final int currentId, final int newId, int transformTime) { if (!player.getItemAssistant().playerHasItem(itemNeeded)) { diff --git a/2006Scape Server/src/main/java/com/rs2/game/npcs/NpcHandler.java b/2006Scape Server/src/main/java/com/rs2/game/npcs/NpcHandler.java index 92a23442..b394abf6 100644 --- a/2006Scape Server/src/main/java/com/rs2/game/npcs/NpcHandler.java +++ b/2006Scape Server/src/main/java/com/rs2/game/npcs/NpcHandler.java @@ -333,28 +333,34 @@ public class NpcHandler { npcs[slot] = newNPC; } - private void killedBarrow(int i) { + private boolean killedBarrow(int i) { + boolean barrows = false; Player c = (Client) PlayerHandler.players[npcs[i].killedBy]; if (c != null) { for (int o = 0; o < c.barrowsNpcs.length; o++) { if (npcs[i].npcType == c.barrowsNpcs[o][0]) { c.barrowsNpcs[o][1] = 2; // 2 for dead c.barrowsKillCount++; + barrows = true; } } } + return barrows; } - private void killedCrypt(int i) { + private boolean killedCrypt(int i) { + boolean crypt = false; Player c = (Client) PlayerHandler.players[npcs[i].killedBy]; if (c != null) { for (int o = 0; o < c.barrowCrypt.length; o++) { if (npcs[i].npcType == c.barrowCrypt[o][0]) { c.barrowsKillCount++; c.getPacketSender().sendString("" + c.barrowsKillCount, 4536); + crypt = true; } } } + return crypt; } public void newNPC(int npcType, int x, int y, int heightLevel, @@ -747,10 +753,16 @@ public class NpcHandler { npcs[i].animUpdateRequired = true; npcs[i].freezeTimer = 0; npcs[i].applyDead = true; - killedBarrow(i); - killedCrypt(i); + boolean barrows = killedBarrow(i); + boolean crypt = killedCrypt(i); npcs[i].actionTimer = 4; // delete time resetPlayersInCombat(i); + if (!crypt && !barrows && c != null) { + c.incrementNpcKillCount(npcs[i].npcType, 1); + if (c.displayRegularKcMessages || (c.displayBossKcMessages && Constants.BOSS_NPC_IDS.contains(npcs[i].npcType)) || (c.displaySlayerKcMessages && Constants.SLAYER_NPC_IDS.contains(npcs[i].npcType))) { + c.getPacketSender().sendMessage("Your " + npcs[i].name() + " kill count is now: " + c.getNpcKillCount(npcs[i].npcType)); + } + } } else if (npcs[i].actionTimer == 0 && npcs[i].applyDead && npcs[i].needRespawn == false) { diff --git a/2006Scape Server/src/main/java/com/rs2/game/players/Player.java b/2006Scape Server/src/main/java/com/rs2/game/players/Player.java index b40fb4d5..ed3f43ac 100644 --- a/2006Scape Server/src/main/java/com/rs2/game/players/Player.java +++ b/2006Scape Server/src/main/java/com/rs2/game/players/Player.java @@ -144,6 +144,24 @@ public abstract class Player { private GateHandler gateHandler = new GateHandler(); private SingleGates singleGates = new SingleGates(); private DoubleGates doubleGates = new DoubleGates(); + + private Map npcKillCounts = new HashMap<>(); + public boolean displayBossKcMessages = false; + public boolean displaySlayerKcMessages = false; + public boolean displayRegularKcMessages = false; + + public int getNpcKillCount(int npcId) { + return npcKillCounts.getOrDefault(npcId, 0); + } + + public Map getNpcKillCounts() { + return npcKillCounts; + } + + public void incrementNpcKillCount(int npcId, int count) { + npcKillCounts.put(npcId, npcKillCounts.getOrDefault(npcId, 0) + count); + } + public int lastMainFrameInterface = -1; //Possibly used in future to prevent packet exploits public int getXPRate() { return xpRate; } diff --git a/2006Scape Server/src/main/java/com/rs2/game/players/PlayerSave.java b/2006Scape Server/src/main/java/com/rs2/game/players/PlayerSave.java index 70a4b327..af5c3cec 100644 --- a/2006Scape Server/src/main/java/com/rs2/game/players/PlayerSave.java +++ b/2006Scape Server/src/main/java/com/rs2/game/players/PlayerSave.java @@ -5,6 +5,7 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; +import java.util.Map; import com.rs2.util.Misc; @@ -439,6 +440,15 @@ public class PlayerSave { case "discord-user-id": player.discordCode = token2; break; + case "display-boss-kc-messages": + player.displayBossKcMessages = Boolean.parseBoolean(token2); + break; + case "display-slayer-kc-messages": + player.displaySlayerKcMessages = Boolean.parseBoolean(token2); + break; + case "display-regular-kc-messages": + player.displayRegularKcMessages = Boolean.parseBoolean(token2); + break; } break; case 3: @@ -480,6 +490,16 @@ public class PlayerSave { if (token.equals("character-ignore")) { player.ignores[Integer.parseInt(token3[0])] = Long.parseLong(token3[1]); } + case 10: + if (token.startsWith("npcid-")) { + try { + int npcId = Integer.parseInt(token.substring(6)); + int killCount = Integer.parseInt(token2); + player.incrementNpcKillCount(npcId, killCount); + } catch (NumberFormatException e) { + System.out.println("Error parsing NPC kill count for " + token); + } + } break; } } else { @@ -511,6 +531,9 @@ public class PlayerSave { case "[IGNORES]": ReadMode = 9; break; + case "[NPC-KILLS]": + ReadMode = 10; + break; case "[EOF]": try { characterfile.close(); @@ -833,6 +856,12 @@ public class PlayerSave { characterfile.newLine(); characterfile.write("discord-user-id = " + player.discordCode); characterfile.newLine(); + characterfile.write("display-boss-kc-messages = " + player.displayBossKcMessages); + characterfile.newLine(); + characterfile.write("display-slayer-kc-messages = " + player.displaySlayerKcMessages); + characterfile.newLine(); + characterfile.write("display-regular-kc-messages = " + player.displayRegularKcMessages); + characterfile.newLine(); characterfile.newLine(); /* EQUIPMENT */ @@ -905,6 +934,14 @@ public class PlayerSave { } characterfile.newLine(); + characterfile.write("[NPC-KILLS]"); + characterfile.newLine(); + for (Map.Entry entry : player.getNpcKillCounts().entrySet()) { + characterfile.write("npcid-" + entry.getKey() + " = " + entry.getValue()); + characterfile.newLine(); + } + characterfile.newLine(); + /* EOF */ characterfile.write("[EOF]"); characterfile.newLine(); diff --git a/2006Scape Server/src/main/java/com/rs2/net/packets/impl/Commands.java b/2006Scape Server/src/main/java/com/rs2/net/packets/impl/Commands.java index be577777..a43b2327 100644 --- a/2006Scape Server/src/main/java/com/rs2/net/packets/impl/Commands.java +++ b/2006Scape Server/src/main/java/com/rs2/net/packets/impl/Commands.java @@ -2,12 +2,13 @@ package com.rs2.net.packets.impl; import static com.rs2.util.GameLogger.writeLog; -import java.util.Arrays; +import java.util.*; import com.rs2.Connection; import com.rs2.Constants; import com.rs2.GameEngine; import com.rs2.game.bots.BotHandler; +import com.rs2.game.npcs.NPCDefinition; import com.rs2.game.npcs.NpcHandler; import com.rs2.game.players.*; import com.rs2.game.players.antimacro.AntiSpam; @@ -229,6 +230,135 @@ public class Commands implements PacketType { case "prayer": player.getPacketSender().sendMessage(String.format("Prayer points: %d", player.playerLevel[Constants.PRAYER])); break; + case "togglenpckillmsgs": + case "togglenpckillmsg": + case "togglenpckcmsgs": + case "togglenpckcmsg": + player.displayRegularKcMessages = !player.displayRegularKcMessages; + player.getPacketSender().sendMessage("You now have regular NPC kill count messages: " + (player.displayRegularKcMessages ? "enabled" : "disabled")); + break; + case "togglebosskillmsgs": + case "togglebosskillmsg": + case "togglebossksmsgs": + case "togglebossksmsg": + player.displayBossKcMessages = !player.displayBossKcMessages; + player.getPacketSender().sendMessage("You now have boss NPC kill count messages: " + (player.displayBossKcMessages ? "enabled" : "disabled")); + break; + case "toggleslayerkillmsgs": + case "toggleslayerkillmsg": + case "toggleslayerkcmsgs": + case "toggleslayerkcmsg": + player.displaySlayerKcMessages = !player.displaySlayerKcMessages; + player.getPacketSender().sendMessage("You now have slayer NPC kill count messages: " + (player.displaySlayerKcMessages ? "enabled" : "disabled")); + break; + case "kc": + case "kills": + case "checknpckill": + case "checknpckills": + if (arguments.length > 0) { + // Combine all arguments into a single string, assuming space-separated + String npcNameInput = String.join(" ", arguments).toLowerCase(); + try { + // Try to parse as an ID + int npcId = Integer.parseInt(arguments[0]); + int killCount = player.getNpcKillCounts().getOrDefault(npcId, 0); + String npcName = NPCDefinition.forId(npcId).getName(); + player.getPacketSender().sendMessage("Kill count for " + npcName + ": " + killCount); + } catch (NumberFormatException e) { + // If not an ID, treat as a name + List matchingDefs = new ArrayList<>(); + for (int id = 0; id <= 3789; id++) { + try { + NPCDefinition def = NPCDefinition.forId(id); + if (def.getName().toLowerCase().startsWith(npcNameInput)) { + matchingDefs.add(def); + } + } catch (Exception exception) { + System.err.println("Exception during kc command: " + exception.getMessage()); + break; + } + } + if (matchingDefs.isEmpty()) { + player.getPacketSender().sendMessage("No NPCs found with the name: " + npcNameInput); + } else { + boolean empty = true; + for (NPCDefinition def : matchingDefs) { + int killCount = player.getNpcKillCounts().getOrDefault(def.getId(), 0); + if (killCount > 0) { + empty = false; + player.getPacketSender().sendMessage("Kill count for " + def.getName() + " (ID: " + def.getId() + "): " + killCount); + } + } + if (empty) { + player.getPacketSender().sendMessage("Kill count for " + npcNameInput + ": 0"); + } + } + } + } else { + player.getPacketSender().sendMessage("Please provide an NPC ID or name."); + } + break; + case "bosskillcounts": + case "bosskillcount": + case "bosskc": + case "kcboss": + // Clear all lines + for (int i = 8144; i < 8196; i++) { + player.getPacketSender().sendString("", i); + } + for (int i = 12174; i < (12174 + 50); i++) { + player.getPacketSender().sendString("", i); + } + for (int i = 14945; i < (14945 + 100); i++) { + player.getPacketSender().sendString("", i); + } + + player.getPacketSender().sendString("@dre@Boss Kill Counts", 8144); + int bossLineId = 8147; // Starting line for display + player.getPacketSender().sendString("Barrows Chests: " + player.getNpcKillCounts().getOrDefault(100000, 0), bossLineId++); + for (Integer bossId : Constants.BOSS_NPC_IDS) { + int killCount = player.getNpcKillCounts().getOrDefault(bossId, 0); + String npcName = NPCDefinition.forId(bossId).getName(); + player.getPacketSender().sendString(npcName + ": " + killCount, bossLineId++); + } + + player.getPacketSender().showInterface(8134); + break; + case "slayerkillcounts": + case "slayerkillcount": + case "slayerkc": + case "kcslayer": + // Clear all lines + for (int i = 8144; i < 8196; i++) { + player.getPacketSender().sendString("", i); + } + for (int i = 12174; i < (12174 + 50); i++) { + player.getPacketSender().sendString("", i); + } + for (int i = 14945; i < (14945 + 100); i++) { + player.getPacketSender().sendString("", i); + } + + player.getPacketSender().sendString("@dre@Slayer Kill Counts", 8144); + int slayerLineId = 8147; // Starting line for display + + // LinkedHashMap to store cumulative kills by NPC name + LinkedHashMap nameToKills = new LinkedHashMap<>(); + + // Populate the HashMap + for (Integer npcId : Constants.SLAYER_NPC_IDS) { + String npcName = NPCDefinition.forId(npcId).getName(); + int killCount = player.getNpcKillCounts().getOrDefault(npcId, 0); + nameToKills.put(npcName, nameToKills.getOrDefault(npcName, 0) + killCount); + } + + // Display the results + for (Map.Entry entry : nameToKills.entrySet()) { + player.getPacketSender().sendString(entry.getKey() + ": " + entry.getValue(), slayerLineId++); + } + + player.getPacketSender().showInterface(8134); + break; case "snow": Calendar date = new GregorianCalendar(); if ((date.get(Calendar.MONTH) + 1) == 12 && !player.inWild()) { @@ -325,6 +455,24 @@ public class Commands implements PacketType { "::withdrawshop(::wshop)", "Withdraw profits from player owned shop", "", + "::togglenpckillmsgs(::togglenpckcmsgs)", + "Toggle regular NPC kill count message display","", + "", + "::togglebosskillmsgs(::togglebosskcmsgs)", + "Toggle regular Boss kill count message display","", + "", + "::toggleslayerkillmsgs(::toggleslayerkcmsgs)", + "Toggle regular Slayer kill count message display", + "", + "::kc(::checknpckills)", + "Search for your NPC kills for an NPC name or ID", + "", + "::bosskc(::toggleslayerkcmsgs)", + "View your boss kills", + "", + "::slayerkc(::toggleslayerkcmsgs)", + "View your slayer kills", + "", "::snow", "Add some snow in your mainscreen(works only in december)", (Constants.VARIABLE_XP_RATE ? "\\n" + "::xprate\\n" + "Opens dialogue for the player to set/increase their XP rate." : ""),