[Cleanup/Feature] Add support for bots to #showstats/#mystats (#3427)

* Initial Push

* Update classes.cpp

* Update mob.cpp

* Update mob.cpp

* Update showstats.cpp

* Update mystats.cpp

* Remove unused variables.

* Update mob.cpp

* Update class.cpp

* Update race.cpp

* Update mob.h
This commit is contained in:
Alex King 2023-07-01 19:47:54 -04:00 committed by GitHub
parent 927d379e75
commit a1f2764978
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1544 additions and 855 deletions

View File

@ -17,6 +17,7 @@
*/
#include "../common/global_define.h"
#include "../common/classes.h"
#include "data_verification.h"
const char *GetClassIDName(uint8 class_id, uint8 level)
{
@ -750,3 +751,51 @@ uint8 ClassArmorType(uint8 class_id)
return ARMOR_TYPE_UNKNOWN;
}
}
const std::string GetPlayerClassAbbreviation(uint8 class_id)
{
if (!EQ::ValueWithin(class_id, WARRIOR, BERSERKER)) {
return std::string();
}
switch (class_id) {
case WARRIOR:
return "WAR";
case CLERIC:
return "CLR";
case PALADIN:
return "PAL";
case RANGER:
return "RNG";
case SHADOWKNIGHT:
return "SHD";
case DRUID:
return "DRU";
case MONK:
return "MNK";
case BARD:
return "BRD";
case ROGUE:
return "ROG";
case SHAMAN:
return "SHM";
case NECROMANCER:
return "NEC";
case WIZARD:
return "WIZ";
case MAGICIAN:
return "MAG";
case ENCHANTER:
return "ENC";
case BEASTLORD:
return "BST";
case BERSERKER:
return "BER";
}
return std::string();
}
bool IsPlayerClass(uint8 class_id) {
return EQ::ValueWithin(class_id, WARRIOR, BERSERKER);
}

View File

@ -19,6 +19,7 @@
#define CLASSES_CH
#include "../common/types.h"
#include <string>
#define NO_CLASS 0
#define WARRIOR 1
@ -127,6 +128,9 @@
const char* GetClassIDName(uint8 class_id, uint8 level = 0);
const char* GetPlayerClassName(uint32 player_class_value, uint8 level = 0);
bool IsPlayerClass(uint8 class_id);
const std::string GetPlayerClassAbbreviation(uint8 class_id);
uint32 GetPlayerClassValue(uint8 class_id);
uint32 GetPlayerClassBit(uint8 class_id);

View File

