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
This commit is contained in:
ipkpjersi
2024-09-20 21:40:58 -04:00
committed by GitHub
parent fd731242dd
commit e404eadb3b
7 changed files with 318 additions and 5 deletions
@@ -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<Integer> 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<Integer> 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
));
}
@@ -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 {
@@ -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)) {
@@ -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) {
@@ -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<Integer, Integer> 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<Integer, Integer> 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; }
@@ -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<Integer, Integer> entry : player.getNpcKillCounts().entrySet()) {
characterfile.write("npcid-" + entry.getKey() + " = " + entry.getValue());
characterfile.newLine();
}
characterfile.newLine();
/* EOF */
characterfile.write("[EOF]");
characterfile.newLine();
@@ -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<NPCDefinition> 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<String, Integer> 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<String, Integer> 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." : ""),