From fcbf5cae47897e188c4fb6c8efdb6f5a1b7135ec Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 5 Feb 2024 15:34:10 -0500 Subject: [PATCH] [Commands] Add #show special_abilities (#4043) * [Commands] Add #show special_abilities # Notes - Allows operators to see what special abilities an NPC has. - Move special ability values to `common/emu_constants.h`. - Add `EQ::constants::GetSpecialAbilityMap()` and `EQ::constants::GetSpecialAbilityName()`. - Add `NPC::DescribeSpecialAbilities(client)` to describe special abilities to a specified client. # Images * Update npc.cpp --- common/emu_constants.cpp | 73 +++++++ common/emu_constants.h | 62 ++++++ zone/common.h | 59 ------ zone/gm_commands/show.cpp | 2 + zone/gm_commands/show/special_abilities.cpp | 13 ++ zone/npc.cpp | 212 ++++++++++++++++++++ zone/npc.h | 2 + 7 files changed, 364 insertions(+), 59 deletions(-) create mode 100644 zone/gm_commands/show/special_abilities.cpp diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 1a63d934a..e85059ecf 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -639,3 +639,76 @@ std::string EQ::constants::GetAppearanceTypeName(uint32 appearance_type) return std::string(); } + +const std::map& EQ::constants::GetSpecialAbilityMap() +{ + static const std::map special_ability_map = { + { SPECATK_SUMMON, "Summon" }, + { SPECATK_ENRAGE, "Enrage" }, + { SPECATK_RAMPAGE, "Rampage" }, + { SPECATK_AREA_RAMPAGE, "Area Rampage" }, + { SPECATK_FLURRY, "Flurry" }, + { SPECATK_TRIPLE, "Triple Attack" }, + { SPECATK_QUAD, "Quadruple Attack" }, + { SPECATK_INNATE_DW, "Dual Wield" }, + { SPECATK_BANE, "Bane Attack" }, + { SPECATK_MAGICAL, "Magical Attack" }, + { SPECATK_RANGED_ATK, "Ranged Attack" }, + { UNSLOWABLE, "Immune to Slow" }, + { UNMEZABLE, "Immune to Mesmerize" }, + { UNCHARMABLE, "Immune to Charm" }, + { UNSTUNABLE, "Immune to Stun" }, + { UNSNAREABLE, "Immune to Snare" }, + { UNFEARABLE, "Immune to Fear" }, + { UNDISPELLABLE, "Immune to Dispell" }, + { IMMUNE_MELEE, "Immune to Melee" }, + { IMMUNE_MAGIC, "Immune to Magic" }, + { IMMUNE_FLEEING, "Immune to Fleeing" }, + { IMMUNE_MELEE_EXCEPT_BANE, "Immune to Melee except Bane" }, + { IMMUNE_MELEE_NONMAGICAL, "Immune to Non-Magical Melee" }, + { IMMUNE_AGGRO, "Immune to Aggro" }, + { IMMUNE_AGGRO_ON, "Immune to Being Aggro" }, + { IMMUNE_CASTING_FROM_RANGE, "Immune to Ranged Spells" }, + { IMMUNE_FEIGN_DEATH, "Immune to Feign Death" }, + { IMMUNE_TAUNT, "Immune to Taunt" }, + { NPC_TUNNELVISION, "Tunnel Vision" }, + { NPC_NO_BUFFHEAL_FRIENDS, "Does Not Heal of Buff Allies" }, + { IMMUNE_PACIFY, "Immune to Pacify" }, + { LEASH, "Leashed" }, + { TETHER, "Tethered" }, + { DESTRUCTIBLE_OBJECT, "Destructible Object" }, + { NO_HARM_FROM_CLIENT, "Immune to Harm from Client" }, + { ALWAYS_FLEE, "Always Flees" }, + { FLEE_PERCENT, "Flee Percentage" }, + { ALLOW_BENEFICIAL, "Allows Beneficial Spells" }, + { DISABLE_MELEE, "Melee is Disabled" }, + { NPC_CHASE_DISTANCE, "Chase Distance" }, + { ALLOW_TO_TANK, "Allowed to Tank" }, + { IGNORE_ROOT_AGGRO_RULES, "Ignores Root Aggro" }, + { CASTING_RESIST_DIFF, "Casting Resist Difficulty" }, + { COUNTER_AVOID_DAMAGE, "Counter Damage Avoidance" }, + { PROX_AGGRO, "Proximity Aggro" }, + { IMMUNE_RANGED_ATTACKS, "Immune to Ranged Attacks" }, + { IMMUNE_DAMAGE_CLIENT, "Immune to Client Damage" }, + { IMMUNE_DAMAGE_NPC, "Immune to NPC Damage" }, + { IMMUNE_AGGRO_CLIENT, "Immune to Client Aggro" }, + { IMMUNE_AGGRO_NPC, "Immune to NPC Aggro" }, + { MODIFY_AVOID_DAMAGE, "Modify Damage Avoidance" }, + { IMMUNE_FADING_MEMORIES, "Immune to Memory Fades" }, + { IMMUNE_OPEN, "Immune to Open" }, + { IMMUNE_ASSASSINATE, "Immune to Assassinate" }, + { IMMUNE_HEADSHOT, "Immune to Headshot" }, + }; + + return special_ability_map; +} + +std::string EQ::constants::GetSpecialAbilityName(uint32 ability_id) +{ + const auto& a = EQ::constants::GetSpecialAbilityMap().find(ability_id); + if (a != EQ::constants::GetSpecialAbilityMap().end()) { + return a->second; + } + + return std::string(); +} diff --git a/common/emu_constants.h b/common/emu_constants.h index f886b0d77..c9e856858 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -401,6 +401,9 @@ namespace EQ extern const std::map& GetAppearanceTypeMap(); std::string GetAppearanceTypeName(uint32 animation_type); + extern const std::map& GetSpecialAbilityMap(); + std::string GetSpecialAbilityName(uint32 ability_id); + const int STANCE_TYPE_FIRST = stancePassive; const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE; @@ -597,6 +600,65 @@ enum class ApplySpellType { Raid }; +enum { + SPECATK_SUMMON = 1, + SPECATK_ENRAGE = 2, + SPECATK_RAMPAGE = 3, + SPECATK_AREA_RAMPAGE = 4, + SPECATK_FLURRY = 5, + SPECATK_TRIPLE = 6, + SPECATK_QUAD = 7, + SPECATK_INNATE_DW = 8, + SPECATK_BANE = 9, + SPECATK_MAGICAL = 10, + SPECATK_RANGED_ATK = 11, + UNSLOWABLE = 12, + UNMEZABLE = 13, + UNCHARMABLE = 14, + UNSTUNABLE = 15, + UNSNAREABLE = 16, + UNFEARABLE = 17, + UNDISPELLABLE = 18, + IMMUNE_MELEE = 19, + IMMUNE_MAGIC = 20, + IMMUNE_FLEEING = 21, + IMMUNE_MELEE_EXCEPT_BANE = 22, + IMMUNE_MELEE_NONMAGICAL = 23, + IMMUNE_AGGRO = 24, + IMMUNE_AGGRO_ON = 25, + IMMUNE_CASTING_FROM_RANGE = 26, + IMMUNE_FEIGN_DEATH = 27, + IMMUNE_TAUNT = 28, + NPC_TUNNELVISION = 29, + NPC_NO_BUFFHEAL_FRIENDS = 30, + IMMUNE_PACIFY = 31, + LEASH = 32, + TETHER = 33, + DESTRUCTIBLE_OBJECT = 34, + NO_HARM_FROM_CLIENT = 35, + ALWAYS_FLEE = 36, + FLEE_PERCENT = 37, + ALLOW_BENEFICIAL = 38, + DISABLE_MELEE = 39, + NPC_CHASE_DISTANCE = 40, + ALLOW_TO_TANK = 41, + IGNORE_ROOT_AGGRO_RULES = 42, + CASTING_RESIST_DIFF = 43, + COUNTER_AVOID_DAMAGE = 44, // Modify by percent NPC's opponents chance to riposte, block, parry or dodge individually, or for all skills + PROX_AGGRO = 45, + IMMUNE_RANGED_ATTACKS = 46, + IMMUNE_DAMAGE_CLIENT = 47, + IMMUNE_DAMAGE_NPC = 48, + IMMUNE_AGGRO_CLIENT = 49, + IMMUNE_AGGRO_NPC = 50, + MODIFY_AVOID_DAMAGE = 51, // Modify by percent the NPCs chance to riposte, block, parry or dodge individually, or for all skills + IMMUNE_FADING_MEMORIES = 52, + IMMUNE_OPEN = 53, + IMMUNE_ASSASSINATE = 54, + IMMUNE_HEADSHOT = 55, + MAX_SPECIAL_ATTACK = 56 +}; + namespace HeroicBonusBucket { diff --git a/zone/common.h b/zone/common.h index 395e38c27..39ff112d1 100644 --- a/zone/common.h +++ b/zone/common.h @@ -153,65 +153,6 @@ typedef enum { //focus types } focusType; //Any new FocusType needs to be added to the Mob::IsFocus function #define HIGHEST_FOCUS focusFcHealAmtCrit //Should always be last focusType in enum -enum { - SPECATK_SUMMON = 1, - SPECATK_ENRAGE = 2, - SPECATK_RAMPAGE = 3, - SPECATK_AREA_RAMPAGE = 4, - SPECATK_FLURRY = 5, - SPECATK_TRIPLE = 6, - SPECATK_QUAD = 7, - SPECATK_INNATE_DW = 8, - SPECATK_BANE = 9, - SPECATK_MAGICAL = 10, - SPECATK_RANGED_ATK = 11, - UNSLOWABLE = 12, - UNMEZABLE = 13, - UNCHARMABLE = 14, - UNSTUNABLE = 15, - UNSNAREABLE = 16, - UNFEARABLE = 17, - UNDISPELLABLE = 18, - IMMUNE_MELEE = 19, - IMMUNE_MAGIC = 20, - IMMUNE_FLEEING = 21, - IMMUNE_MELEE_EXCEPT_BANE = 22, - IMMUNE_MELEE_NONMAGICAL = 23, - IMMUNE_AGGRO = 24, - IMMUNE_AGGRO_ON = 25, - IMMUNE_CASTING_FROM_RANGE = 26, - IMMUNE_FEIGN_DEATH = 27, - IMMUNE_TAUNT = 28, - NPC_TUNNELVISION = 29, - NPC_NO_BUFFHEAL_FRIENDS = 30, - IMMUNE_PACIFY = 31, - LEASH = 32, - TETHER = 33, - DESTRUCTIBLE_OBJECT = 34, - NO_HARM_FROM_CLIENT = 35, - ALWAYS_FLEE = 36, - FLEE_PERCENT = 37, - ALLOW_BENEFICIAL = 38, - DISABLE_MELEE = 39, - NPC_CHASE_DISTANCE = 40, - ALLOW_TO_TANK = 41, - IGNORE_ROOT_AGGRO_RULES = 42, - CASTING_RESIST_DIFF = 43, - COUNTER_AVOID_DAMAGE = 44, //Modify by percent NPC's opponents chance to riposte, block, parry or dodge individually, or for all skills - PROX_AGGRO = 45, - IMMUNE_RANGED_ATTACKS = 46, - IMMUNE_DAMAGE_CLIENT = 47, - IMMUNE_DAMAGE_NPC = 48, - IMMUNE_AGGRO_CLIENT = 49, - IMMUNE_AGGRO_NPC = 50, - MODIFY_AVOID_DAMAGE = 51, //Modify by percent the NPCs chance to riposte, block, parry or dodge individually, or for all skills - IMMUNE_FADING_MEMORIES = 52, - IMMUNE_OPEN = 53, - IMMUNE_ASSASSINATE = 54, - IMMUNE_HEADSHOT = 55, - MAX_SPECIAL_ATTACK -}; - typedef enum { //fear states fearStateNotFeared = 0, fearStateRunning, //I am running, hoping to find a grid at my WP diff --git a/zone/gm_commands/show.cpp b/zone/gm_commands/show.cpp index 756eb9d93..b1aab35cd 100755 --- a/zone/gm_commands/show.cpp +++ b/zone/gm_commands/show.cpp @@ -30,6 +30,7 @@ #include "show/server_info.cpp" #include "show/skills.cpp" #include "show/spawn_status.cpp" +#include "show/special_abilities.cpp" #include "show/spells.cpp" #include "show/spells_list.cpp" #include "show/stats.cpp" @@ -88,6 +89,7 @@ void command_show(Client *c, const Seperator *sep) Cmd{.cmd = "server_info", .u = "server_info", .fn = ShowServerInfo, .a = {"#serverinfo"}}, Cmd{.cmd = "skills", .u = "skills", .fn = ShowSkills, .a = {"#showskills"}}, Cmd{.cmd = "spawn_status", .u = "spawn_status [all|disabled|enabled|Spawn ID]", .fn = ShowSpawnStatus, .a = {"#spawnstatus"}}, + Cmd{.cmd = "special_abilities", .u = "special_abilities", .fn = ShowSpecialAbilities, .a = {"#showspecialabilities"}}, Cmd{.cmd = "spells", .u = "spells [disciplines|spells]", .fn = ShowSpells, .a = {"#showspells"}}, Cmd{.cmd = "spells_list", .u = "spells_list", .fn = ShowSpellsList, .a = {"#showspellslist"}}, Cmd{.cmd = "stats", .u = "stats", .fn = ShowStats, .a = {"#showstats"}}, diff --git a/zone/gm_commands/show/special_abilities.cpp b/zone/gm_commands/show/special_abilities.cpp new file mode 100644 index 000000000..3e8f198ba --- /dev/null +++ b/zone/gm_commands/show/special_abilities.cpp @@ -0,0 +1,13 @@ +#include "../../client.h" + +void ShowSpecialAbilities(Client* c, const Seperator* sep) +{ + if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { + c->Message(Chat::White, "You must target an NPC to use this command."); + return; + } + + NPC* t = c->GetTarget()->CastToNPC(); + + t->DescribeSpecialAbilities(c); +} diff --git a/zone/npc.cpp b/zone/npc.cpp index 1eead5772..1f05f582a 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3995,3 +3995,215 @@ bool NPC::CanPathTo(float x, float y, float z) return !route.empty(); } + +void NPC::DescribeSpecialAbilities(Client* c) +{ + if (!c) { + return; + } + + // These abilities are simple on/off flags + static const std::vector toggleable_special_abilities = { + SPECATK_TRIPLE, + SPECATK_QUAD, + SPECATK_INNATE_DW, + SPECATK_BANE, + SPECATK_MAGICAL, + UNSLOWABLE, + UNMEZABLE, + UNCHARMABLE, + UNSTUNABLE, + UNSNAREABLE, + UNFEARABLE, + UNDISPELLABLE, + IMMUNE_MELEE, + IMMUNE_MAGIC, + IMMUNE_FLEEING, + IMMUNE_MELEE_EXCEPT_BANE, + IMMUNE_MELEE_NONMAGICAL, + IMMUNE_AGGRO, + IMMUNE_AGGRO_ON, + IMMUNE_CASTING_FROM_RANGE, + IMMUNE_FEIGN_DEATH, + IMMUNE_TAUNT, + NPC_NO_BUFFHEAL_FRIENDS, + IMMUNE_PACIFY, + DESTRUCTIBLE_OBJECT, + NO_HARM_FROM_CLIENT, + ALWAYS_FLEE, + ALLOW_BENEFICIAL, + DISABLE_MELEE, + ALLOW_TO_TANK, + IGNORE_ROOT_AGGRO_RULES, + PROX_AGGRO, + IMMUNE_RANGED_ATTACKS, + IMMUNE_DAMAGE_CLIENT, + IMMUNE_DAMAGE_NPC, + IMMUNE_AGGRO_CLIENT, + IMMUNE_AGGRO_NPC, + IMMUNE_FADING_MEMORIES, + IMMUNE_OPEN, + IMMUNE_ASSASSINATE, + IMMUNE_HEADSHOT, + }; + + // These abilities have parameters that need to be parsed out individually + static const std::map> parameter_special_abilities = { + { SPECATK_SUMMON, { "Cooldown in Milliseconds", "Health Percentage" } }, + { + SPECATK_ENRAGE, + { + "Health Percentage", + "Duration in Milliseconds", + "Cooldown in Milliseconds" + } + }, + { + SPECATK_RAMPAGE, + { + "Chance", + "Targets", + "Flat Damage Bonus", + "Ignore Armor Percentage", + "Ignore Flat Armor Amount", + "Critical Chance", + "Flat Critical Chance Bonus" + } + }, + { + SPECATK_AREA_RAMPAGE, + { + "Targets", + "Normal Attack Damage Percentage", + "Flat Damage Bonus", + "Ignore Armor Percentage", + "Ignore Flat Armor Amount", + "Critical Chance", + "Flat Critical Chance Bonus" + } + }, + { + SPECATK_FLURRY, + { + "Attacks", + "Normal Attack Damage Percentage", + "Flat Damage Bonus", + "Ignore Armor Percentage", + "Ignore Flat Armor Amount", + "Critical Chance", + "Flat Critical Chance Bonus" + } + }, + { + SPECATK_RANGED_ATK, + { + "Attacks", + "Maximum Range", + "Chance", + "Damage Percentage", + "Minimum Range" + } + }, + { NPC_TUNNELVISION, { "Aggro Modifier on Non-Tanks" } }, + { LEASH, { "Range" } }, + { TETHER, { "Range" } }, + { FLEE_PERCENT, { "Health Percentage", "Chance" } }, + { + NPC_CHASE_DISTANCE, + { + "Maximum Distance", + "Minimum Distance", + "Ignore Line of Sight" + } + }, + { CASTING_RESIST_DIFF, { "Resist Difficulty Value" } }, + { + COUNTER_AVOID_DAMAGE, + { + "Reduction Percentage for Block, Dodge, Parry, and Riposte", + "Reduction Percentage for Riposte", + "Reduction Percentage for Block", + "Reduction Percentage for Parry", + "Reduction Percentage for Dodge", + } + }, + { + MODIFY_AVOID_DAMAGE, + { + "Addition Percentage for Block, Dodge, Parry, and Riposte", + "Addition Percentage for Riposte", + "Addition Percentage for Block", + "Addition Percentage for Parry", + "Addition Percentage for Dodge", + } + }, + }; + + std::vector messages = { }; + + for (const auto& e : toggleable_special_abilities) { + if (GetSpecialAbility(e)) { + messages.emplace_back( + fmt::format( + "{} ({})", + EQ::constants::GetSpecialAbilityName(e), + e + ) + ); + } + } + + int slot_id; + + for (const auto& e : parameter_special_abilities) { + if (GetSpecialAbility(e.first)) { + slot_id = 0; + + for (const auto& a : e.second) { + messages.emplace_back( + fmt::format( + "{} ({}) | {}: {}", + EQ::constants::GetSpecialAbilityName(e.first), + e.first, + a, + GetSpecialAbilityParam(e.first, slot_id) + ) + ); + + slot_id++; + } + } + } + + if (messages.empty()) { + c->Message( + Chat::White, + fmt::format( + "{} has no special abilities.", + c->GetTargetDescription(this) + ).c_str() + ); + return; + } + + c->Message( + Chat::White, + fmt::format( + "{} has the following special abilit{}:", + c->GetTargetDescription(this), + messages.size() != 1 ? "ies" : "y" + ).c_str() + ); + + std::sort( + messages.begin(), + messages.end(), + [](const std::string& a, const std::string& b) { + return a < b; + } + ); + + for (const auto& e : messages) { + c->Message(Chat::White, e.c_str()); + } +} diff --git a/zone/npc.h b/zone/npc.h index 3bc250ab5..844198ed7 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -369,6 +369,8 @@ public: inline bool IsGuarding() const { return(m_GuardPoint.w != 0); } void SaveGuardSpotCharm(); + void DescribeSpecialAbilities(Client* c); + uint16 GetMeleeTexture1() const; uint16 GetMeleeTexture2() const;