@ -17,6 +17,7 @@
*/
#include "../common/races.h"
#include "data_verification.h"
const char* GetRaceIDName(uint16 race_id)
{
@ -67,7 +68,7 @@ const char* GetRaceIDName(uint16 race_id)
// return "Froglok";
//case DRAKKIN:
// return "Drakkin";
// RoF2 Race Labels
case RT_ABHORRENT:
return "Abhorrent";
@ -1696,7 +1697,7 @@ bool PlayerAppearance::IsValidBeardColor(uint16 race_id, uint8 gender_id, uint8
{
if (beard_color_value == 0xFF)
return true;
switch (BINDRG(race_id, gender_id)) {
case GNOME_MALE:
if (beard_color_value <= 24)
@ -1783,7 +1784,7 @@ bool PlayerAppearance::IsValidEyeColor(uint16 race_id, uint8 gender_id, uint8 ey
case VAHSHIR_FEMALE:
if (eye_color_value <= 9)
return true;
break;
break;
case TROLL_MALE:
case TROLL_FEMALE:
if (eye_color_value <= 10)
@ -2109,7 +2110,7 @@ bool PlayerAppearance::IsValidTexture(uint16 race_id, uint8 gender_id, uint8 tex
{
if (texture_value == 0xFF)
return true;
if (use_luclin) {
switch (BINDRG(race_id, gender_id)) {
case HUMAN_MALE:
@ -2243,4 +2244,58 @@ const char* GetGenderName(uint32 gender_id) {
gender_name = "Neuter";
}
return gender_name;
}
}
const std::string GetPlayerRaceAbbreviation(uint16 race_id)
{
if (!IsPlayerRace(race_id)) {
return std::string();
}
switch (race_id) {
case RACE_HUMAN_1:
return "HUM";
case RACE_BARBARIAN_2:
return "BAR";
case RACE_ERUDITE_3:
return "ERU";
case RACE_WOOD_ELF_4:
return "ELF";
case RACE_HIGH_ELF_5:
return "HIE";
case RACE_DARK_ELF_6:
return "DEF";
case RACE_HALF_ELF_7:
return "HEF";
case RACE_DWARF_8:
return "DWF";
case RACE_TROLL_9:
return "TRL";
case RACE_OGRE_10:
return "OGR";
case RACE_HALFLING_11:
return "HFL";
case RACE_GNOME_12:
return "GNM";
case RACE_IKSAR_128:
return "IKS";
case RACE_VAH_SHIR_130:
return "VAH";
case RACE_FROGLOK_330:
return "FRG";
case RACE_DRAKKIN_522:
return "DRK";
}
return std::string();
}
bool IsPlayerRace(uint16 race_id) {
return (
EQ::ValueWithin(race_id, RACE_HUMAN_1, RACE_GNOME_12) ||
race_id == RACE_IKSAR_128 ||
race_id == RACE_VAH_SHIR_130 ||
race_id == RACE_FROGLOK_330 ||
race_id == RACE_DRAKKIN_522
);
}

View File

@ -19,6 +19,7 @@
#ifndef RACES_H
#define RACES_H
#include "../common/types.h"
#include <string>
#define MALE 0
#define FEMALE 1
@ -853,6 +854,9 @@ const char* GetRaceIDName(uint16 race_id);
const char* GetPlayerRaceName(uint32 player_race_value);
const char* GetGenderName(uint32 gender_id);
bool IsPlayerRace(uint16 race_id);
const std::string GetPlayerRaceAbbreviation(uint16 race_id);
uint32 GetPlayerRaceValue(uint16 race_id);
uint32 GetPlayerRaceBit(uint16 race_id);

View File

@ -5316,7 +5316,7 @@ void bot_command_view_combos(Client *c, const Seperator *sep)
const uint16 bot_race = static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[1]));
const std::string race_name = GetRaceIDName(bot_race);
if (!Mob::IsPlayerRace(bot_race)) {
if (!IsPlayerRace(bot_race)) {
c->Message(
Chat::White,
fmt::format(
@ -8900,7 +8900,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
return bot_id;
}
if (!Bot::IsValidRaceClassCombo(bot_race, bot_class) && bot_owner->IsPlayerRace(bot_race)) {
if (!Bot::IsValidRaceClassCombo(bot_race, bot_class) && IsPlayerRace(bot_race)) {
const std::string bot_race_name = GetRaceIDName(bot_race);
const std::string bot_class_name = GetClassIDName(bot_class);
const auto view_saylink = Saylink::Silent(

View File

@ -3958,32 +3958,32 @@ void Client::SendFullPopup(
}
void Client::SendWindow(
uint32 PopupID,
uint32 NegativeID,
uint32 Buttons,
const char *ButtonName0,
const char *ButtonName1,
uint32 Duration,
uint32 button_one_id,
uint32 button_two_id,
uint32 button_type,
const char* button_one_text,
const char* button_two_text,
uint32 duration,
int title_type,
Client *target,
const char *Title,
const char *Text,
Mob* target,
const char* title,
const char* text,
...
)
{
va_list argptr;
char buffer[4096];
va_start(argptr, Text);
vsnprintf(buffer, sizeof(buffer), Text, argptr);
va_start(argptr, text);
vsnprintf(buffer, sizeof(buffer), text, argptr);
va_end(argptr);
size_t len = strlen(buffer);
auto app = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct));
OnLevelMessage_Struct *olms = (OnLevelMessage_Struct *) app->pBuffer;
auto app = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct));
auto* olms = (OnLevelMessage_Struct *) app->pBuffer;
if (strlen(Text) > (sizeof(olms->Text) - 1)) {
if (strlen(text) > (sizeof(olms->Text) - 1)) {
safe_delete(app);
return;
}
@ -3996,47 +3996,47 @@ void Client::SendWindow(
case 1: {
char name[64] = "";
strcpy(name, target->GetName());
if (strlen(target->GetLastName()) > 0) {
char last_name[64] = "";
strcpy(last_name, target->GetLastName());
strcat(name, " ");
strcat(name, last_name);
}
strcpy(olms->Title, name);
break;
}
case 2: {
if (target->GuildID()) {
char *guild_name = (char *) guild_mgr.GetGuildName(target->GuildID());
strcpy(olms->Title, guild_name);
}
else {
if (target->IsClient() && target->CastToClient()->GuildID()) {
auto guild_name = guild_mgr.GetGuildName(target->CastToClient()->GuildID());
strn0cpy(olms->Title, guild_name, sizeof(olms->Title));
} else {
strcpy(olms->Title, "No Guild");
}
break;
}
default: {
strcpy(olms->Title, Title);
strcpy(olms->Title, title);
break;
}
}
memcpy(olms->Text, buffer, len + 1);
olms->Buttons = Buttons;
olms->Buttons = button_type;
sprintf(olms->ButtonName0, "%s", ButtonName0);
sprintf(olms->ButtonName1, "%s", ButtonName1);
strn0cpy(olms->ButtonName0, button_one_text, sizeof(olms->ButtonName0));
strn0cpy(olms->ButtonName1, button_two_text, sizeof(olms->ButtonName1));
if (Duration > 0) {
olms->Duration = Duration * 1000;
}
else {
olms->Duration = 0xffffffff;
if (duration > 0) {
olms->Duration = duration * 1000;
} else {
olms->Duration = UINT32_MAX;
}
olms->PopupID = PopupID;
olms->NegativeID = NegativeID;
olms->PopupID = button_one_id;
olms->NegativeID = button_two_id;
FastQueuePacket(&app);
}
@ -6365,544 +6365,6 @@ void Client::RemoveFromInstance(uint16 instance_id)
database.RemoveClientFromInstance(instance_id, CharacterID());
}
void Client::SendStatsWindow(Client* client, bool use_window)
{
// Define the types of page breaks we need
std::string indP = "&nbsp;";
std::string indS = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
std::string indM = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
std::string indL = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
std::string div = " | ";
std::string color_red = "<c \"#993333\">";
std::string color_blue = "<c \"#9999FF\">";
std::string color_green = "<c \"#33FF99\">";
std::string bright_green = "<c \"#7CFC00\">";
std::string bright_red = "<c \"#FF0000\">";
std::string heroic_color = "<c \"#d6b228\"> +";
// Set Class
std::string class_Name = itoa(GetClass());
std::string class_List[] = { "WAR", "CLR", "PAL", "RNG", "SHD", "DRU", "MNK", "BRD", "ROG", "SHM", "NEC", "WIZ", "MAG", "ENC", "BST", "BER" };
if (GetClass() < 17 && GetClass() > 0) {
class_Name = class_List[GetClass() - 1];
}
// Race
std::string race_name = GetRaceIDName(GetRace());
/*##########################################################
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
H/M/E String
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
##########################################################*/
std::string HME_row = "";
//Loop Variables
/*===========================*/
std::string cur_field = "";
std::string total_field = "";
std::string cur_name = "";
std::string cur_spacing = "";
std::string cur_color = "";
int hme_rows = 3; // Rows in display
int max_HME_value_len = 9; // 9 digits in the displayed value
for(int hme_row_counter = 0; hme_row_counter < hme_rows; hme_row_counter++)
{
switch(hme_row_counter) {
case 0: {
cur_name = " H: ";
cur_field = itoa(GetHP());
total_field = itoa(GetMaxHP());
break;
}
case 1: {
if(CalcMaxMana() > 0) {
cur_name = " M: ";
cur_field = itoa(GetMana());
total_field = itoa(GetMaxMana());
}
else { continue; }
break;
}
case 2: {
cur_name = " E: ";
cur_field = itoa(GetEndurance());
total_field = itoa(GetMaxEndurance());
break;
}
default: { break; }
}
if(cur_field.compare(total_field) == 0) { cur_color = bright_green; }
else { cur_color = bright_red; }
cur_spacing.clear();
for(int a = cur_field.size(); a < max_HME_value_len; a++) { cur_spacing += " ."; }
HME_row += indM + cur_name + cur_spacing + cur_color + cur_field + "</c> / " + total_field + "<br>";
}
/*##########################################################
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Regen String
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
##########################################################*/
std::string regen_string;
//Loop Variables
/*===========================*/
std::string regen_row_header = "";
std::string regen_row_color = "";
std::string base_regen_field = "";
std::string base_regen_spacing = "";
std::string item_regen_field = "";
std::string item_regen_spacing = "";
std::string cap_regen_field = "";
std::string cap_regen_spacing = "";
std::string spell_regen_field = "";
std::string spell_regen_spacing = "";
std::string aa_regen_field = "";
std::string aa_regen_spacing = "";
std::string total_regen_field = "";
int regen_rows = 3; // Number of rows
int max_regen_value_len = 5; // 5 digits in the displayed value(larger values will not get cut off, this is just a baseline)
for(int regen_row_counter = 0; regen_row_counter < regen_rows; regen_row_counter++)
{
switch(regen_row_counter)
{
case 0: {
regen_row_header = "H: ";
regen_row_color = color_red;
base_regen_field = itoa(LevelRegen());
item_regen_field = itoa(itembonuses.HPRegen + itembonuses.heroic_hp_regen);
cap_regen_field = itoa(CalcHPRegenCap());
spell_regen_field = itoa(spellbonuses.HPRegen);
aa_regen_field = itoa(aabonuses.HPRegen);
total_regen_field = itoa(CalcHPRegen(true));
break;
}
case 1: {
if(GetMaxMana() > 0) {
regen_row_header = "M: ";
regen_row_color = color_blue;
base_regen_field = itoa(CalcBaseManaRegen());
int32 heroic_mana_regen = itembonuses.heroic_mana_regen;
item_regen_field = itoa(itembonuses.ManaRegen + heroic_mana_regen);
cap_regen_field = itoa(CalcManaRegenCap());
spell_regen_field = itoa(spellbonuses.ManaRegen);
aa_regen_field = itoa(aabonuses.ManaRegen);
total_regen_field = itoa(CalcManaRegen(true));
}
else { continue; }
break;
}
case 2: {
regen_row_header = "E: ";
regen_row_color = color_green;
base_regen_field = itoa(((GetLevel() * 4 / 10) + 2));
item_regen_field = itoa(itembonuses.EnduranceRegen + itembonuses.heroic_end_regen);
cap_regen_field = itoa(CalcEnduranceRegenCap());
spell_regen_field = itoa(spellbonuses.EnduranceRegen);
aa_regen_field = itoa(aabonuses.EnduranceRegen);
total_regen_field = itoa(CalcEnduranceRegen(true));
break;
}
default: { break; }
}
base_regen_spacing.clear();
item_regen_spacing.clear();
cap_regen_spacing.clear();
spell_regen_spacing.clear();
aa_regen_spacing.clear();
for(int b = base_regen_field.size(); b < max_regen_value_len; b++) { base_regen_spacing += " ."; }
for(int b = item_regen_field.size(); b < max_regen_value_len; b++) { item_regen_spacing += " ."; }
for(int b = cap_regen_field.size(); b < max_regen_value_len; b++) { cap_regen_spacing += " ."; }
for(int b = spell_regen_field.size(); b < max_regen_value_len; b++) { spell_regen_spacing += " ."; }
for(int b = aa_regen_field.size(); b < max_regen_value_len; b++) { aa_regen_spacing += " ."; }
regen_string += indS + regen_row_color + regen_row_header + base_regen_spacing + base_regen_field;
regen_string += div + item_regen_spacing + item_regen_field + " (" + cap_regen_field;
regen_string += ") " + cap_regen_spacing + div + spell_regen_spacing + spell_regen_field;
regen_string += div + aa_regen_spacing + aa_regen_field + div + total_regen_field + "</c><br>";
}
/*##########################################################
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Stat String
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
##########################################################*/
std::string stat_field = "";
//Loop Variables
/*===========================*/
//first field(stat)
std::string a_stat = "";;
std::string a_stat_name = "";
std::string a_stat_spacing = "";
//second field(heroic stat)
std::string h_stat = "";
std::string h_stat_spacing = "";
//third field(resist)
std::string a_resist = "";
std::string a_resist_name = "";
std::string a_resist_spacing = "";
//fourth field(heroic resist)
std::string h_resist_field = "";
int stat_rows = 7; // Number of rows
int max_stat_value_len = 3; // 3 digits in the displayed value
for(int stat_row_counter = 0; stat_row_counter < stat_rows; stat_row_counter++)
{
switch(stat_row_counter) {
case 0: {
a_stat_name = " STR: ";
a_resist_name = "MR: ";
a_stat = itoa(GetSTR());
h_stat = itoa(GetHeroicSTR());
a_resist = itoa(GetMR());
h_resist_field = itoa(GetHeroicMR());
break;
}
case 1: {
a_stat_name = " STA: ";
a_resist_name = "CR: ";
a_stat = itoa(GetSTA());
h_stat = itoa(GetHeroicSTA());
a_resist = itoa(GetCR());
h_resist_field = itoa(GetHeroicCR());
break;
}
case 2: {
a_stat_name = " AGI : ";
a_resist_name = "FR: ";
a_stat = itoa(GetAGI());
h_stat = itoa(GetHeroicAGI());
a_resist = itoa(GetFR());
h_resist_field = itoa(GetHeroicFR());
break;
}
case 3: {
a_stat_name = " DEX: ";
a_resist_name = "PR: ";
a_stat = itoa(GetDEX());
h_stat = itoa(GetHeroicDEX());
a_resist = itoa(GetPR());
h_resist_field = itoa(GetHeroicPR());
break;
}
case 4: {
a_stat_name = " INT : ";
a_resist_name = "DR: ";
a_stat = itoa(GetINT());
h_stat = itoa(GetHeroicINT());
a_resist = itoa(GetDR());
h_resist_field = itoa(GetHeroicDR());
break;
}
case 5: {
a_stat_name = " WIS: ";
a_resist_name = "Cp: ";
a_stat = itoa(GetWIS());
h_stat = itoa(GetHeroicWIS());
a_resist = itoa(GetCorrup());
h_resist_field = itoa(GetHeroicCorrup());
break;
}
case 6: {
a_stat_name = " CHA: ";
a_resist_name = "PhR: "; // Not implemented for clients yet
a_stat = itoa(GetCHA());
h_stat = itoa(GetHeroicCHA());
a_resist = itoa(GetPhR());
h_resist_field = itoa(GetHeroicPhR());
break;
}
default: { break; }
}
a_stat_spacing.clear();
h_stat_spacing.clear();
a_resist_spacing.clear();
for(int a = a_stat.size(); a < max_stat_value_len; a++) { a_stat_spacing += " . "; }
for(int h = h_stat.size(); h < 20; h++) { h_stat_spacing += " . "; }
for(int h = a_resist.size(); h < max_stat_value_len; h++) { a_resist_spacing += " . "; }
stat_field += indP + a_stat_name + a_stat_spacing + a_stat + heroic_color + h_stat + "</c>";
stat_field += h_stat_spacing + a_resist_name + a_resist_spacing + a_resist + heroic_color + h_resist_field + "</c>";
if(stat_row_counter < 6) {
stat_field += "<br>";
}
}
/*##########################################################
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Mod2 String
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
##########################################################*/
std::string mod2_field = "";
//Loop Variables
/*===========================*/
std::string mod2a = "";
std::string mod2a_name = "";
std::string mod2a_spacing = "";
std::string mod2a_cap = "";
std::string mod_row_spacing = "";
std::string mod2b = "";
std::string mod2b_name = "";
std::string mod2b_spacing = "";
std::string mod2b_cap = "";
int mod2a_space_count;
int mod2b_space_count;
int mod2_rows = 4;
int max_mod2_value_len = 3; // 3 digits in the displayed value
for(int mod2_row_counter = 0; mod2_row_counter < mod2_rows; mod2_row_counter++)
{
switch (mod2_row_counter)
{
case 0: {
mod2a_name = "Avoidance: ";
mod2b_name = "Combat Effects: ";
mod2a = itoa(GetAvoidance());
mod2a_cap = itoa(RuleI(Character, ItemAvoidanceCap));
mod2b = itoa(GetCombatEffects());
mod2b_cap = itoa(RuleI(Character, ItemCombatEffectsCap));
mod2a_space_count = 2;
mod2b_space_count = 0;
break;
}
case 1: {
mod2a_name = "Accuracy: ";
mod2b_name = "Strike Through: ";
mod2a = itoa(GetAccuracy());
mod2a_cap = itoa(RuleI(Character, ItemAccuracyCap));
mod2b = itoa(GetStrikeThrough());
mod2b_cap = itoa(RuleI(Character, ItemStrikethroughCap));
mod2a_space_count = 3;
mod2b_space_count = 1;
break;
}
case 2: {
mod2a_name = "Shielding: ";
mod2b_name = "Spell Shielding: ";
mod2a = itoa(GetShielding());
mod2a_cap = itoa(RuleI(Character, ItemShieldingCap));
mod2b = itoa(GetSpellShield());
mod2b_cap = itoa(RuleI(Character, ItemSpellShieldingCap));
mod2a_space_count = 2;
mod2b_space_count = 1;
break;
}
case 3: {
mod2a_name = "Stun Resist: ";
mod2b_name = "DoT Shielding: ";
mod2a = itoa(GetStunResist());
mod2a_cap = itoa(RuleI(Character, ItemStunResistCap));
mod2b = itoa(GetDoTShield());
mod2b_cap = itoa(RuleI(Character, ItemDoTShieldingCap));
mod2a_space_count = 0;
mod2b_space_count = 2;
break;
}
}
mod2a_spacing.clear();
mod_row_spacing.clear();
mod2b_spacing.clear();
for(int a = mod2a.size(); a < (max_mod2_value_len + mod2a_space_count); a++) { mod2a_spacing += " . "; }
for(int a = mod2a_cap.size(); a < 6 ; a++) { mod_row_spacing += " . "; }
for(int a = mod2b.size(); a < (max_mod2_value_len + mod2b_space_count); a++) { mod2b_spacing += " . "; }
mod2_field += indP + mod2a_name + mod2a_spacing + mod2a + " / " + mod2a_cap + mod_row_spacing;
mod2_field += mod2b_name + mod2b_spacing + mod2b + " / " + mod2b_cap + "<br>";
}
uint32 rune_number = 0;
uint32 magic_rune_number = 0;
uint32 buff_count = GetMaxTotalSlots();
for (int i=0; i < buff_count; i++) {
if (IsValidSpell(buffs[i].spellid)) {
if (buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; }
if (buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; }
}
}
int shield_ac = 0;
GetRawACNoShield(shield_ac);
std::string skill_list[] = {
"1H Blunt","1H Slashing","2H Blunt","2H Slashing","Abjuration",
"Alteration","Apply Poison","Archery","Backstab","Bind Wound",
"Bash","Block","Brass Instruments","Channeling","Conjuration",
"Defense","Disarm","Disarm Traps","Divination","Dodge",
"Double Attack","Dragon Punch","Dual Wield","Eagle Strike","Evocation",
"Feign Death","Flying Kick","Forage","Hand To Hand","Hide",
"Kick","Meditate","Mend","Offense","Parry",
"Pick Lock","1H Piercing","Riposte","Round Kick","Safe Fall",
"Sense Heading","Singing","Sneak","Specialize Abjuration","Specialize Alteration",
"Specialize Conjuration","Specialize Divination","Specialize Evocation","Pick Pockets","Stringed Instruments",
"Swimming","Throwing","Tiger Claw","Tracking","Wind Instruments",
"Fishing","Make Poison","Tinkering","Research","Alchemy",
"Baking","Tailoring","Sense Traps","Blacksmithing","Fletching",
"Brewing","Alcohol_Tolerance","Begging","Jewelry Making","Pottery",
"Percussion Instruments","Intimidation","Berserking","Taunt","Frenzy",
"Remove Traps","Triple Attack","2H Piercing"
};
std::string skill_mods = "";
for (int j = 0; j <= EQ::skills::HIGHEST_SKILL; j++) {
if(itembonuses.skillmod[j] > 0)
skill_mods += indP + skill_list[j] + " : +" + itoa(itembonuses.skillmod[j]) + "%<br>";
else if(itembonuses.skillmod[j] < 0)
skill_mods += indP + skill_list[j] + " : -" + itoa(itembonuses.skillmod[j]) + "%<br>";
}
std::string skill_dmgs = "";
for (int j = 0; j <= EQ::skills::HIGHEST_SKILL; j++) {
if((itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) > 0)
skill_dmgs += indP + skill_list[j] + " : +" + itoa(itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) + "<br>";
else if((itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) < 0)
skill_dmgs += indP + skill_list[j] + " : -" + itoa(itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) + "<br>";
}
std::string faction_item_string = "";
char faction_buf[256];
for (auto iter = item_faction_bonuses.begin(); iter != item_faction_bonuses.end(); ++iter) {
memset(&faction_buf, 0, sizeof(faction_buf));
if(!content_db.GetFactionName((int)((*iter).first), faction_buf, sizeof(faction_buf)))
strcpy(faction_buf, "Not in DB");
if((*iter).second > 0) {
faction_item_string += indP + faction_buf + " : +" + itoa((*iter).second) + "<br>";
}
else if((*iter).second < 0) {
faction_item_string += indP + faction_buf + " : -" + itoa((*iter).second) + "<br>";
}
}
std::string bard_info = "";
if(GetClass() == BARD) {
bard_info = indP + "Singing: " + itoa(GetSingMod()) + "<br>" +
indP + "Brass: " + itoa(GetBrassMod()) + "<br>" +
indP + "String: " + itoa(GetStringMod()) + "<br>" +
indP + "Percussion: " + itoa(GetPercMod()) + "<br>" +
indP + "Wind: " + itoa(GetWindMod()) + "<br>";
}
EQ::skills::SkillType skill = EQ::skills::SkillHandtoHand;
auto *inst = GetInv().GetItem(EQ::invslot::slotPrimary);
if (inst && inst->IsClassCommon()) {
switch (inst->GetItem()->ItemType) {
case EQ::item::ItemType1HSlash:
skill = EQ::skills::Skill1HSlashing;
break;
case EQ::item::ItemType2HSlash:
skill = EQ::skills::Skill2HSlashing;
break;
case EQ::item::ItemType1HPiercing:
skill = EQ::skills::Skill1HPiercing;
break;
case EQ::item::ItemType1HBlunt:
skill = EQ::skills::Skill1HBlunt;
break;
case EQ::item::ItemType2HBlunt:
skill = EQ::skills::Skill2HBlunt;
break;
case EQ::item::ItemType2HPiercing:
if (ClientVersion() < EQ::versions::ClientVersion::RoF2)
skill = EQ::skills::Skill1HPiercing;
else
skill = EQ::skills::Skill2HPiercing;
break;
default:
break;
}
}
std::ostringstream final_string;
final_string <<
/* C/L/R */ indP << "Class: " << class_Name << indS << "Level: " << static_cast<int>(GetLevel()) << indS << "Race: " << race_name << "<br>" <<
/* Runes */ indP << "Rune: " << rune_number << indL << indS << "Spell Rune: " << magic_rune_number << "<br>" <<
/* HP/M/E */ HME_row <<
/* DS */ indP << "DS: " << (itembonuses.DamageShield + spellbonuses.DamageShield*-1) << " (Spell: " << (spellbonuses.DamageShield*-1) << " + Item: " << itembonuses.DamageShield << " / " << RuleI(Character, ItemDamageShieldCap) << ")<br>" <<
/* Atk */ indP << "<c \"#CCFF00\">tohit: " << compute_tohit(skill) << " / " << GetTotalToHit(skill, 0) << "</c><br>" <<
/* Atk2 */ indP << "- Offense: " << offense(skill) << " | Item: " << itembonuses.ATK << " (" << RuleI(Character, ItemATKCap) << ")~Used: " << (itembonuses.ATK * 1.342) << " | Spell: " << spellbonuses.ATK << "<br>" <<
/* AC */ indP << "<c \"#CCFF00\">mitigation AC: " << GetMitigationAC() << "</c><br>" <<
/* AC2 */ indP << "- defense: " << compute_defense() << " / " << GetTotalDefense() << " | Spell: " << spellbonuses.AC << " | Shield: " << shield_ac << "<br>" <<
/* Haste */ indP << "<c \"#CCFF00\">Haste: " << GetHaste() << "</c><br>" <<
/* Haste2 */ indP << " - Item: " << itembonuses.haste << " + Spell: " << (spellbonuses.haste + spellbonuses.hastetype2) << " (Cap: " << RuleI(Character, HasteCap) << ") | Over: " << (spellbonuses.hastetype3 + ExtraHaste) << "<br>" <<
/* RunSpeed*/ indP << "<c \"#CCFF00\">Runspeed: " << GetRunspeed() << "</c><br>" <<
/* RegenLbl */ indL << indS << "Regen<br>" << indS << indP << indP << " Base | Items (Cap) " << indP << " | Spell | A.A.s | Total<br>" <<
/* Regen */ regen_string << "<br>" <<
/* Stats */ stat_field << "<br><br>" <<
/* Mod2s */ mod2_field << "<br>" <<
/* HealAmt */ indP << "Heal Amount: " << GetHealAmt() << " / " << RuleI(Character, ItemHealAmtCap) << "<br>" <<
/* SpellDmg*/ indP << "Spell Dmg: " << GetSpellDmg() << " / " << RuleI(Character, ItemSpellDmgCap) << "<br>" <<
/* Clair */ indP << "Clairvoyance: " << GetClair() << " / " << RuleI(Character, ItemClairvoyanceCap) << "<br>" <<
/* DSMit */ indP << "Dmg Shld Mit: " << GetDSMit() << " / " << RuleI(Character, ItemDSMitigationCap) << "<br><br>";
if(GetClass() == BARD)
final_string << bard_info << "<br>";
if(skill_mods.size() > 0)
final_string << skill_mods << "<br>";
if(skill_dmgs.size() > 0)
final_string << skill_dmgs << "<br>";
if(faction_item_string.size() > 0)
final_string << faction_item_string;
std::string final_stats = final_string.str();
if(use_window) {
if(final_stats.size() < 4096)
{
uint32 Buttons = (client->ClientVersion() < EQ::versions::ClientVersion::SoD) ? 0 : 1;
client->SendWindow(0, POPUPID_UPDATE_SHOWSTATSWINDOW, Buttons, "Cancel", "Update", 0, 1, this, "", "%s", final_stats.c_str());
goto Extra_Info;
}
else {
client->Message(Chat::Yellow, "The window has exceeded its character limit, displaying stats to chat window:");
}
}
client->Message(Chat::Yellow, "~~~~~ %s %s ~~~~~", GetCleanName(), GetLastName());
client->Message(Chat::White, " Level: %i Class: %i Race: %i DS: %i/%i Size: %1.1f Weight: %.1f/%d ", GetLevel(), GetClass(), GetRace(), GetDS(), RuleI(Character, ItemDamageShieldCap), GetSize(), (float)CalcCurrentWeight() / 10.0f, GetSTR());
client->Message(Chat::White, " HP: %i/%i HP Regen: %i/%i",GetHP(), GetMaxHP(), CalcHPRegen(), CalcHPRegenCap());
client->Message(Chat::White, " compute_tohit: %i TotalToHit: %i", compute_tohit(skill), GetTotalToHit(skill, 0));
client->Message(Chat::White, " compute_defense: %i TotalDefense: %i", compute_defense(), GetTotalDefense());
client->Message(Chat::White, " offense: %i mitigation ac: %i", offense(skill), GetMitigationAC());
client->Message(Chat::White, " AFK: %i LFG: %i Anon: %i PVP: %i GM: %i Flymode: %i GMSpeed: %i Hideme: %i GMInvul: %d LD: %i ClientVersion: %i TellsOff: %i", AFK, LFG, GetAnon(), GetPVP(), GetGM(), flymode, GetGMSpeed(), GetHideMe(), GetGMInvul(), IsLD(), ClientVersionBit(), tellsoff);
if(CalcMaxMana() > 0)
client->Message(Chat::White, " Mana: %i/%i Mana Regen: %i/%i", GetMana(), GetMaxMana(), CalcManaRegen(), CalcManaRegenCap());
client->Message(Chat::White, " End.: %i/%i End. Regen: %i/%i",GetEndurance(), GetMaxEndurance(), CalcEnduranceRegen(), CalcEnduranceRegenCap());
client->Message(Chat::White, " ATK: %i Worn/Spell ATK %i/%i Server Side ATK: %i", GetTotalATK(), RuleI(Character, ItemATKCap), GetATKBonus(), GetATK());
client->Message(Chat::White, " Haste: %i / %i (Item: %i + Spell: %i + Over: %i) Run speed: %i", GetHaste(), RuleI(Character, HasteCap), itembonuses.haste, spellbonuses.haste + spellbonuses.hastetype2, spellbonuses.hastetype3 + ExtraHaste, GetRunspeed());
client->Message(Chat::White, " STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA());
client->Message(Chat::White, " hSTR: %i hSTA: %i hDEX: %i hAGI: %i hINT: %i hWIS: %i hCHA: %i", GetHeroicSTR(), GetHeroicSTA(), GetHeroicDEX(), GetHeroicAGI(), GetHeroicINT(), GetHeroicWIS(), GetHeroicCHA());
client->Message(Chat::White, " MR: %i PR: %i FR: %i CR: %i DR: %i Corruption: %i PhR: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR(), GetCorrup(), GetPhR());
client->Message(Chat::White, " hMR: %i hPR: %i hFR: %i hCR: %i hDR: %i hCorruption: %i", GetHeroicMR(), GetHeroicPR(), GetHeroicFR(), GetHeroicCR(), GetHeroicDR(), GetHeroicCorrup());
client->Message(Chat::White, " Shielding: %i Spell Shield: %i DoT Shielding: %i Stun Resist: %i Strikethrough: %i Avoidance: %i Accuracy: %i Combat Effects: %i", GetShielding(), GetSpellShield(), GetDoTShield(), GetStunResist(), GetStrikeThrough(), GetAvoidance(), GetAccuracy(), GetCombatEffects());
client->Message(Chat::White, " Heal Amt.: %i Spell Dmg.: %i Clairvoyance: %i DS Mitigation: %i", GetHealAmt(), GetSpellDmg(), GetClair(), GetDSMit());
if(GetClass() == BARD)
client->Message(Chat::White, " Singing: %i Brass: %i String: %i Percussion: %i Wind: %i", GetSingMod(), GetBrassMod(), GetStringMod(), GetPercMod(), GetWindMod());
Extra_Info:
client->Message(Chat::White, " BaseRace: %i Gender: %i BaseGender: %i Texture: %i HelmTexture: %i", GetBaseRace(), GetGender(), GetBaseGender(), GetTexture(), GetHelmTexture());
if (client->Admin() >= AccountStatus::GMAdmin) {
client->Message(Chat::White, " CharID: %i EntityID: %i PetID: %i OwnerID: %i AIControlled: %i Targetted: %i", CharacterID(), GetID(), GetPetID(), GetOwnerID(), IsAIControlled(), targeted);
}
}
void Client::SendAltCurrencies() {
if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
uint32 count = zone->AlternateCurrencies.size();

View File

@ -772,7 +772,6 @@ public:
void SetLanguageSkill(int langid, int value);
void SetHoTT(uint32 mobid);
void ShowSkillsWindow();
void SendStatsWindow(Client* client, bool use_window);
uint16 MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const;
inline uint16 MaxSkill(EQ::skills::SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
@ -1095,7 +1094,7 @@ public:
uint16 GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid, uint16 maxSkill);
void SendPopupToClient(const char *Title, const char *Text, uint32 PopupID = 0, uint32 Buttons = 0, uint32 Duration = 0);
void SendFullPopup(const char *Title, const char *Text, uint32 PopupID = 0, uint32 NegativeID = 0, uint32 Buttons = 0, uint32 Duration = 0, const char *ButtonName0 = 0, const char *ButtonName1 = 0, uint32 SoundControls = 0);
void SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const char *ButtonName0, const char *ButtonName1, uint32 Duration, int title_type, Client* target, const char *Title, const char *Text, ...);
void SendWindow(uint32 button_one_id, uint32 button_two_id, uint32 button_type, const char* button_one_text, const char* button_two_text, uint32 duration, int title_type, Mob* target, const char* title, const char* text, ...);
bool PendingTranslocate;
time_t TranslocateTime;
bool PendingSacrifice;

View File

@ -649,7 +649,7 @@ int64 Client::CalcBaseMana()
int64 Client::CalcBaseManaRegen()
{
uint8 clevel = GetLevel();
int32 regen = 0;
int64 regen = 0;
if (IsSitting() || (GetHorseId() != 0)) {
if (HasSkill(EQ::skills::SkillMeditate)) {
regen = (((GetSkill(EQ::skills::SkillMeditate) / 10) + (clevel - (clevel / 4))) / 4) + 4;
@ -666,7 +666,7 @@ int64 Client::CalcBaseManaRegen()
int64 Client::CalcManaRegen(bool bCombat)
{
int regen = 0;
int64 regen = 0;
auto level = GetLevel();
// so the new formulas break down with older skill caps where you don't have the skill until 4 or 8
// so for servers that want to use the old skill progression they can set this rule so they
@ -688,9 +688,9 @@ int64 Client::CalcManaRegen(bool bCombat)
}
}
if (old)
regen = std::max(regen, 2);
regen = std::max(regen, static_cast<int64>(2));
} else if (old) {
regen = std::max(regen, 1);
regen = std::max(regen, static_cast<int64>(1));
}
}

View File

@ -11462,10 +11462,9 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app)
break;
case POPUPID_UPDATE_SHOWSTATSWINDOW:
if (GetTarget() && GetTarget()->IsClient()) {
GetTarget()->CastToClient()->SendStatsWindow(this, true);
}
else {
if (GetTarget() && GetTarget()->IsOfClientBot()) {
GetTarget()->SendStatsWindow(this, true);
} else {
SendStatsWindow(this, true);
}
return;

View File

@ -13,7 +13,7 @@ void FindClass(Client *c, const Seperator *sep)
class_id,
class_name,
(
c->IsPlayerClass(class_id) ?
IsPlayerClass(class_id) ?
fmt::format(
" ({})",
Strings::Commify(GetPlayerClassBit(class_id))
@ -55,7 +55,7 @@ void FindClass(Client *c, const Seperator *sep)
class_id,
class_name,
(
c->IsPlayerClass(class_id) ?
IsPlayerClass(class_id) ?
fmt::format(
" | ({})",
Strings::Commify(GetPlayerClassBit(class_id))

View File

@ -13,7 +13,7 @@ void FindRace(Client *c, const Seperator *sep)
race_id,
race_name,
(
c->IsPlayerRace(race_id) ?
IsPlayerRace(race_id) ?
fmt::format(
" ({})",
Strings::Commify(GetPlayerRaceBit(race_id))
@ -55,7 +55,7 @@ void FindRace(Client *c, const Seperator *sep)
race_id,
race_name,
(
c->IsPlayerRace(race_id) ?
IsPlayerRace(race_id) ?
fmt::format(
" ({})",
Strings::Commify(GetPlayerRaceBit(race_id))

View File

@ -2,16 +2,19 @@
void command_mystats(Client *c, const Seperator *sep)
{
if (c->GetTarget() && c->GetPet()) {
if (c->GetTarget()->IsPet() && c->GetTarget() == c->GetPet()) {
c->GetTarget()->ShowStats(c);
}
else {
c->ShowStats(c);
}
Mob* t = c;
if (c->GetTarget()) {
t = c->GetTarget();
}
else {
c->ShowStats(c);
if (
(t->IsPet() && t == c->GetPet()) ||
(t->IsBot() && t->CastToBot()->GetOwner() && t->CastToBot()->GetOwner() == c)
) {
t->ShowStats(c);
return;
}
c->ShowStats(c);
}

View File

@ -2,11 +2,11 @@
void command_showstats(Client *c, const Seperator *sep)
{
if (c->GetTarget() != 0) {
c->GetTarget()->ShowStats(c);
}
else {
c->ShowStats(c);
Mob* t = c;
if (c->GetTarget()) {
t = c->GetTarget();
}
t->ShowStats(c);
}

View File

@ -20,7 +20,7 @@ void command_texture(Client *c, const Seperator *sep)
target = c->GetTarget();
}
if (Mob::IsPlayerRace(target->GetModel())) { // Player Races Wear Armor, so Wearchange is sent instead
if (IsPlayerRace(target->GetModel())) { // Player Races Wear Armor, so Wearchange is sent instead
for (
int texture_slot = EQ::textures::textureBegin;
texture_slot <= EQ::textures::LastTintableTexture;
@ -44,7 +44,7 @@ void command_texture(Client *c, const Seperator *sep)
c->GetTargetDescription(target, TargetDescriptionType::UCSelf),
texture,
(
Mob::IsPlayerRace(target->GetModel()) ?
IsPlayerRace(target->GetModel()) ?
"" :
fmt::format(
" Helmet Texture: {}",

File diff suppressed because it is too large Load Diff

View File

@ -801,8 +801,6 @@ public:
//Util
static uint32 RandomTimer(int min, int max);
static uint8 GetDefaultGender(uint16 in_race, uint8 in_gender = 0xFF);
static bool IsPlayerClass(uint16 in_class);
static bool IsPlayerRace(uint16 in_race);
EQ::skills::SkillType GetSkillByItemType(int ItemType);
uint8 GetItemTypeBySkill(EQ::skills::SkillType skill);
virtual void MakePet(uint16 spell_id, const char* pettype, const char *petname = nullptr);
@ -811,6 +809,7 @@ public:
char GetCasterClass() const;
uint8 GetArchetype() const;
void SetZone(uint32 zone_id, uint32 instance_id);
void SendStatsWindow(Client* c, bool use_window);
void ShowStats(Client* client);
void ShowBuffs(Client* c);
bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, bool lookForAftArc = true);