[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
This commit is contained in:
Alex King 2024-02-05 15:34:10 -05:00 committed by GitHub
parent 0ffea36905
commit fcbf5cae47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 364 additions and 59 deletions

View File

@ -639,3 +639,76 @@ std::string EQ::constants::GetAppearanceTypeName(uint32 appearance_type)
return std::string();
}
const std::map<uint32, std::string>& EQ::constants::GetSpecialAbilityMap()
{
static const std::map<uint32, std::string> 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();
}

View File

@ -401,6 +401,9 @@ namespace EQ
extern const std::map<uint32, std::string>& GetAppearanceTypeMap();
std::string GetAppearanceTypeName(uint32 animation_type);
extern const std::map<uint32, std::string>& 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
{

View File

@ -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

View File

@ -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"}},

View File

@ -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);
}

View File

@ -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<uint32> 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<uint32, std::vector<std::string>> 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<std::string> 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());
}
}

View File

@ -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;