diff --git a/common/features.h b/common/features.h index 38f238617..df9d1353a 100644 --- a/common/features.h +++ b/common/features.h @@ -272,10 +272,6 @@ enum { commandInvSnapshot = 150 //ability to clear/restore snapshots }; -//default states for logging flag on NPCs and clients (having NPCs on by default is prolly a bad idea) -#define CLIENT_DEFAULT_LOGGING_ENABLED true -#define NPC_DEFAULT_LOGGING_ENABLED false - // This is the item ID we use for say links, we use the max that fits in 5 ASCII chars #define SAYLINK_ITEM_ID 0xFFFFF diff --git a/common/rulesys.cpp b/common/rulesys.cpp index 3ce748fb9..61bb72357 100644 --- a/common/rulesys.cpp +++ b/common/rulesys.cpp @@ -382,15 +382,18 @@ bool RuleManager::ListRulesets(Database *database, std::map &i return true; } -int32 RuleManager::GetIntRule(RuleManager::IntType t) const{ - return(m_RuleIntValues[t]); +int32 RuleManager::GetIntRule(RuleManager::IntType t) const +{ + return (m_RuleIntValues[t]); } -float RuleManager::GetRealRule(RuleManager::RealType t) const{ - return(m_RuleRealValues[t]); +float RuleManager::GetRealRule(RuleManager::RealType t) const +{ + return (m_RuleRealValues[t]); } -bool RuleManager::GetBoolRule(RuleManager::BoolType t) const{ +bool RuleManager::GetBoolRule(RuleManager::BoolType t) const +{ return (m_RuleBoolValues[t] == 1); } diff --git a/common/ruletypes.h b/common/ruletypes.h index 16e85805d..a40c2c5bc 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -508,6 +508,7 @@ RULE_BOOL(Combat, UseRevampHandToHand, false) // use h2h revamped dmg/delays I b RULE_BOOL(Combat, ClassicMasterWu, false) // classic master wu uses a random special, modern doesn't RULE_INT(Combat, LevelToStopDamageCaps, 0) // 1 will effectively disable them, 20 should give basically same results as old incorrect system RULE_BOOL(Combat, ClassicNPCBackstab, false) // true disables npc facestab - npcs get normal attack if not behind +RULE_BOOL(Combat, UseNPCDamageClassLevelMods, true) // Uses GetClassLevelDamageMod calc in npc_scale_manager RULE_CATEGORY_END() RULE_CATEGORY(NPC) diff --git a/common/say_link.cpp b/common/say_link.cpp index a215bdc46..4b8ddc009 100644 --- a/common/say_link.cpp +++ b/common/say_link.cpp @@ -26,30 +26,31 @@ #include "../zone/zonedb.h" -bool EQEmu::saylink::DegenerateLinkBody(SayLinkBody_Struct& say_link_body_struct, const std::string& say_link_body) +bool EQEmu::saylink::DegenerateLinkBody(SayLinkBody_Struct &say_link_body_struct, const std::string &say_link_body) { memset(&say_link_body_struct, 0, sizeof(say_link_body_struct)); - if (say_link_body.length() != EQEmu::constants::SAY_LINK_BODY_SIZE) + if (say_link_body.length() != EQEmu::constants::SAY_LINK_BODY_SIZE) { return false; + } - say_link_body_struct.action_id = (uint8)strtol(say_link_body.substr(0, 1).c_str(), nullptr, 16); - say_link_body_struct.item_id = (uint32)strtol(say_link_body.substr(1, 5).c_str(), nullptr, 16); - say_link_body_struct.augment_1 = (uint32)strtol(say_link_body.substr(6, 5).c_str(), nullptr, 16); - say_link_body_struct.augment_2 = (uint32)strtol(say_link_body.substr(11, 5).c_str(), nullptr, 16); - say_link_body_struct.augment_3 = (uint32)strtol(say_link_body.substr(16, 5).c_str(), nullptr, 16); - say_link_body_struct.augment_4 = (uint32)strtol(say_link_body.substr(21, 5).c_str(), nullptr, 16); - say_link_body_struct.augment_5 = (uint32)strtol(say_link_body.substr(26, 5).c_str(), nullptr, 16); - say_link_body_struct.augment_6 = (uint32)strtol(say_link_body.substr(31, 5).c_str(), nullptr, 16); - say_link_body_struct.is_evolving = (uint8)strtol(say_link_body.substr(36, 1).c_str(), nullptr, 16); - say_link_body_struct.evolve_group = (uint32)strtol(say_link_body.substr(37, 4).c_str(), nullptr, 16); - say_link_body_struct.evolve_level = (uint8)strtol(say_link_body.substr(41, 2).c_str(), nullptr, 16); - say_link_body_struct.ornament_icon = (uint32)strtol(say_link_body.substr(43, 5).c_str(), nullptr, 16); - say_link_body_struct.hash = (uint32)strtol(say_link_body.substr(48, 8).c_str(), nullptr, 16); + say_link_body_struct.action_id = (uint8) strtol(say_link_body.substr(0, 1).c_str(), nullptr, 16); + say_link_body_struct.item_id = (uint32) strtol(say_link_body.substr(1, 5).c_str(), nullptr, 16); + say_link_body_struct.augment_1 = (uint32) strtol(say_link_body.substr(6, 5).c_str(), nullptr, 16); + say_link_body_struct.augment_2 = (uint32) strtol(say_link_body.substr(11, 5).c_str(), nullptr, 16); + say_link_body_struct.augment_3 = (uint32) strtol(say_link_body.substr(16, 5).c_str(), nullptr, 16); + say_link_body_struct.augment_4 = (uint32) strtol(say_link_body.substr(21, 5).c_str(), nullptr, 16); + say_link_body_struct.augment_5 = (uint32) strtol(say_link_body.substr(26, 5).c_str(), nullptr, 16); + say_link_body_struct.augment_6 = (uint32) strtol(say_link_body.substr(31, 5).c_str(), nullptr, 16); + say_link_body_struct.is_evolving = (uint8) strtol(say_link_body.substr(36, 1).c_str(), nullptr, 16); + say_link_body_struct.evolve_group = (uint32) strtol(say_link_body.substr(37, 4).c_str(), nullptr, 16); + say_link_body_struct.evolve_level = (uint8) strtol(say_link_body.substr(41, 2).c_str(), nullptr, 16); + say_link_body_struct.ornament_icon = (uint32) strtol(say_link_body.substr(43, 5).c_str(), nullptr, 16); + say_link_body_struct.hash = (uint32) strtol(say_link_body.substr(48, 8).c_str(), nullptr, 16); return true; } -bool EQEmu::saylink::GenerateLinkBody(std::string& say_link_body, const SayLinkBody_Struct& say_link_body_struct) +bool EQEmu::saylink::GenerateLinkBody(std::string &say_link_body, const SayLinkBody_Struct &say_link_body_struct) { say_link_body = StringFormat( "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X", @@ -68,8 +69,9 @@ bool EQEmu::saylink::GenerateLinkBody(std::string& say_link_body, const SayLinkB (0xFFFFFFFF & say_link_body_struct.hash) ); - if (say_link_body.length() != EQEmu::constants::SAY_LINK_BODY_SIZE) + if (say_link_body.length() != EQEmu::constants::SAY_LINK_BODY_SIZE) { return false; + } return true; } @@ -79,7 +81,7 @@ EQEmu::SayLinkEngine::SayLinkEngine() Reset(); } -const std::string& EQEmu::SayLinkEngine::GenerateLink() +const std::string &EQEmu::SayLinkEngine::GenerateLink() { m_Link.clear(); m_LinkBody.clear(); @@ -97,7 +99,7 @@ const std::string& EQEmu::SayLinkEngine::GenerateLink() if ((m_Link.length() == 0) || (m_Link.length() > (EQEmu::constants::SAY_LINK_MAXIMUM_SIZE))) { m_Error = true; - m_Link = ""; + m_Link = ""; Log(Logs::General, Logs::Error, "SayLinkEngine::GenerateLink() failed to generate a useable say link"); Log(Logs::General, Logs::Error, ">> LinkType: %i, Lengths: {link: %u(%u), body: %u(%u), text: %u(%u)}", m_LinkType, @@ -145,83 +147,97 @@ void EQEmu::SayLinkEngine::generate_body() memset(&m_LinkBodyStruct, 0, sizeof(SayLinkBody_Struct)); - const EQEmu::ItemData* item_data = nullptr; + const EQEmu::ItemData *item_data = nullptr; switch (m_LinkType) { - case saylink::SayLinkBlank: - break; - case saylink::SayLinkItemData: - if (m_ItemData == nullptr) { break; } - m_LinkBodyStruct.item_id = m_ItemData->ID; - m_LinkBodyStruct.evolve_group = m_ItemData->LoreGroup; // this probably won't work for all items - //m_LinkBodyStruct.evolve_level = m_ItemData->EvolvingLevel; - // TODO: add hash call - break; - case saylink::SayLinkLootItem: - if (m_LootData == nullptr) { break; } - item_data = database.GetItem(m_LootData->item_id); - if (item_data == nullptr) { break; } - m_LinkBodyStruct.item_id = item_data->ID; - m_LinkBodyStruct.augment_1 = m_LootData->aug_1; - m_LinkBodyStruct.augment_2 = m_LootData->aug_2; - m_LinkBodyStruct.augment_3 = m_LootData->aug_3; - m_LinkBodyStruct.augment_4 = m_LootData->aug_4; - m_LinkBodyStruct.augment_5 = m_LootData->aug_5; - m_LinkBodyStruct.augment_6 = m_LootData->aug_6; - m_LinkBodyStruct.evolve_group = item_data->LoreGroup; // see note above - //m_LinkBodyStruct.evolve_level = item_data->EvolvingLevel; - // TODO: add hash call - break; - case saylink::SayLinkItemInst: - if (m_ItemInst == nullptr) { break; } - if (m_ItemInst->GetItem() == nullptr) { break; } - m_LinkBodyStruct.item_id = m_ItemInst->GetItem()->ID; - m_LinkBodyStruct.augment_1 = m_ItemInst->GetAugmentItemID(0); - m_LinkBodyStruct.augment_2 = m_ItemInst->GetAugmentItemID(1); - m_LinkBodyStruct.augment_3 = m_ItemInst->GetAugmentItemID(2); - m_LinkBodyStruct.augment_4 = m_ItemInst->GetAugmentItemID(3); - m_LinkBodyStruct.augment_5 = m_ItemInst->GetAugmentItemID(4); - m_LinkBodyStruct.augment_6 = m_ItemInst->GetAugmentItemID(5); - m_LinkBodyStruct.is_evolving = (m_ItemInst->IsEvolving() ? 1 : 0); - m_LinkBodyStruct.evolve_group = m_ItemInst->GetItem()->LoreGroup; // see note above - m_LinkBodyStruct.evolve_level = m_ItemInst->GetEvolveLvl(); - m_LinkBodyStruct.ornament_icon = m_ItemInst->GetOrnamentationIcon(); - // TODO: add hash call - break; - default: - break; + case saylink::SayLinkBlank: + break; + case saylink::SayLinkItemData: + if (m_ItemData == nullptr) { break; } + m_LinkBodyStruct.item_id = m_ItemData->ID; + m_LinkBodyStruct.evolve_group = m_ItemData->LoreGroup; // this probably won't work for all items + //m_LinkBodyStruct.evolve_level = m_ItemData->EvolvingLevel; + // TODO: add hash call + break; + case saylink::SayLinkLootItem: + if (m_LootData == nullptr) { break; } + item_data = database.GetItem(m_LootData->item_id); + if (item_data == nullptr) { break; } + m_LinkBodyStruct.item_id = item_data->ID; + m_LinkBodyStruct.augment_1 = m_LootData->aug_1; + m_LinkBodyStruct.augment_2 = m_LootData->aug_2; + m_LinkBodyStruct.augment_3 = m_LootData->aug_3; + m_LinkBodyStruct.augment_4 = m_LootData->aug_4; + m_LinkBodyStruct.augment_5 = m_LootData->aug_5; + m_LinkBodyStruct.augment_6 = m_LootData->aug_6; + m_LinkBodyStruct.evolve_group = item_data->LoreGroup; // see note above + //m_LinkBodyStruct.evolve_level = item_data->EvolvingLevel; + // TODO: add hash call + break; + case saylink::SayLinkItemInst: + if (m_ItemInst == nullptr) { break; } + if (m_ItemInst->GetItem() == nullptr) { break; } + m_LinkBodyStruct.item_id = m_ItemInst->GetItem()->ID; + m_LinkBodyStruct.augment_1 = m_ItemInst->GetAugmentItemID(0); + m_LinkBodyStruct.augment_2 = m_ItemInst->GetAugmentItemID(1); + m_LinkBodyStruct.augment_3 = m_ItemInst->GetAugmentItemID(2); + m_LinkBodyStruct.augment_4 = m_ItemInst->GetAugmentItemID(3); + m_LinkBodyStruct.augment_5 = m_ItemInst->GetAugmentItemID(4); + m_LinkBodyStruct.augment_6 = m_ItemInst->GetAugmentItemID(5); + m_LinkBodyStruct.is_evolving = (m_ItemInst->IsEvolving() ? 1 : 0); + m_LinkBodyStruct.evolve_group = m_ItemInst->GetItem()->LoreGroup; // see note above + m_LinkBodyStruct.evolve_level = m_ItemInst->GetEvolveLvl(); + m_LinkBodyStruct.ornament_icon = m_ItemInst->GetOrnamentationIcon(); + // TODO: add hash call + break; + default: + break; } - if (m_LinkProxyStruct.action_id) + if (m_LinkProxyStruct.action_id) { m_LinkBodyStruct.action_id = m_LinkProxyStruct.action_id; - if (m_LinkProxyStruct.item_id) + } + if (m_LinkProxyStruct.item_id) { m_LinkBodyStruct.item_id = m_LinkProxyStruct.item_id; - if (m_LinkProxyStruct.augment_1) + } + if (m_LinkProxyStruct.augment_1) { m_LinkBodyStruct.augment_1 = m_LinkProxyStruct.augment_1; - if (m_LinkProxyStruct.augment_2) + } + if (m_LinkProxyStruct.augment_2) { m_LinkBodyStruct.augment_2 = m_LinkProxyStruct.augment_2; - if (m_LinkProxyStruct.augment_3) + } + if (m_LinkProxyStruct.augment_3) { m_LinkBodyStruct.augment_3 = m_LinkProxyStruct.augment_3; - if (m_LinkProxyStruct.augment_4) + } + if (m_LinkProxyStruct.augment_4) { m_LinkBodyStruct.augment_4 = m_LinkProxyStruct.augment_4; - if (m_LinkProxyStruct.augment_5) + } + if (m_LinkProxyStruct.augment_5) { m_LinkBodyStruct.augment_5 = m_LinkProxyStruct.augment_5; - if (m_LinkProxyStruct.augment_6) + } + if (m_LinkProxyStruct.augment_6) { m_LinkBodyStruct.augment_6 = m_LinkProxyStruct.augment_6; - if (m_LinkProxyStruct.is_evolving) + } + if (m_LinkProxyStruct.is_evolving) { m_LinkBodyStruct.is_evolving = m_LinkProxyStruct.is_evolving; - if (m_LinkProxyStruct.evolve_group) + } + if (m_LinkProxyStruct.evolve_group) { m_LinkBodyStruct.evolve_group = m_LinkProxyStruct.evolve_group; - if (m_LinkProxyStruct.evolve_level) + } + if (m_LinkProxyStruct.evolve_level) { m_LinkBodyStruct.evolve_level = m_LinkProxyStruct.evolve_level; - if (m_LinkProxyStruct.ornament_icon) + } + if (m_LinkProxyStruct.ornament_icon) { m_LinkBodyStruct.ornament_icon = m_LinkProxyStruct.ornament_icon; - if (m_LinkProxyStruct.hash) + } + if (m_LinkProxyStruct.hash) { m_LinkBodyStruct.hash = m_LinkProxyStruct.hash; + } - if (m_TaskUse) + if (m_TaskUse) { m_LinkBodyStruct.hash = 0x14505DC2; + } m_LinkBody = StringFormat( "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X", @@ -248,29 +264,79 @@ void EQEmu::SayLinkEngine::generate_text() return; } - const EQEmu::ItemData* item_data = nullptr; + const EQEmu::ItemData *item_data = nullptr; switch (m_LinkType) { - case saylink::SayLinkBlank: - break; - case saylink::SayLinkItemData: - if (m_ItemData == nullptr) { break; } - m_LinkText = m_ItemData->Name; - return; - case saylink::SayLinkLootItem: - if (m_LootData == nullptr) { break; } - item_data = database.GetItem(m_LootData->item_id); - if (item_data == nullptr) { break; } - m_LinkText = item_data->Name; - return; - case saylink::SayLinkItemInst: - if (m_ItemInst == nullptr) { break; } - if (m_ItemInst->GetItem() == nullptr) { break; } - m_LinkText = m_ItemInst->GetItem()->Name; - return; - default: - break; + case saylink::SayLinkBlank: + break; + case saylink::SayLinkItemData: + if (m_ItemData == nullptr) { break; } + m_LinkText = m_ItemData->Name; + return; + case saylink::SayLinkLootItem: + if (m_LootData == nullptr) { break; } + item_data = database.GetItem(m_LootData->item_id); + if (item_data == nullptr) { break; } + m_LinkText = item_data->Name; + return; + case saylink::SayLinkItemInst: + if (m_ItemInst == nullptr) { break; } + if (m_ItemInst->GetItem() == nullptr) { break; } + m_LinkText = m_ItemInst->GetItem()->Name; + return; + default: + break; } m_LinkText = "null"; } + +std::string EQEmu::SayLinkEngine::GenerateQuestSaylink(std::string saylink_text, bool silent, std::string link_name) +{ + uint32 saylink_id = 0; + + /** + * Query for an existing phrase and id in the saylink table + */ + std::string query = StringFormat( + "SELECT `id` FROM `saylink` WHERE `phrase` = '%s' LIMIT 1", + EscapeString(saylink_text).c_str()); + + auto results = database.QueryDatabase(query); + + if (results.Success()) { + if (results.RowCount() >= 1) { + for (auto row = results.begin(); row != results.end(); ++row) + saylink_id = static_cast(atoi(row[0])); + } + else { + std::string insert_query = StringFormat( + "INSERT INTO `saylink` (`phrase`) VALUES ('%s')", + EscapeString(saylink_text).c_str()); + + results = database.QueryDatabase(insert_query); + if (!results.Success()) { + Log(Logs::General, Logs::Error, "Error in saylink phrase queries %s", results.ErrorMessage().c_str()); + } + else { + saylink_id = results.LastInsertedID(); + } + } + } + + /** + * Generate the actual link + */ + EQEmu::SayLinkEngine linker; + linker.SetProxyItemID(SAYLINK_ITEM_ID); + if (silent) { + linker.SetProxyAugment2ID(saylink_id); + } + else { + linker.SetProxyAugment1ID(saylink_id); + } + + linker.SetProxyText(link_name.c_str()); + + return linker.GenerateLink(); +} \ No newline at end of file diff --git a/common/say_link.h b/common/say_link.h index 55e3a8237..c282bde05 100644 --- a/common/say_link.h +++ b/common/say_link.h @@ -101,6 +101,8 @@ namespace EQEmu const std::string& LinkBody() { return m_LinkBody; } // contains string format: '' const std::string& LinkText() { return m_LinkText; } // contains string format: '' + static std::string GenerateQuestSaylink(std::string saylink_text, bool silent, std::string link_name); + void Reset(); private: diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 9e0cd5f7e..c4dd86f32 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1175,19 +1175,17 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_ } } -const EQEmu::ItemData* SharedDatabase::GetItem(uint32 id) { - if (id == 0) - { +const EQEmu::ItemData *SharedDatabase::GetItem(uint32 id) +{ + if (id == 0) { return nullptr; } - if(!items_hash || id > items_hash->max_key()) - { + if (!items_hash || id > items_hash->max_key()) { return nullptr; } - if(items_hash->exists(id)) - { + if (items_hash->exists(id)) { return &(items_hash->at(id)); } diff --git a/common/string_util.cpp b/common/string_util.cpp index 3e9986b48..609841807 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -63,6 +63,33 @@ const std::string vStringFormat(const char* format, va_list args) return output; } +const std::string str_tolower(std::string s) +{ + std::transform( + s.begin(), s.end(), s.begin(), + [](unsigned char c) { return std::tolower(c); } + ); + return s; +} + +const std::string str_toupper(std::string s) +{ + std::transform( + s.begin(), s.end(), s.begin(), + [](unsigned char c) { return std::toupper(c); } + ); + return s; +} + +const std::string ucfirst(std::string s) +{ + std::string output = s; + if (!s.empty()) + output[0] = static_cast(toupper(s[0])); + + return output; +} + const std::string StringFormat(const char* format, ...) { va_list args; diff --git a/common/string_util.h b/common/string_util.h index 0db8ff2c9..68c711789 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -24,6 +24,9 @@ #include "types.h" //std::string based +const std::string str_tolower(std::string s); +const std::string str_toupper(std::string s); +const std::string ucfirst(std::string s); const std::string StringFormat(const char* format, ...); const std::string vStringFormat(const char* format, va_list args); std::vector SplitString(const std::string &s, char delim); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 0414a8d2b..8b0c17562 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -82,10 +82,12 @@ SET(zone_sources merc.cpp mob.cpp mob_ai.cpp + mob_info.cpp mod_functions.cpp net.cpp npc.cpp npc_ai.cpp + npc_scale_manager.cpp object.cpp oriented_bounding_box.cpp pathfinder_interface.cpp @@ -137,8 +139,7 @@ SET(zone_sources zone.cpp zone_config.cpp zonedb.cpp - zoning.cpp -) + zoning.cpp) SET(zone_headers aa.h @@ -207,6 +208,7 @@ SET(zone_headers net.h npc.h npc_ai.h + npc_scale_manager.h object.h oriented_bounding_box.h pathfinder_interface.h @@ -242,8 +244,7 @@ SET(zone_headers zone.h zone_config.h zonedb.h - zonedump.h -) + zonedump.h) IF(EQEMU_DEPOP_INVALIDATES_CACHE) ADD_DEFINITIONS(-DDEPOP_INVALIDATES_NPC_TYPES_CACHE) diff --git a/zone/attack.cpp b/zone/attack.cpp index d6cf4227f..22e10f842 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -37,6 +37,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include +#include #ifdef BOTS #include "bot.h" @@ -5463,3 +5464,8 @@ bool Mob::GetWasSpawnedInWater() const { void Mob::SetSpawnedInWater(bool spawned_in_water) { Mob::spawned_in_water = spawned_in_water; } + +int32 Mob::GetHPRegen() const +{ + return hp_regen; +} \ No newline at end of file diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index e71c6e0d5..123e820b4 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -62,12 +62,12 @@ void Mob::CalcBonuses() void NPC::CalcBonuses() { memset(&itembonuses, 0, sizeof(StatBonuses)); - if(RuleB(NPC, UseItemBonusesForNonPets)){ + if (RuleB(NPC, UseItemBonusesForNonPets)) { memset(&itembonuses, 0, sizeof(StatBonuses)); CalcItemBonuses(&itembonuses); } - else{ - if(GetOwner()){ + else { + if (GetOwner()) { memset(&itembonuses, 0, sizeof(StatBonuses)); CalcItemBonuses(&itembonuses); } diff --git a/zone/client.cpp b/zone/client.cpp index 18940d58a..564db321c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -254,7 +254,6 @@ Client::Client(EQStreamInterface* ieqs) InitializeMercInfo(); SetMerc(0); if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) SetPVP(true, false); - logging_enabled = CLIENT_DEFAULT_LOGGING_ENABLED; //for good measure: memset(&m_pp, 0, sizeof(m_pp)); @@ -6670,30 +6669,13 @@ void Client::SendStatsWindow(Client* client, bool use_window) 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]; } + if (GetClass() < 17 && GetClass() > 0) { + class_Name = class_List[GetClass() - 1]; + } // Race - std::string race_Name = itoa(GetRace()); - switch(GetRace()) - { - case 1: race_Name = "Human"; break; - case 2: race_Name = "Barbarian"; break; - case 3: race_Name = "Erudite"; break; - case 4: race_Name = "Wood Elf"; break; - case 5: race_Name = "High Elf"; break; - case 6: race_Name = "Dark Elf"; break; - case 7: race_Name = "Half Elf"; break; - case 8: race_Name = "Dwarf"; break; - case 9: race_Name = "Troll"; break; - case 10: race_Name = "Ogre"; break; - case 11: race_Name = "Halfing"; break; - case 12: race_Name = "Gnome"; break; - case 128: race_Name = "Iksar"; break; - case 130: race_Name = "Vah Shir"; break; - case 330: race_Name = "Froglok"; break; - case 522: race_Name = "Drakkin"; break; - default: break; - } + std::string race_name = GetRaceIDName(GetRace()); + /*########################################################## ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ H/M/E String @@ -7122,7 +7104,7 @@ void Client::SendStatsWindow(Client* client, bool use_window) std::ostringstream final_string; final_string << - /* C/L/R */ indP << "Class: " << class_Name << indS << "Level: " << static_cast(GetLevel()) << indS << "Race: " << race_Name << "
" << + /* C/L/R */ indP << "Class: " << class_Name << indS << "Level: " << static_cast(GetLevel()) << indS << "Race: " << race_name << "
" << /* Runes */ indP << "Rune: " << rune_number << indL << indS << "Spell Rune: " << magic_rune_number << "
" << /* HP/M/E */ HME_row << /* DS */ indP << "DS: " << (itembonuses.DamageShield + spellbonuses.DamageShield*-1) << " (Spell: " << (spellbonuses.DamageShield*-1) << " + Item: " << itembonuses.DamageShield << " / " << RuleI(Character, ItemDamageShieldCap) << ")
" << diff --git a/zone/client.h b/zone/client.h index 023367e50..f14dc9336 100644 --- a/zone/client.h +++ b/zone/client.h @@ -851,7 +851,6 @@ public: void SetAATitle(const char *Title); void SetTitleSuffix(const char *txt); void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing); - int32 acmod(); // Item methods void EVENT_ITEM_ScriptStopReturn(); diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 39c37c874..8651a622f 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -275,8 +275,9 @@ int32 Client::CalcHPRegen(bool bCombat) if (!skip_innate && m_pp.InnateSkills[InnateRegen] != InnateDisabled) { if (level >= 50) { ++base; - if (level >= 55) + if (level >= 55) { ++base; + } } base *= 2; } @@ -331,6 +332,7 @@ int32 Client::CalcMaxHP() if (hp_perc_cap) { int curHP_cap = (max_hp * hp_perc_cap) / 100; if (cur_hp > curHP_cap || (spellbonuses.HPPercCap[1] && cur_hp > spellbonuses.HPPercCap[1])) { + cur_hp = curHP_cap; } } @@ -340,136 +342,137 @@ int32 Client::CalcMaxHP() uint32 Mob::GetClassLevelFactor() { uint32 multiplier = 0; - uint8 mlevel = GetLevel(); + uint8 mlevel = GetLevel(); switch (GetClass()) { case WARRIOR: { - if (mlevel < 20) { - multiplier = 220; - } - else if (mlevel < 30) { - multiplier = 230; - } - else if (mlevel < 40) { - multiplier = 250; - } - else if (mlevel < 53) { - multiplier = 270; - } - else if (mlevel < 57) { - multiplier = 280; - } - else if (mlevel < 60) { - multiplier = 290; - } - else if (mlevel < 70) { - multiplier = 300; - } - else { - multiplier = 311; - } - break; + if (mlevel < 20) { + multiplier = 220; } + else if (mlevel < 30) { + multiplier = 230; + } + else if (mlevel < 40) { + multiplier = 250; + } + else if (mlevel < 53) { + multiplier = 270; + } + else if (mlevel < 57) { + multiplier = 280; + } + else if (mlevel < 60) { + multiplier = 290; + } + else if (mlevel < 70) { + multiplier = 300; + } + else { + multiplier = 311; + } + break; + } case DRUID: case CLERIC: case SHAMAN: { - if (mlevel < 70) { - multiplier = 150; - } - else { - multiplier = 157; - } - break; + if (mlevel < 70) { + multiplier = 150; } + else { + multiplier = 157; + } + break; + } case BERSERKER: case PALADIN: case SHADOWKNIGHT: { - if (mlevel < 35) { - multiplier = 210; - } - else if (mlevel < 45) { - multiplier = 220; - } - else if (mlevel < 51) { - multiplier = 230; - } - else if (mlevel < 56) { - multiplier = 240; - } - else if (mlevel < 60) { - multiplier = 250; - } - else if (mlevel < 68) { - multiplier = 260; - } - else { - multiplier = 270; - } - break; + if (mlevel < 35) { + multiplier = 210; } + else if (mlevel < 45) { + multiplier = 220; + } + else if (mlevel < 51) { + multiplier = 230; + } + else if (mlevel < 56) { + multiplier = 240; + } + else if (mlevel < 60) { + multiplier = 250; + } + else if (mlevel < 68) { + multiplier = 260; + } + else { + multiplier = 270; + } + break; + } case MONK: case BARD: case ROGUE: case BEASTLORD: { - if (mlevel < 51) { - multiplier = 180; - } - else if (mlevel < 58) { - multiplier = 190; - } - else if (mlevel < 70) { - multiplier = 200; - } - else { - multiplier = 210; - } - break; + if (mlevel < 51) { + multiplier = 180; } + else if (mlevel < 58) { + multiplier = 190; + } + else if (mlevel < 70) { + multiplier = 200; + } + else { + multiplier = 210; + } + break; + } case RANGER: { - if (mlevel < 58) { - multiplier = 200; - } - else if (mlevel < 70) { - multiplier = 210; - } - else { - multiplier = 220; - } - break; + if (mlevel < 58) { + multiplier = 200; } + else if (mlevel < 70) { + multiplier = 210; + } + else { + multiplier = 220; + } + break; + } case MAGICIAN: case WIZARD: case NECROMANCER: case ENCHANTER: { - if (mlevel < 70) { - multiplier = 120; - } - else { - multiplier = 127; - } - break; + if (mlevel < 70) { + multiplier = 120; } + else { + multiplier = 127; + } + break; + } default: { - if (mlevel < 35) { - multiplier = 210; - } - else if (mlevel < 45) { - multiplier = 220; - } - else if (mlevel < 51) { - multiplier = 230; - } - else if (mlevel < 56) { - multiplier = 240; - } - else if (mlevel < 60) { - multiplier = 250; - } - else { - multiplier = 260; - } - break; + if (mlevel < 35) { + multiplier = 210; } + else if (mlevel < 45) { + multiplier = 220; + } + else if (mlevel < 51) { + multiplier = 230; + } + else if (mlevel < 56) { + multiplier = 240; + } + else if (mlevel < 60) { + multiplier = 250; + } + else { + multiplier = 260; + } + break; + } } + return multiplier; } @@ -560,537 +563,6 @@ int32 Client::GetRawItemAC() return Total; } -int32 Client::acmod() -{ - int agility = GetAGI(); - int level = GetLevel(); - if (agility < 1 || level < 1) { - return (0); - } - if (agility <= 74) { - if (agility == 1) { - return -24; - } - else if (agility <= 3) { - return -23; - } - else if (agility == 4) { - return -22; - } - else if (agility <= 6) { - return -21; - } - else if (agility <= 8) { - return -20; - } - else if (agility == 9) { - return -19; - } - else if (agility <= 11) { - return -18; - } - else if (agility == 12) { - return -17; - } - else if (agility <= 14) { - return -16; - } - else if (agility <= 16) { - return -15; - } - else if (agility == 17) { - return -14; - } - else if (agility <= 19) { - return -13; - } - else if (agility == 20) { - return -12; - } - else if (agility <= 22) { - return -11; - } - else if (agility <= 24) { - return -10; - } - else if (agility == 25) { - return -9; - } - else if (agility <= 27) { - return -8; - } - else if (agility == 28) { - return -7; - } - else if (agility <= 30) { - return -6; - } - else if (agility <= 32) { - return -5; - } - else if (agility == 33) { - return -4; - } - else if (agility <= 35) { - return -3; - } - else if (agility == 36) { - return -2; - } - else if (agility <= 38) { - return -1; - } - else if (agility <= 65) { - return 0; - } - else if (agility <= 70) { - return 1; - } - else if (agility <= 74) { - return 5; - } - } - else if (agility <= 137) { - if (agility == 75) { - if (level <= 6) { - return 9; - } - else if (level <= 19) { - return 23; - } - else if (level <= 39) { - return 33; - } - else { - return 39; - } - } - else if (agility >= 76 && agility <= 79) { - if (level <= 6) { - return 10; - } - else if (level <= 19) { - return 23; - } - else if (level <= 39) { - return 33; - } - else { - return 40; - } - } - else if (agility == 80) { - if (level <= 6) { - return 11; - } - else if (level <= 19) { - return 24; - } - else if (level <= 39) { - return 34; - } - else { - return 41; - } - } - else if (agility >= 81 && agility <= 85) { - if (level <= 6) { - return 12; - } - else if (level <= 19) { - return 25; - } - else if (level <= 39) { - return 35; - } - else { - return 42; - } - } - else if (agility >= 86 && agility <= 90) { - if (level <= 6) { - return 12; - } - else if (level <= 19) { - return 26; - } - else if (level <= 39) { - return 36; - } - else { - return 42; - } - } - else if (agility >= 91 && agility <= 95) { - if (level <= 6) { - return 13; - } - else if (level <= 19) { - return 26; - } - else if (level <= 39) { - return 36; - } - else { - return 43; - } - } - else if (agility >= 96 && agility <= 99) { - if (level <= 6) { - return 14; - } - else if (level <= 19) { - return 27; - } - else if (level <= 39) { - return 37; - } - else { - return 44; - } - } - else if (agility == 100 && level >= 7) { - if (level <= 19) { - return 28; - } - else if (level <= 39) { - return 38; - } - else { - return 45; - } - } - else if (level <= 6) { - return 15; - } - //level is >6 - else if (agility >= 101 && agility <= 105) { - if (level <= 19) { - return 29; - } - else if (level <= 39) { - return 39; // not verified - } - else { - return 45; - } - } - else if (agility >= 106 && agility <= 110) { - if (level <= 19) { - return 29; - } - else if (level <= 39) { - return 39; // not verified - } - else { - return 46; - } - } - else if (agility >= 111 && agility <= 115) { - if (level <= 19) { - return 30; - } - else if (level <= 39) { - return 40; // not verified - } - else { - return 47; - } - } - else if (agility >= 116 && agility <= 119) { - if (level <= 19) { - return 31; - } - else if (level <= 39) { - return 41; - } - else { - return 47; - } - } - else if (level <= 19) { - return 32; - } - //level is > 19 - else if (agility == 120) { - if (level <= 39) { - return 42; - } - else { - return 48; - } - } - else if (agility <= 125) { - if (level <= 39) { - return 42; - } - else { - return 49; - } - } - else if (agility <= 135) { - if (level <= 39) { - return 42; - } - else { - return 50; - } - } - else { - if (level <= 39) { - return 42; - } - else { - return 51; - } - } - } - else if (agility <= 300) { - if (level <= 6) { - if (agility <= 139) { - return (21); - } - else if (agility == 140) { - return (22); - } - else if (agility <= 145) { - return (23); - } - else if (agility <= 150) { - return (23); - } - else if (agility <= 155) { - return (24); - } - else if (agility <= 159) { - return (25); - } - else if (agility == 160) { - return (26); - } - else if (agility <= 165) { - return (26); - } - else if (agility <= 170) { - return (27); - } - else if (agility <= 175) { - return (28); - } - else if (agility <= 179) { - return (28); - } - else if (agility == 180) { - return (29); - } - else if (agility <= 185) { - return (30); - } - else if (agility <= 190) { - return (31); - } - else if (agility <= 195) { - return (31); - } - else if (agility <= 199) { - return (32); - } - else if (agility <= 219) { - return (33); - } - else if (agility <= 239) { - return (34); - } - else { - return (35); - } - } - else if (level <= 19) { - if (agility <= 139) { - return (34); - } - else if (agility == 140) { - return (35); - } - else if (agility <= 145) { - return (36); - } - else if (agility <= 150) { - return (37); - } - else if (agility <= 155) { - return (37); - } - else if (agility <= 159) { - return (38); - } - else if (agility == 160) { - return (39); - } - else if (agility <= 165) { - return (40); - } - else if (agility <= 170) { - return (40); - } - else if (agility <= 175) { - return (41); - } - else if (agility <= 179) { - return (42); - } - else if (agility == 180) { - return (43); - } - else if (agility <= 185) { - return (43); - } - else if (agility <= 190) { - return (44); - } - else if (agility <= 195) { - return (45); - } - else if (agility <= 199) { - return (45); - } - else if (agility <= 219) { - return (46); - } - else if (agility <= 239) { - return (47); - } - else { - return (48); - } - } - else if (level <= 39) { - if (agility <= 139) { - return (44); - } - else if (agility == 140) { - return (45); - } - else if (agility <= 145) { - return (46); - } - else if (agility <= 150) { - return (47); - } - else if (agility <= 155) { - return (47); - } - else if (agility <= 159) { - return (48); - } - else if (agility == 160) { - return (49); - } - else if (agility <= 165) { - return (50); - } - else if (agility <= 170) { - return (50); - } - else if (agility <= 175) { - return (51); - } - else if (agility <= 179) { - return (52); - } - else if (agility == 180) { - return (53); - } - else if (agility <= 185) { - return (53); - } - else if (agility <= 190) { - return (54); - } - else if (agility <= 195) { - return (55); - } - else if (agility <= 199) { - return (55); - } - else if (agility <= 219) { - return (56); - } - else if (agility <= 239) { - return (57); - } - else { - return (58); - } - } - else { //lvl >= 40 - if (agility <= 139) { - return (51); - } - else if (agility == 140) { - return (52); - } - else if (agility <= 145) { - return (53); - } - else if (agility <= 150) { - return (53); - } - else if (agility <= 155) { - return (54); - } - else if (agility <= 159) { - return (55); - } - else if (agility == 160) { - return (56); - } - else if (agility <= 165) { - return (56); - } - else if (agility <= 170) { - return (57); - } - else if (agility <= 175) { - return (58); - } - else if (agility <= 179) { - return (58); - } - else if (agility == 180) { - return (59); - } - else if (agility <= 185) { - return (60); - } - else if (agility <= 190) { - return (61); - } - else if (agility <= 195) { - return (61); - } - else if (agility <= 199) { - return (62); - } - else if (agility <= 219) { - return (63); - } - else if (agility <= 239) { - return (64); - } - else { - return (65); - } - } - } - else { - //seems about 21 agil per extra AC pt over 300... - return (65 + ((agility - 300) / 21)); - } - Log(Logs::Detail, Logs::Error, "Error in Client::acmod(): Agility: %i, Level: %i", agility, level); - return 0; -}; - int32 Client::CalcMaxMana() { switch (GetCasterClass()) { diff --git a/zone/command.cpp b/zone/command.cpp index 98faec71a..4cc8b8a54 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -249,6 +249,7 @@ int command_init(void) command_add("lastname", "[new lastname] - Set your or your player target's lastname", 50, command_lastname) || command_add("level", "[level] - Set your or your target's level", 10, command_level) || command_add("listnpcs", "[name/range] - Search NPCs", 20, command_listnpcs) || + command_add("list", "[npc] [name|all] - Search entities", 20, command_list) || command_add("listpetition", "- List petitions", 50, command_listpetition) || command_add("load_shared_memory", "[shared_memory_name] - Reloads shared memory and uses the input as output", 250, command_load_shared_memory) || command_add("loc", "- Print out your or your target's current location and heading", 0, command_loc) || @@ -1328,16 +1329,266 @@ void command_delpetition(Client *c, const Seperator *sep) void command_listnpcs(Client *c, const Seperator *sep) { - if (strcasecmp(sep->arg[1], "all") == 0) - entity_list.ListNPCs(c,sep->arg[1],sep->arg[2],0); - else if(sep->IsNumber(1) && sep->IsNumber(2)) - entity_list.ListNPCs(c,sep->arg[1],sep->arg[2],2); - else if(sep->arg[1][0] != 0) - entity_list.ListNPCs(c,sep->arg[1],sep->arg[2],1); + c->Message(0, "Deprecated, use the #list command (#list npcs )"); +} + +void command_list(Client *c, const Seperator *sep) +{ + std::string search_type; + if (strcasecmp(sep->arg[1], "npcs") == 0) { + search_type = "npcs"; + } + + if (strcasecmp(sep->arg[1], "players") == 0) { + search_type = "players"; + } + + if (strcasecmp(sep->arg[1], "corpses") == 0) { + search_type = "corpses"; + } + + if (strcasecmp(sep->arg[1], "doors") == 0) { + search_type = "doors"; + } + + if (strcasecmp(sep->arg[1], "objects") == 0) { + search_type = "objects"; + } + + if (search_type.length() > 0) { + + int entity_count = 0; + int found_count = 0; + + std::string search_string; + + if (sep->arg[2]) { + search_string = sep->arg[2]; + } + + /** + * NPC + */ + if (search_type.find("npcs") != std::string::npos) { + auto &entity_list_search = entity_list.GetMobList(); + + for (auto &itr : entity_list_search) { + Mob *entity = itr.second; + if (!entity->IsNPC()) { + continue; + } + + entity_count++; + + std::string entity_name = entity->GetCleanName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", + EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Client + */ + if (search_type.find("players") != std::string::npos) { + auto &entity_list_search = entity_list.GetClientList(); + + for (auto &itr : entity_list_search) { + Client *entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetCleanName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", + EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Corpse + */ + if (search_type.find("corpses") != std::string::npos) { + auto &entity_list_search = entity_list.GetCorpseList(); + + for (auto &itr : entity_list_search) { + Corpse *entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetCleanName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f", + EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Doors + */ + if (search_type.find("doors") != std::string::npos) { + auto &entity_list_search = entity_list.GetDoorsList(); + + for (auto &itr : entity_list_search) { + Doors * entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetDoorName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | Entity ID %5d | Door ID %i | %s | x %.0f | y %0.f | z %.0f", + EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetDoorID(), + entity->GetDoorName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + /** + * Objects + */ + if (search_type.find("objects") != std::string::npos) { + auto &entity_list_search = entity_list.GetObjectList(); + + for (auto &itr : entity_list_search) { + Object * entity = itr.second; + + entity_count++; + + std::string entity_name = entity->GetModelName(); + + /** + * Filter by name + */ + if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) { + continue; + } + + std::string saylink = StringFormat( + "#goto %.0f %0.f %.0f", + entity->GetX(), + entity->GetY(), + entity->GetZ()); + + c->Message( + 0, + "| %s | Entity ID %5d | Object DBID %i | %s | x %.0f | y %0.f | z %.0f", + EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Goto").c_str(), + entity->GetID(), + entity->GetDBID(), + entity->GetModelName(), + entity->GetX(), + entity->GetY(), + entity->GetZ() + ); + + found_count++; + } + } + + if (found_count) { + c->Message( + 0, "Found (%i) of type (%s) in zone (%i) total", + found_count, + search_type.c_str(), + entity_count + ); + } + } else { - c->Message(0, "Usage of #listnpcs:"); - c->Message(0, "#listnpcs [#] [#] (Each number would search by ID, ex. #listnpcs 1 30, searches 1-30)"); - c->Message(0, "#listnpcs [name] (Would search for a npc with [name])"); + c->Message(0, "Usage of #list"); + c->Message(0, "- #list [npcs|players|corpses|doors|objects] [search]"); + c->Message(0, "- Example: #list npc (Blank for all)"); } } @@ -1460,24 +1711,24 @@ void command_npcstats(Client *c, const Seperator *sep) c->Message(0, "ERROR: Target is not a NPC!"); else { auto target_npc = c->GetTarget()->CastToNPC(); - c->Message(0, "NPC Stats:"); - c->Message(0, "Name: %s NpcID: %u", target_npc->GetName(), target_npc->GetNPCTypeID()); - c->Message(0, "Race: %i Level: %i Class: %i Material: %i", target_npc->GetRace(), target_npc->GetLevel(), target_npc->GetClass(), target_npc->GetTexture()); - c->Message(0, "Current HP: %i Max HP: %i", target_npc->GetHP(), target_npc->GetMaxHP()); + c->Message(0, "# NPC Stats"); + c->Message(0, "- Name: %s NpcID: %u", target_npc->GetName(), target_npc->GetNPCTypeID()); + c->Message(0, "- Race: %i Level: %i Class: %i Material: %i", target_npc->GetRace(), target_npc->GetLevel(), target_npc->GetClass(), target_npc->GetTexture()); + c->Message(0, "- Current HP: %i Max HP: %i", target_npc->GetHP(), target_npc->GetMaxHP()); //c->Message(0, "Weapon Item Number: %s", target_npc->GetWeapNo()); - c->Message(0, "Gender: %i Size: %f Bodytype: %d", target_npc->GetGender(), target_npc->GetSize(), target_npc->GetBodyType()); - c->Message(0, "Runspeed: %.3f Walkspeed: %.3f", static_cast(0.025f * target_npc->GetRunspeed()), static_cast(0.025f * target_npc->GetWalkspeed())); - c->Message(0, "Spawn Group: %i Grid: %i", target_npc->GetSp2(), target_npc->GetGrid()); + c->Message(0, "- Gender: %i Size: %f Bodytype: %d", target_npc->GetGender(), target_npc->GetSize(), target_npc->GetBodyType()); + c->Message(0, "- Runspeed: %.3f Walkspeed: %.3f", static_cast(0.025f * target_npc->GetRunspeed()), static_cast(0.025f * target_npc->GetWalkspeed())); + c->Message(0, "- Spawn Group: %i Grid: %i", target_npc->GetSp2(), target_npc->GetGrid()); if (target_npc->proximity) { - c->Message(0, "Proximity: Enabled"); - c->Message(0, "Cur_X: %1.3f, Cur_Y: %1.3f, Cur_Z: %1.3f", target_npc->GetX(), target_npc->GetY(), target_npc->GetZ()); - c->Message(0, "Min_X: %1.3f(%1.3f), Max_X: %1.3f(%1.3f), X_Range: %1.3f", target_npc->proximity->min_x, (target_npc->proximity->min_x - target_npc->GetX()), target_npc->proximity->max_x, (target_npc->proximity->max_x - target_npc->GetX()), (target_npc->proximity->max_x - target_npc->proximity->min_x)); - c->Message(0, "Min_Y: %1.3f(%1.3f), Max_Y: %1.3f(%1.3f), Y_Range: %1.3f", target_npc->proximity->min_y, (target_npc->proximity->min_y - target_npc->GetY()), target_npc->proximity->max_y, (target_npc->proximity->max_y - target_npc->GetY()), (target_npc->proximity->max_y - target_npc->proximity->min_y)); - c->Message(0, "Min_Z: %1.3f(%1.3f), Max_Z: %1.3f(%1.3f), Z_Range: %1.3f", target_npc->proximity->min_z, (target_npc->proximity->min_z - target_npc->GetZ()), target_npc->proximity->max_z, (target_npc->proximity->max_z - target_npc->GetZ()), (target_npc->proximity->max_z - target_npc->proximity->min_z)); - c->Message(0, "Say: %s", (target_npc->proximity->say ? "Enabled" : "Disabled")); + c->Message(0, "- Proximity: Enabled"); + c->Message(0, "-- Cur_X: %1.3f, Cur_Y: %1.3f, Cur_Z: %1.3f", target_npc->GetX(), target_npc->GetY(), target_npc->GetZ()); + c->Message(0, "-- Min_X: %1.3f(%1.3f), Max_X: %1.3f(%1.3f), X_Range: %1.3f", target_npc->proximity->min_x, (target_npc->proximity->min_x - target_npc->GetX()), target_npc->proximity->max_x, (target_npc->proximity->max_x - target_npc->GetX()), (target_npc->proximity->max_x - target_npc->proximity->min_x)); + c->Message(0, "-- Min_Y: %1.3f(%1.3f), Max_Y: %1.3f(%1.3f), Y_Range: %1.3f", target_npc->proximity->min_y, (target_npc->proximity->min_y - target_npc->GetY()), target_npc->proximity->max_y, (target_npc->proximity->max_y - target_npc->GetY()), (target_npc->proximity->max_y - target_npc->proximity->min_y)); + c->Message(0, "-- Min_Z: %1.3f(%1.3f), Max_Z: %1.3f(%1.3f), Z_Range: %1.3f", target_npc->proximity->min_z, (target_npc->proximity->min_z - target_npc->GetZ()), target_npc->proximity->max_z, (target_npc->proximity->max_z - target_npc->GetZ()), (target_npc->proximity->max_z - target_npc->proximity->min_z)); + c->Message(0, "-- Say: %s", (target_npc->proximity->say ? "Enabled" : "Disabled")); } else { - c->Message(0, "Proximity: Disabled"); + c->Message(0, "-Proximity: Disabled"); } c->Message(0, ""); c->Message(0, "EmoteID: %i", target_npc->GetEmoteID()); @@ -2024,12 +2275,12 @@ void command_grid(Client *c, const Seperator *sep) } std::string query = StringFormat( - "SELECT `x`, `y`, `z`, `heading`, `number`, `pause` " - "FROM `grid_entries` " - "WHERE `zoneid` = %u and `gridid` = %i " - "ORDER BY `number` ", - zone->GetZoneID(), - target->CastToNPC()->GetGrid() + "SELECT `x`, `y`, `z`, `heading`, `number`, `pause` " + "FROM `grid_entries` " + "WHERE `zoneid` = %u and `gridid` = %i " + "ORDER BY `number` ", + zone->GetZoneID(), + target->CastToNPC()->GetGrid() ); auto results = database.QueryDatabase(query); @@ -2046,11 +2297,12 @@ void command_grid(Client *c, const Seperator *sep) /** * Depop any node npc's already spawned */ - auto &mob_list = entity_list.GetMobList(); - for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) { + auto &mob_list = entity_list.GetMobList(); + for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) { Mob *mob = itr->second; - if (mob->IsNPC() && mob->GetRace() == 2254) + if (mob->IsNPC() && mob->GetRace() == 2254) { mob->Depop(); + } } /** @@ -2060,21 +2312,22 @@ void command_grid(Client *c, const Seperator *sep) auto node_position = glm::vec4(atof(row[0]), atof(row[1]), atof(row[2]), atof(row[3])); NPC *npc = NPC::SpawnGridNodeNPC( - target->GetCleanName(), - node_position, - static_cast(target->CastToNPC()->GetGrid()), - static_cast(atoi(row[4])), - static_cast(atoi(row[5])) + target->GetCleanName(), + node_position, + static_cast(target->CastToNPC()->GetGrid()), + static_cast(atoi(row[4])), + static_cast(atoi(row[5])) ); npc->SetFlyMode(1); npc->GMMove(node_position.x, node_position.y, node_position.z, node_position.w); } } - else if (strcasecmp("delete", sep->arg[1]) == 0) - database.ModifyGrid(c, true,atoi(sep->arg[2]),0,0,zone->GetZoneID()); + else if (strcasecmp("delete", sep->arg[1]) == 0) { + database.ModifyGrid(c, true, atoi(sep->arg[2]), 0, 0, zone->GetZoneID()); + } else { - c->Message(0,"Usage: #grid add/delete grid_num wandertype pausetype"); - c->Message(0,"Usage: #grid max - displays the highest grid ID used in this zone (for add)"); + c->Message(0, "Usage: #grid add/delete grid_num wandertype pausetype"); + c->Message(0, "Usage: #grid max - displays the highest grid ID used in this zone (for add)"); } } diff --git a/zone/command.h b/zone/command.h index d3bae6c0c..16a7177c2 100644 --- a/zone/command.h +++ b/zone/command.h @@ -152,6 +152,7 @@ void command_kill(Client *c, const Seperator *sep); void command_lastname(Client *c, const Seperator *sep); void command_level(Client *c, const Seperator *sep); void command_listnpcs(Client *c, const Seperator *sep); +void command_list(Client *c, const Seperator *sep); void command_listpetition(Client *c, const Seperator *sep); void command_load_shared_memory(Client *c, const Seperator *sep); void command_loc(Client *c, const Seperator *sep); diff --git a/zone/doors.cpp b/zone/doors.cpp index 9561757df..1c5301622 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -820,7 +820,7 @@ void Doors::SetDisableTimer(bool flag) { void Doors::CreateDatabaseEntry() { - if(database.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion()) - 1 >= 255) { + if (database.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion()) - 1 >= 255) { return; } @@ -828,18 +828,32 @@ void Doors::CreateDatabaseEntry() * Persist */ database.InsertDoor( - GetDoorDBID(), - GetDoorID(), - GetDoorName(), - m_Position, - GetOpenType(), - static_cast(GetGuildID()), - GetLockpick(), - GetKeyItem(), - static_cast(GetDoorParam()), - static_cast(GetInvertState()), - GetIncline(), - GetSize() + GetDoorDBID(), + GetDoorID(), + GetDoorName(), + m_Position, + GetOpenType(), + static_cast(GetGuildID()), + GetLockpick(), + GetKeyItem(), + static_cast(GetDoorParam()), + static_cast(GetInvertState()), + GetIncline(), + GetSize() ); } +float Doors::GetX() +{ + return m_Position.x; +} + +float Doors::GetY() +{ + return m_Position.y; +} + +float Doors::GetZ() +{ + return m_Position.z; +} diff --git a/zone/doors.h b/zone/doors.h index f112f84f1..78debbaba 100644 --- a/zone/doors.h +++ b/zone/doors.h @@ -64,6 +64,10 @@ public: void SetSize(uint16 size); void ToggleState(Mob *sender); + float GetX(); + float GetY(); + float GetZ(); + private: uint32 database_id; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 633bcda53..4bdd4fcb8 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3025,10 +3025,10 @@ XS(XS__saylink) { Perl_croak(aTHX_ "Usage: quest::saylink(string message, [bool silent = false], [link_name = message])"); dXSTARG; - Const_char *RETVAL; - char message[250]; - char link_name[250]; - bool silent = false; + std::string RETVAL; + char message[250]; + char link_name[250]; + bool silent = false; strcpy(message, (char *) SvPV_nolen(ST(0))); if (items >= 2) { silent = ((int) SvIV(ST(1))) == 0 ? false : true; @@ -3039,7 +3039,8 @@ XS(XS__saylink) { strcpy(link_name, message); RETVAL = quest_manager.saylink(message, silent, link_name); - sv_setpv(TARG, RETVAL); + + sv_setpv(TARG, RETVAL.c_str()); XSprePUSH; PUSHTARG; XSRETURN(1); diff --git a/zone/entity.cpp b/zone/entity.cpp index 2523c7d96..3cc2928cf 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -40,6 +40,8 @@ #include "string_ids.h" #include "worldserver.h" #include "water_map.h" +#include "npc_scale_manager.h" +#include "../common/say_link.h" #ifdef _WINDOWS #define snprintf _snprintf @@ -2753,58 +2755,6 @@ char *EntityList::RemoveNumbers(char *name) return name; } -void EntityList::ListNPCs(Client* client, const char *arg1, const char *arg2, uint8 searchtype) -{ - if (arg1 == 0) - searchtype = 0; - else if (arg2 == 0 && searchtype >= 2) - searchtype = 0; - uint32 x = 0; - uint32 z = 0; - char sName[36]; - - auto it = npc_list.begin(); - client->Message(0, "NPCs in the zone:"); - if (searchtype == 0) { - while (it != npc_list.end()) { - NPC *n = it->second; - - client->Message(0, " %5d: %s (%.0f, %0.f, %.0f)", n->GetID(), n->GetName(), n->GetX(), n->GetY(), n->GetZ()); - x++; - z++; - ++it; - } - } else if (searchtype == 1) { - client->Message(0, "Searching by name method. (%s)",arg1); - auto tmp = new char[strlen(arg1) + 1]; - strcpy(tmp, arg1); - strupr(tmp); - while (it != npc_list.end()) { - z++; - strcpy(sName, it->second->GetName()); - strupr(sName); - if (strstr(sName, tmp)) { - NPC *n = it->second; - client->Message(0, " %5d: %s (%.0f, %.0f, %.0f)", n->GetID(), n->GetName(), n->GetX(), n->GetY(), n->GetZ()); - x++; - } - ++it; - } - safe_delete_array(tmp); - } else if (searchtype == 2) { - client->Message(0, "Searching by number method. (%s %s)",arg1,arg2); - while (it != npc_list.end()) { - z++; - if ((it->second->GetID() >= atoi(arg1)) && (it->second->GetID() <= atoi(arg2)) && (atoi(arg1) <= atoi(arg2))) { - client->Message(0, " %5d: %s", it->second->GetID(), it->second->GetName()); - x++; - } - ++it; - } - } - client->Message(0, "%d npcs listed. There is a total of %d npcs in this zone.", x, z); -} - void EntityList::ListNPCCorpses(Client *client) { uint32 x = 0; diff --git a/zone/entity.h b/zone/entity.h index f32532392..c5d4ad2a5 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -382,7 +382,6 @@ public: void SendPetitionToAdmins(); void AddLootToNPCS(uint32 item_id, uint32 count); - void ListNPCs(Client* client, const char* arg1 = 0, const char* arg2 = 0, uint8 searchtype = 0); void ListNPCCorpses(Client* client); void ListPlayerCorpses(Client* client); int32 DeleteNPCCorpses(); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 955b69d5e..5d9e2d95c 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -789,33 +789,29 @@ int lua_merchant_count_item(uint32 npc_id, uint32 item_id) { std::string lua_item_link(int item_id) { char text[250] = { 0 }; - quest_manager.varlink(text, item_id); - return std::string(text); + return quest_manager.varlink(text, item_id); } std::string lua_say_link(const char *phrase, bool silent, const char *link_name) { char text[256] = { 0 }; strncpy(text, phrase, 255); - quest_manager.saylink(text, silent, link_name); - return std::string(text); + return quest_manager.saylink(text, silent, link_name); } std::string lua_say_link(const char *phrase, bool silent) { char text[256] = { 0 }; strncpy(text, phrase, 255); - quest_manager.saylink(text, silent, text); - return std::string(text); + return quest_manager.saylink(text, silent, text); } std::string lua_say_link(const char *phrase) { char text[256] = { 0 }; strncpy(text, phrase, 255); - quest_manager.saylink(text, false, text); - return std::string(text); + return quest_manager.saylink(text, false, text); } std::string lua_get_data(std::string bucket_key) { diff --git a/zone/mob.cpp b/zone/mob.cpp index e4c7b5f26..90a999a0f 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -38,149 +38,153 @@ extern EntityList entity_list; extern Zone* zone; extern WorldServer worldserver; -Mob::Mob(const char* in_name, - const char* in_lastname, - int32 in_cur_hp, - int32 in_max_hp, - uint8 in_gender, - uint16 in_race, - uint8 in_class, - bodyType in_bodytype, - uint8 in_deity, - uint8 in_level, - uint32 in_npctype_id, - float in_size, - float in_runspeed, - const glm::vec4& position, - uint8 in_light, - uint8 in_texture, - uint8 in_helmtexture, - uint16 in_ac, - uint16 in_atk, - uint16 in_str, - uint16 in_sta, - uint16 in_dex, - uint16 in_agi, - uint16 in_int, - uint16 in_wis, - uint16 in_cha, - uint8 in_haircolor, - uint8 in_beardcolor, - uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? - uint8 in_eyecolor2, - uint8 in_hairstyle, - uint8 in_luclinface, - uint8 in_beard, - uint32 in_drakkin_heritage, - uint32 in_drakkin_tattoo, - uint32 in_drakkin_details, - EQEmu::TintProfile in_armor_tint, - uint8 in_aa_title, - uint8 in_see_invis, // see through invis/ivu - uint8 in_see_invis_undead, - uint8 in_see_hide, - uint8 in_see_improved_hide, - int32 in_hp_regen, - int32 in_mana_regen, - uint8 in_qglobal, - uint8 in_maxlevel, - uint32 in_scalerate, - uint8 in_armtexture, - uint8 in_bracertexture, - uint8 in_handtexture, - uint8 in_legtexture, - uint8 in_feettexture - ) : - attack_timer(2000), - attack_dw_timer(2000), - ranged_timer(2000), - tic_timer(6000), - mana_timer(2000), - spellend_timer(0), - rewind_timer(30000), - bindwound_timer(10000), - stunned_timer(0), - spun_timer(0), - bardsong_timer(6000), - gravity_timer(1000), - viral_timer(0), - m_FearWalkTarget(-999999.0f, -999999.0f, -999999.0f), - m_TargetLocation(glm::vec3()), - m_TargetV(glm::vec3()), - flee_timer(FLEE_CHECK_TIMER), - m_Position(position), - tmHidden(-1), - mitigation_ac(0), - m_specialattacks(eSpecialAttacks::None), - fix_z_timer(300), - fix_z_timer_engaged(100), - attack_anim_timer(1000), - position_update_melee_push_timer(500), - hate_list_cleanup_timer(6000) +Mob::Mob( + const char *in_name, + const char *in_lastname, + int32 in_cur_hp, + int32 in_max_hp, + uint8 in_gender, + uint16 in_race, + uint8 in_class, + bodyType in_bodytype, + uint8 in_deity, + uint8 in_level, + uint32 in_npctype_id, + float in_size, + float in_runspeed, + const glm::vec4 &position, + uint8 in_light, + uint8 in_texture, + uint8 in_helmtexture, + uint16 in_ac, + uint16 in_atk, + uint16 in_str, + uint16 in_sta, + uint16 in_dex, + uint16 in_agi, + uint16 in_int, + uint16 in_wis, + uint16 in_cha, + uint8 in_haircolor, + uint8 in_beardcolor, + uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? + uint8 in_eyecolor2, + uint8 in_hairstyle, + uint8 in_luclinface, + uint8 in_beard, + uint32 in_drakkin_heritage, + uint32 in_drakkin_tattoo, + uint32 in_drakkin_details, + EQEmu::TintProfile in_armor_tint, + uint8 in_aa_title, + uint8 in_see_invis, // see through invis/ivu + uint8 in_see_invis_undead, + uint8 in_see_hide, + uint8 in_see_improved_hide, + int32 in_hp_regen, + int32 in_mana_regen, + uint8 in_qglobal, + uint8 in_maxlevel, + uint32 in_scalerate, + uint8 in_armtexture, + uint8 in_bracertexture, + uint8 in_handtexture, + uint8 in_legtexture, + uint8 in_feettexture +) : + attack_timer(2000), + attack_dw_timer(2000), + ranged_timer(2000), + tic_timer(6000), + mana_timer(2000), + spellend_timer(0), + rewind_timer(30000), + bindwound_timer(10000), + stunned_timer(0), + spun_timer(0), + bardsong_timer(6000), + gravity_timer(1000), + viral_timer(0), + m_FearWalkTarget(-999999.0f, -999999.0f, -999999.0f), + m_TargetLocation(glm::vec3()), + m_TargetV(glm::vec3()), + flee_timer(FLEE_CHECK_TIMER), + m_Position(position), + tmHidden(-1), + mitigation_ac(0), + m_specialattacks(eSpecialAttacks::None), + fix_z_timer(300), + fix_z_timer_engaged(100), + attack_anim_timer(1000), + position_update_melee_push_timer(500), + hate_list_cleanup_timer(6000) { - targeted = 0; - tar_ndx = 0; - tar_vector = 0; + + targeted = 0; + tar_ndx = 0; + tar_vector = 0; currently_fleeing = false; last_major_update_position = m_Position; - is_distance_roamer = false; + is_distance_roamer = false; AI_Init(); SetMoving(false); - moved = false; + moved = false; m_RewindLocation = glm::vec3(); _egnode = nullptr; - name[0] = 0; - orig_name[0] = 0; + name[0] = 0; + orig_name[0] = 0; clean_name[0] = 0; - lastname[0] = 0; + lastname[0] = 0; if (in_name) { strn0cpy(name, in_name, 64); strn0cpy(orig_name, in_name, 64); } - if (in_lastname) + if (in_lastname) { strn0cpy(lastname, in_lastname, 64); - cur_hp = in_cur_hp; - max_hp = in_max_hp; - base_hp = in_max_hp; - gender = in_gender; - race = in_race; - base_gender = in_gender; - base_race = in_race; - class_ = in_class; - bodytype = in_bodytype; + } + cur_hp = in_cur_hp; + max_hp = in_max_hp; + base_hp = in_max_hp; + gender = in_gender; + race = in_race; + base_gender = in_gender; + base_race = in_race; + class_ = in_class; + bodytype = in_bodytype; orig_bodytype = in_bodytype; - deity = in_deity; - level = in_level; - orig_level = in_level; - npctype_id = in_npctype_id; - size = in_size; - base_size = size; - runspeed = in_runspeed; + deity = in_deity; + level = in_level; + orig_level = in_level; + npctype_id = in_npctype_id; + size = in_size; + base_size = size; + runspeed = in_runspeed; // neotokyo: sanity check - if (runspeed < 0 || runspeed > 20) + if (runspeed < 0 || runspeed > 20) { runspeed = 1.25f; - base_runspeed = (int)((float)runspeed * 40.0f); + } + base_runspeed = (int) ((float) runspeed * 40.0f); // clients if (runspeed == 0.7f) { - base_runspeed = 28; - walkspeed = 0.3f; + base_runspeed = 28; + walkspeed = 0.3f; base_walkspeed = 12; - fearspeed = 0.625f; + fearspeed = 0.625f; base_fearspeed = 25; // npcs } else { base_walkspeed = base_runspeed * 100 / 265; - walkspeed = ((float)base_walkspeed) * 0.025f; + walkspeed = ((float) base_walkspeed) * 0.025f; base_fearspeed = base_runspeed * 100 / 127; - fearspeed = ((float)base_fearspeed) * 0.025f; + fearspeed = ((float) base_fearspeed) * 0.025f; } last_hp_percent = 0; - last_hp = 0; + last_hp = 0; current_speed = base_runspeed; @@ -188,226 +192,223 @@ Mob::Mob(const char* in_name, // sanity check - if (runspeed < 0 || runspeed > 20) + if (runspeed < 0 || runspeed > 20) { runspeed = 1.25f; + } - m_Light.Type[EQEmu::lightsource::LightInnate] = in_light; + m_Light.Type[EQEmu::lightsource::LightInnate] = in_light; m_Light.Level[EQEmu::lightsource::LightInnate] = EQEmu::lightsource::TypeToLevel(m_Light.Type[EQEmu::lightsource::LightInnate]); - m_Light.Type[EQEmu::lightsource::LightActive] = m_Light.Type[EQEmu::lightsource::LightInnate]; + m_Light.Type[EQEmu::lightsource::LightActive] = m_Light.Type[EQEmu::lightsource::LightInnate]; m_Light.Level[EQEmu::lightsource::LightActive] = m_Light.Level[EQEmu::lightsource::LightInnate]; - texture = in_texture; - helmtexture = in_helmtexture; - armtexture = in_armtexture; + texture = in_texture; + helmtexture = in_helmtexture; + armtexture = in_armtexture; bracertexture = in_bracertexture; - handtexture = in_handtexture; - legtexture = in_legtexture; - feettexture = in_feettexture; - multitexture = (armtexture || bracertexture || handtexture || legtexture || feettexture); + handtexture = in_handtexture; + legtexture = in_legtexture; + feettexture = in_feettexture; + multitexture = (armtexture || bracertexture || handtexture || legtexture || feettexture); - haircolor = in_haircolor; - beardcolor = in_beardcolor; - eyecolor1 = in_eyecolor1; - eyecolor2 = in_eyecolor2; - hairstyle = in_hairstyle; - luclinface = in_luclinface; - beard = in_beard; - drakkin_heritage = in_drakkin_heritage; - drakkin_tattoo = in_drakkin_tattoo; - drakkin_details = in_drakkin_details; - attack_speed = 0; - attack_delay = 0; - slow_mitigation = 0; - findable = false; - trackable = true; - has_shieldequiped = false; + haircolor = in_haircolor; + beardcolor = in_beardcolor; + eyecolor1 = in_eyecolor1; + eyecolor2 = in_eyecolor2; + hairstyle = in_hairstyle; + luclinface = in_luclinface; + beard = in_beard; + drakkin_heritage = in_drakkin_heritage; + drakkin_tattoo = in_drakkin_tattoo; + drakkin_details = in_drakkin_details; + attack_speed = 0; + attack_delay = 0; + slow_mitigation = 0; + findable = false; + trackable = true; + has_shieldequiped = false; has_twohandbluntequiped = false; - has_twohanderequipped = false; - can_facestab = false; - has_numhits = false; - has_MGB = false; - has_ProjectIllusion = false; - SpellPowerDistanceMod = 0; - last_los_check = false; + has_twohanderequipped = false; + can_facestab = false; + has_numhits = false; + has_MGB = false; + has_ProjectIllusion = false; + SpellPowerDistanceMod = 0; + last_los_check = false; - if (in_aa_title > 0) + if (in_aa_title > 0) { aa_title = in_aa_title; - else + } + else { aa_title = 0xFF; - AC = in_ac; - ATK = in_atk; - STR = in_str; - STA = in_sta; - DEX = in_dex; - AGI = in_agi; - INT = in_int; - WIS = in_wis; - CHA = in_cha; - MR = CR = FR = DR = PR = Corrup = 0; + } - ExtraHaste = 0; - bEnraged = false; - - shield_target = nullptr; - current_mana = 0; - max_mana = 0; - hp_regen = in_hp_regen; - mana_regen = in_mana_regen; - ooc_regen = RuleI(NPC, OOCRegen); //default Out of Combat Regen - maxlevel = in_maxlevel; - scalerate = in_scalerate; - invisible = false; - invisible_undead = false; + AC = in_ac; + ATK = in_atk; + STR = in_str; + STA = in_sta; + DEX = in_dex; + AGI = in_agi; + INT = in_int; + WIS = in_wis; + CHA = in_cha; + MR = CR = FR = DR = PR = Corrup = 0; + ExtraHaste = 0; + bEnraged = false; + shield_target = nullptr; + current_mana = 0; + max_mana = 0; + hp_regen = in_hp_regen; + mana_regen = in_mana_regen; + ooc_regen = RuleI(NPC, OOCRegen); //default Out of Combat Regen + maxlevel = in_maxlevel; + scalerate = in_scalerate; + invisible = 0; + invisible_undead = false; invisible_animals = false; - sneaking = false; - hidden = false; - improved_hidden = false; - invulnerable = false; - IsFullHP = (cur_hp == max_hp); - qglobal = 0; - spawned = false; - rare_spawn = false; + sneaking = false; + hidden = false; + improved_hidden = false; + invulnerable = false; + IsFullHP = (cur_hp == max_hp); + qglobal = 0; + spawned = false; + rare_spawn = false; InitializeBuffSlots(); // clear the proc arrays - int i; - int j; - for (j = 0; j < MAX_PROCS; j++) - { - PermaProcs[j].spellID = SPELL_UNKNOWN; - PermaProcs[j].chance = 0; - PermaProcs[j].base_spellID = SPELL_UNKNOWN; - PermaProcs[j].level_override = -1; - SpellProcs[j].spellID = SPELL_UNKNOWN; - SpellProcs[j].chance = 0; - SpellProcs[j].base_spellID = SPELL_UNKNOWN; - SpellProcs[j].level_override = -1; - DefensiveProcs[j].spellID = SPELL_UNKNOWN; - DefensiveProcs[j].chance = 0; - DefensiveProcs[j].base_spellID = SPELL_UNKNOWN; + for (int j = 0; j < MAX_PROCS; j++) { + PermaProcs[j].spellID = SPELL_UNKNOWN; + PermaProcs[j].chance = 0; + PermaProcs[j].base_spellID = SPELL_UNKNOWN; + PermaProcs[j].level_override = -1; + SpellProcs[j].spellID = SPELL_UNKNOWN; + SpellProcs[j].chance = 0; + SpellProcs[j].base_spellID = SPELL_UNKNOWN; + SpellProcs[j].level_override = -1; + DefensiveProcs[j].spellID = SPELL_UNKNOWN; + DefensiveProcs[j].chance = 0; + DefensiveProcs[j].base_spellID = SPELL_UNKNOWN; DefensiveProcs[j].level_override = -1; - RangedProcs[j].spellID = SPELL_UNKNOWN; - RangedProcs[j].chance = 0; - RangedProcs[j].base_spellID = SPELL_UNKNOWN; - RangedProcs[j].level_override = -1; + RangedProcs[j].spellID = SPELL_UNKNOWN; + RangedProcs[j].chance = 0; + RangedProcs[j].base_spellID = SPELL_UNKNOWN; + RangedProcs[j].level_override = -1; } - for (i = EQEmu::textures::textureBegin; i < EQEmu::textures::materialCount; i++) - { + for (int i = EQEmu::textures::textureBegin; i < EQEmu::textures::materialCount; i++) { armor_tint.Slot[i].Color = in_armor_tint.Slot[i].Color; } std::fill(std::begin(m_spellHitsLeft), std::end(m_spellHitsLeft), 0); - m_Delta = glm::vec4(); + m_Delta = glm::vec4(); animation = 0; - logging_enabled = false; - isgrouped = false; + isgrouped = false; israidgrouped = false; IsHorse = false; entity_id_being_looted = 0; - _appearance = eaStanding; - pRunAnimSpeed = 0; + _appearance = eaStanding; + pRunAnimSpeed = 0; spellend_timer.Disable(); bardsong_timer.Disable(); - bardsong = 0; - bardsong_target_id = 0; - casting_spell_id = 0; - casting_spell_timer = 0; + bardsong = 0; + bardsong_target_id = 0; + casting_spell_id = 0; + casting_spell_timer = 0; casting_spell_timer_duration = 0; casting_spell_inventory_slot = 0; - casting_spell_aa_id = 0; - target = 0; + casting_spell_aa_id = 0; + target = 0; ActiveProjectileATK = false; - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) - { - ProjectileAtk[i].increment = 0; + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { + ProjectileAtk[i].increment = 0; ProjectileAtk[i].hit_increment = 0; - ProjectileAtk[i].target_id = 0; - ProjectileAtk[i].wpn_dmg = 0; - ProjectileAtk[i].origin_x = 0.0f; - ProjectileAtk[i].origin_y = 0.0f; - ProjectileAtk[i].origin_z = 0.0f; - ProjectileAtk[i].tlast_x = 0.0f; - ProjectileAtk[i].tlast_y = 0.0f; - ProjectileAtk[i].ranged_id = 0; - ProjectileAtk[i].ammo_id = 0; - ProjectileAtk[i].ammo_slot = 0; - ProjectileAtk[i].skill = 0; - ProjectileAtk[i].speed_mod = 0.0f; + ProjectileAtk[i].target_id = 0; + ProjectileAtk[i].wpn_dmg = 0; + ProjectileAtk[i].origin_x = 0.0f; + ProjectileAtk[i].origin_y = 0.0f; + ProjectileAtk[i].origin_z = 0.0f; + ProjectileAtk[i].tlast_x = 0.0f; + ProjectileAtk[i].tlast_y = 0.0f; + ProjectileAtk[i].ranged_id = 0; + ProjectileAtk[i].ammo_id = 0; + ProjectileAtk[i].ammo_slot = 0; + ProjectileAtk[i].skill = 0; + ProjectileAtk[i].speed_mod = 0.0f; } memset(&itembonuses, 0, sizeof(StatBonuses)); memset(&spellbonuses, 0, sizeof(StatBonuses)); memset(&aabonuses, 0, sizeof(StatBonuses)); - spellbonuses.AggroRange = -1; + spellbonuses.AggroRange = -1; spellbonuses.AssistRange = -1; pLastChange = 0; SetPetID(0); SetOwnerID(0); - typeofpet = petNone; // default to not a pet - petpower = 0; - held = false; - gheld = false; - nocast = false; - focused = false; - pet_stop = false; - pet_regroup = false; - _IsTempPet = false; - pet_owner_client = false; + typeofpet = petNone; // default to not a pet + petpower = 0; + held = false; + gheld = false; + nocast = false; + focused = false; + pet_stop = false; + pet_regroup = false; + _IsTempPet = false; + pet_owner_client = false; pet_targetlock_id = 0; attacked_count = 0; - mezzed = false; - stunned = false; - silenced = false; - amnesiad = false; - inWater = false; + mezzed = false; + stunned = false; + silenced = false; + amnesiad = false; + inWater = false; int m; - for (m = 0; m < MAX_SHIELDERS; m++) - { - shielder[m].shielder_id = 0; + for (m = 0; m < MAX_SHIELDERS; m++) { + shielder[m].shielder_id = 0; shielder[m].shielder_bonus = 0; } destructibleobject = false; - wandertype = 0; - pausetype = 0; - cur_wp = 0; - m_CurrentWayPoint = glm::vec4(); - cur_wp_pause = 0; - patrol = 0; - follow_id = 0; - follow_dist = 100; // Default Distance for Follow - follow_run = true; // We can run if distance great enough - no_target_hotkey = false; - flee_mode = false; - currently_fleeing = false; + wandertype = 0; + pausetype = 0; + cur_wp = 0; + m_CurrentWayPoint = glm::vec4(); + cur_wp_pause = 0; + patrol = 0; + follow_id = 0; + follow_dist = 100; // Default Distance for Follow + follow_run = true; // We can run if distance great enough + no_target_hotkey = false; + flee_mode = false; + currently_fleeing = false; flee_timer.Start(); permarooted = (runspeed > 0) ? false : true; movetimercompleted = false; - ForcedMovement = 0; - roamer = false; - rooted = false; - charmed = false; - has_virus = false; - for (i = 0; i < MAX_SPELL_TRIGGER * 2; i++) { + ForcedMovement = 0; + roamer = false; + rooted = false; + charmed = false; + has_virus = false; + + for (int i = 0; i < MAX_SPELL_TRIGGER * 2; i++) { viral_spells[i] = 0; } - pStandingPetOrder = SPO_Follow; - pseudo_rooted = false; - see_invis = GetSeeInvisible(in_see_invis); - see_invis_undead = GetSeeInvisible(in_see_invis_undead); - see_hide = GetSeeInvisible(in_see_hide); + pStandingPetOrder = SPO_Follow; + pseudo_rooted = false; + + see_invis = GetSeeInvisible(in_see_invis); + see_invis_undead = GetSeeInvisible(in_see_invis_undead); + see_hide = GetSeeInvisible(in_see_hide); see_improved_hide = GetSeeInvisible(in_see_improved_hide); qglobal = in_qglobal != 0; @@ -416,12 +417,12 @@ Mob::Mob(const char* in_name, bindwound_timer.Disable(); bindwound_target = 0; - trade = new Trade(this); + trade = new Trade(this); // hp event - nexthpevent = -1; + nexthpevent = -1; nextinchpevent = -1; - hasTempPet = false; + hasTempPet = false; count_TempPet = 0; m_is_running = false; @@ -429,25 +430,31 @@ Mob::Mob(const char* in_name, nimbus_effect1 = 0; nimbus_effect2 = 0; nimbus_effect3 = 0; - m_targetable = true; + m_targetable = true; m_TargetRing = glm::vec3(); - flymode = FlyMode3; + flymode = FlyMode3; DistractedFromGrid = false; hate_list.SetHateOwner(this); m_AllowBeneficial = false; - m_DisableMelee = false; - for (int i = 0; i < EQEmu::skills::HIGHEST_SKILL + 2; i++) { SkillDmgTaken_Mod[i] = 0; } - for (int i = 0; i < HIGHEST_RESIST + 2; i++) { Vulnerability_Mod[i] = 0; } + m_DisableMelee = false; - emoteid = 0; - endur_upkeep = false; + for (int i = 0; i < EQEmu::skills::HIGHEST_SKILL + 2; i++) { + SkillDmgTaken_Mod[i] = 0; + } + + for (int i = 0; i < HIGHEST_RESIST + 2; i++) { + Vulnerability_Mod[i] = 0; + } + + emoteid = 0; + endur_upkeep = false; degenerating_effects = false; - PrimaryAggro = false; - AssistAggro = false; - npc_assist_cap = 0; + PrimaryAggro = false; + AssistAggro = false; + npc_assist_cap = 0; PathRecalcTimer.reset(new Timer(500)); PathingLoopCount = 0; @@ -830,6 +837,7 @@ int32 Mob::CalcMaxMana() { int32 Mob::CalcMaxHP() { max_hp = (base_hp + itembonuses.HP + spellbonuses.HP); max_hp += max_hp * ((aabonuses.MaxHPChange + spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f); + return max_hp; } @@ -3466,24 +3474,31 @@ int Mob::GetHaste() return 100 + h; } -void Mob::SetTarget(Mob* mob) { - - if (target == mob) +void Mob::SetTarget(Mob *mob) +{ + if (target == mob) { return; + } target = mob; entity_list.UpdateHoTT(this); - - if(IsNPC()) + + if (IsNPC()) { parse->EventNPC(EVENT_TARGET_CHANGE, CastToNPC(), mob, "", 0); - else if (IsClient()) + } + else if (IsClient()) { parse->EventPlayer(EVENT_TARGET_CHANGE, CastToClient(), "", 0); - if(IsPet() && GetOwner() && GetOwner()->IsClient()) - GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob); + this->DisplayInfo(mob); + } - if (this->IsClient() && this->GetTarget() && this->CastToClient()->hp_other_update_throttle_timer.Check()) + if (IsPet() && GetOwner() && GetOwner()->IsClient()) { + GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob); + } + + if (this->IsClient() && this->GetTarget() && this->CastToClient()->hp_other_update_throttle_timer.Check()) { this->GetTarget()->SendHPUpdate(false, true); + } } // For when we want a Ground Z at a location we are not at yet @@ -5553,14 +5568,26 @@ bool Mob::HasSpellEffect(int effectid) return(0); } -int Mob::GetSpecialAbility(int ability) { - if(ability >= MAX_SPECIAL_ATTACK || ability < 0) { +int Mob::GetSpecialAbility(int ability) +{ + if (ability >= MAX_SPECIAL_ATTACK || ability < 0) { return 0; } return SpecialAbilities[ability].level; } +bool Mob::HasSpecialAbilities() +{ + for (int i = 0; i < MAX_SPECIAL_ATTACK; ++i) { + if (GetSpecialAbility(i)) { + return true; + } + } + + return false; +} + int Mob::GetSpecialAbilityParam(int ability, int param) { if(param >= MAX_SPECIAL_ATTACK_PARAMS || param < 0 || ability >= MAX_SPECIAL_ATTACK || ability < 0) { return 0; diff --git a/zone/mob.h b/zone/mob.h index 59b4a5079..120b89539 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -166,6 +166,10 @@ public: bool is_distance_roamer; + void DisplayInfo(Mob *mob); + +public: + //Somewhat sorted: needs documenting! //Attack @@ -1049,6 +1053,7 @@ public: inline bool Sanctuary() const { return spellbonuses.Sanctuary; } bool HasNPCSpecialAtk(const char* parse); + bool HasSpecialAbilities(); int GetSpecialAbility(int ability); int GetSpecialAbilityParam(int ability, int param); void SetSpecialAbility(int ability, int level); @@ -1146,6 +1151,8 @@ public: int GetWeaponDamage(Mob *against, const EQEmu::ItemData *weapon_item); int GetWeaponDamage(Mob *against, const EQEmu::ItemInstance *weapon_item, uint32 *hate = nullptr); + int32 GetHPRegen() const; + // Bots HealRotation methods #ifdef BOTS bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); } @@ -1476,7 +1483,6 @@ protected: bool pAIControlled; bool roamer; - bool logging_enabled; int wandertype; int pausetype; diff --git a/zone/mob_info.cpp b/zone/mob_info.cpp new file mode 100644 index 000000000..7ae249393 --- /dev/null +++ b/zone/mob_info.cpp @@ -0,0 +1,367 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2018 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY except by those people which sell it, which + * are required to give you total support for your newly bought product; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "client.h" +#include "mob.h" +#include "../common/races.h" +#include "../common/say_link.h" + +inline std::string GetMobAttributeByString(Mob *mob, const std::string &attribute) +{ + if (attribute == "ac") { + return std::to_string(mob->GetAC()); + } + + if (attribute == "atk") { + return std::to_string(mob->GetATK()); + } + + if (attribute == "end") { + int endurance = 0; + if (mob->IsClient()) { + endurance = mob->CastToClient()->GetEndurance(); + } + + return std::to_string(endurance); + } + + if (attribute == "hp") { + return std::to_string(mob->GetHP()); + } + + if (attribute == "mana") { + return std::to_string(mob->GetMana()); + } + + if (attribute == "str") { + return std::to_string(mob->GetSTR()); + } + + if (attribute == "sta") { + return std::to_string(mob->GetSTA()); + } + + if (attribute == "dex") { + return std::to_string(mob->GetDEX()); + } + + if (attribute == "agi") { + return std::to_string(mob->GetAGI()); + } + + if (attribute == "int") { + return std::to_string(mob->GetINT()); + } + + if (attribute == "wis") { + return std::to_string(mob->GetWIS()); + } + + if (attribute == "cha") { + return std::to_string(mob->GetCHA()); + } + + if (attribute == "mr") { + return std::to_string(mob->GetMR()); + } + + if (attribute == "cr") { + return std::to_string(mob->GetCR()); + } + + if (attribute == "fr") { + return std::to_string(mob->GetFR()); + } + + if (attribute == "pr") { + return std::to_string(mob->GetPR()); + } + + if (attribute == "dr") { + return std::to_string(mob->GetDR()); + } + + if (attribute == "cr") { + return std::to_string(mob->GetCR()); + } + + if (attribute == "pr") { + return std::to_string(mob->GetPR()); + } + + if (attribute == "cor") { + return std::to_string(mob->GetCorrup()); + } + + if (attribute == "phy") { + return std::to_string(mob->GetPhR()); + } + + if (attribute == "name") { + return mob->GetCleanName(); + } + + if (attribute == "lastname") { + return mob->GetLastName(); + } + + if (attribute == "race") { + return GetRaceIDName(mob->GetRace()); + } + + if (attribute == "class") { + return GetClassIDName(mob->GetClass(), 0); + } + + if (attribute == "level") { + return std::to_string(mob->GetLevel()); + } + + if (mob->IsNPC()) { + NPC *npc = mob->CastToNPC(); + + if (attribute == "npcid") { + return std::to_string(npc->GetNPCTypeID()); + } + if (attribute == "texture") { + return std::to_string(npc->GetTexture()); + } + if (attribute == "bodytype") { + return std::to_string(npc->GetBodyType()); + } + if (attribute == "gender") { + return std::to_string(npc->GetGender()); + } + if (attribute == "size") { + return std::to_string(npc->GetSize()); + } + if (attribute == "runspeed") { + return std::to_string(npc->GetRunspeed()); + } + if (attribute == "walkspeed") { + return std::to_string(npc->GetWalkspeed()); + } + if (attribute == "spawngroup") { + return std::to_string(npc->GetSp2()); + } + if (attribute == "grid") { + return std::to_string(npc->GetGrid()); + } + if (attribute == "emote") { + return std::to_string(npc->GetEmoteID()); + } + npc->GetNPCEmote(npc->GetEmoteID(), 0); + } + + if (attribute == "type") { + std::string entity_type = "Mob"; + + if (mob->IsCorpse()) { + entity_type = "Corpse"; + } + + if (mob->IsNPC()) { + entity_type = "NPC"; + } + + if (mob->IsClient()) { + entity_type = "Client"; + } + + return entity_type; + } + + return "null"; +} + +inline std::string WriteDisplayInfoSection( + Mob *mob, + const std::string §ion_name, + std::vector attributes_list, + int column_count = 3, + bool display_section_name = false +) +{ + std::string text; + + if (display_section_name) { + text += "" + section_name + "
"; + } + + text += ""; + + int index = 0; + bool first_row = true; + + for (const auto &attribute : attributes_list) { + if (index == 0) { + if (first_row) { + text += "\n"; + first_row = false; + } + else { + text += "\n"; + } + + } + + std::string attribute_name = attribute; + if (attribute_name.length() <= 3) { + attribute_name = str_toupper(attribute_name); + } + if (attribute_name.length() > 3) { + attribute_name = ucfirst(attribute_name); + } + + std::string attribute_value = GetMobAttributeByString(mob, attribute); + + if (attribute_value.length() <= 0) { + continue; + } + + text += ""; + + if (index == column_count) { + index = 0; + continue; + } + + index++; + } + + text += "
" + attribute_name + "" + GetMobAttributeByString(mob, attribute) + "
"; + + return text; +} + +inline void NPCCommandsMenu(Client* client, NPC* npc) +{ + std::string menu_commands; + + if (npc->GetGrid() > 0) { + menu_commands += EQEmu::SayLinkEngine::GenerateQuestSaylink("#grid show", false, "Grid Points") + " "; + } + + if (npc->GetEmoteID() > 0) { + std::string saylink = StringFormat("#emotesearch %u", npc->GetEmoteID()); + menu_commands += EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Emotes") + " "; + } + + if (menu_commands.length() > 0) { + client->Message(0, "# Show Commmands"); + client->Message(0, " - %s", menu_commands.c_str()); + } +} + +void Mob::DisplayInfo(Mob *mob) +{ + if (!this || !mob) { + return; + } + + // std::vector general_stats = { +// + // // "accuracy", + // // "slow_mitigation", + // // "atk", + // // "min_hit", + // // "max_hit", + // // "hp_regen", + // // "attack_delay", + // // "special_abilities" + // }; + + if (this->IsClient()) { + std::string window_text = "*Drag / Maximize Window to see all info

"; + + Client *client = this->CastToClient(); + + std::vector who_attributes = { + "name", + "lastname", + }; + window_text += WriteDisplayInfoSection(mob, "Who", who_attributes, 1, false); + + std::vector type_attributes = { + "race", + "class", + "type" + }; + window_text += WriteDisplayInfoSection(mob, "Type", type_attributes, 3, true); + + std::vector basic_attributes = { + "level", + "hp", + "mana", + "end", + "ac", + "atk" + }; + window_text += WriteDisplayInfoSection(mob, "Main", basic_attributes, 7, true); + + std::vector stat_attributes = { + "str", + "sta", + "agi", + "dex", + "wis", + "int", + "cha", + }; + window_text += WriteDisplayInfoSection(mob, "Statistics", stat_attributes, 7, true); + + std::vector resist_attributes = { + "pr", + "mr", + "dr", + "fr", + "cr", + "cor", + "phy", + }; + window_text += WriteDisplayInfoSection(mob, "Resists", resist_attributes, 7, true); + + if (mob->IsNPC()) { + std::vector npc_attributes = { + "npcid", + "texture", + "bodytype", + "gender", + "size", + "runspeed", + "walkspeed", + "spawngroup", + "grid", + "emote", + }; + window_text += WriteDisplayInfoSection(mob, "NPC Attributes", npc_attributes, 2, true); + + client->Message(0, " "); + mob->CastToNPC()->QueryLoot(client); + + NPCCommandsMenu(client, mob->CastToNPC()); + } + + std::cout << "Window Length: " << window_text.length() << std::endl; + // std::cout << "Window " << window_text << std::endl; + + client->SendPopupToClient("Entity Info", window_text.c_str()); + } +} diff --git a/zone/net.cpp b/zone/net.cpp index f51d50bb4..6f0bfbf9a 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -42,7 +42,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/spdat.h" #include "../common/eqemu_logsys.h" - #include "zone_config.h" #include "masterentity.h" #include "worldserver.h" @@ -62,6 +61,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "embparser.h" #include "lua_parser.h" #include "questmgr.h" +#include "npc_scale_manager.h" #include "../common/event/event_loop.h" #include "../common/event/timer.h" @@ -104,6 +104,7 @@ npcDecayTimes_Struct npcCorpseDecayTimes[100]; TitleManager title_manager; QueryServ *QServ = 0; TaskManager *taskmanager = 0; +NpcScaleManager *npc_scale_manager; QuestParserCollection *parse = 0; EQEmuLogSys LogSys; const SPDat_Spell_Struct* spells; @@ -222,7 +223,6 @@ int main(int argc, char** argv) { worldserver.SetLauncherName("NONE"); } - Log(Logs::General, Logs::Zone_Server, "Connecting to MySQL..."); if (!database.Connect( Config->DatabaseHost.c_str(), @@ -255,6 +255,12 @@ int main(int argc, char** argv) { guild_mgr.SetDatabase(&database); GuildBanks = nullptr; + /** + * NPC Scale Manager + */ + npc_scale_manager = new NpcScaleManager; + npc_scale_manager->LoadScaleData(); + #ifdef _EQDEBUG _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif diff --git a/zone/npc.cpp b/zone/npc.cpp index 2b1af6de9..bd5de40b1 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -39,6 +39,7 @@ #include "spawn2.h" #include "zone.h" #include "quest_parser_collection.h" +#include "npc_scale_manager.h" #include #include @@ -57,250 +58,237 @@ extern Zone* zone; extern volatile bool is_zone_loaded; extern EntityList entity_list; -NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int iflymode, bool IsCorpse) -: Mob(d->name, - d->lastname, - d->max_hp, - d->max_hp, - d->gender, - d->race, - d->class_, - (bodyType)d->bodytype, - d->deity, - d->level, - d->npc_id, - d->size, - d->runspeed, - position, - d->light, // innate_light - d->texture, - d->helmtexture, - d->AC, - d->ATK, - d->STR, - d->STA, - d->DEX, - d->AGI, - d->INT, - d->WIS, - d->CHA, - d->haircolor, - d->beardcolor, - d->eyecolor1, - d->eyecolor2, - d->hairstyle, - d->luclinface, - d->beard, - d->drakkin_heritage, - d->drakkin_tattoo, - d->drakkin_details, - d->armor_tint, - 0, - d->see_invis, // pass see_invis/see_ivu flags to mob constructor - d->see_invis_undead, - d->see_hide, - d->see_improved_hide, - d->hp_regen, - d->mana_regen, - d->qglobal, - d->maxlevel, - d->scalerate, - d->armtexture, - d->bracertexture, - d->handtexture, - d->legtexture, - d->feettexture), - attacked_timer(CombatEventTimer_expire), - swarm_timer(100), - classattack_timer(1000), - knightattack_timer(1000), - assist_timer(AIassistcheck_delay), - qglobal_purge_timer(30000), - send_hp_update_timer(2000), - enraged_timer(1000), - taunt_timer(TauntReuseTime * 1000), - m_SpawnPoint(position), - m_GuardPoint(-1,-1,-1,0), - m_GuardPointSaved(0,0,0,0) +NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &position, int iflymode, bool IsCorpse) + : Mob( + npc_type_data->name, + npc_type_data->lastname, + npc_type_data->max_hp, + npc_type_data->max_hp, + npc_type_data->gender, + npc_type_data->race, + npc_type_data->class_, + (bodyType) npc_type_data->bodytype, + npc_type_data->deity, + npc_type_data->level, + npc_type_data->npc_id, + npc_type_data->size, + npc_type_data->runspeed, + position, + npc_type_data->light, // innate_light + npc_type_data->texture, + npc_type_data->helmtexture, + npc_type_data->AC, + npc_type_data->ATK, + npc_type_data->STR, + npc_type_data->STA, + npc_type_data->DEX, + npc_type_data->AGI, + npc_type_data->INT, + npc_type_data->WIS, + npc_type_data->CHA, + npc_type_data->haircolor, + npc_type_data->beardcolor, + npc_type_data->eyecolor1, + npc_type_data->eyecolor2, + npc_type_data->hairstyle, + npc_type_data->luclinface, + npc_type_data->beard, + npc_type_data->drakkin_heritage, + npc_type_data->drakkin_tattoo, + npc_type_data->drakkin_details, + npc_type_data->armor_tint, + 0, + npc_type_data->see_invis, // pass see_invis/see_ivu flags to mob constructor + npc_type_data->see_invis_undead, + npc_type_data->see_hide, + npc_type_data->see_improved_hide, + npc_type_data->hp_regen, + npc_type_data->mana_regen, + npc_type_data->qglobal, + npc_type_data->maxlevel, + npc_type_data->scalerate, + npc_type_data->armtexture, + npc_type_data->bracertexture, + npc_type_data->handtexture, + npc_type_data->legtexture, + npc_type_data->feettexture +), + attacked_timer(CombatEventTimer_expire), + swarm_timer(100), + classattack_timer(1000), + knightattack_timer(1000), + assist_timer(AIassistcheck_delay), + qglobal_purge_timer(30000), + send_hp_update_timer(2000), + enraged_timer(1000), + taunt_timer(TauntReuseTime * 1000), + m_SpawnPoint(position), + m_GuardPoint(-1, -1, -1, 0), + m_GuardPointSaved(0, 0, 0, 0) { //What is the point of this, since the names get mangled.. - Mob* mob = entity_list.GetMob(name); - if(mob != 0) + Mob *mob = entity_list.GetMob(name); + if (mob != nullptr) { entity_list.RemoveEntity(mob->GetID()); + } - int moblevel=GetLevel(); + int moblevel = GetLevel(); - NPCTypedata = d; + NPCTypedata = npc_type_data; NPCTypedata_ours = nullptr; - respawn2 = in_respawn; + respawn2 = in_respawn; + swarm_timer.Disable(); - if (size <= 0.0f) + if (size <= 0.0f) { size = GetRaceGenderDefaultHeight(race, gender); + } - taunting = false; - proximity = nullptr; - copper = 0; - silver = 0; - gold = 0; - platinum = 0; - max_dmg = d->max_dmg; - min_dmg = d->min_dmg; - attack_count = d->attack_count; - grid = 0; - wp_m = 0; - max_wp=0; - save_wp = 0; - spawn_group = 0; + taunting = false; + proximity = nullptr; + copper = 0; + silver = 0; + gold = 0; + platinum = 0; + max_dmg = npc_type_data->max_dmg; + min_dmg = npc_type_data->min_dmg; + attack_count = npc_type_data->attack_count; + grid = 0; + wp_m = 0; + max_wp = 0; + save_wp = 0; + spawn_group = 0; swarmInfoPtr = nullptr; - spellscale = d->spellscale; - healscale = d->healscale; - - logging_enabled = NPC_DEFAULT_LOGGING_ENABLED; - - pAggroRange = d->aggroradius; - pAssistRange = d->assistradius; - findable = d->findable; - trackable = d->trackable; - - MR = d->MR; - CR = d->CR; - DR = d->DR; - FR = d->FR; - PR = d->PR; - Corrup = d->Corrup; - PhR = d->PhR; - - STR = d->STR; - STA = d->STA; - AGI = d->AGI; - DEX = d->DEX; - INT = d->INT; - WIS = d->WIS; - CHA = d->CHA; - npc_mana = d->Mana; + spellscale = npc_type_data->spellscale; + healscale = npc_type_data->healscale; + pAggroRange = npc_type_data->aggroradius; + pAssistRange = npc_type_data->assistradius; + findable = npc_type_data->findable; + trackable = npc_type_data->trackable; + MR = npc_type_data->MR; + CR = npc_type_data->CR; + DR = npc_type_data->DR; + FR = npc_type_data->FR; + PR = npc_type_data->PR; + Corrup = npc_type_data->Corrup; + PhR = npc_type_data->PhR; + STR = npc_type_data->STR; + STA = npc_type_data->STA; + AGI = npc_type_data->AGI; + DEX = npc_type_data->DEX; + INT = npc_type_data->INT; + WIS = npc_type_data->WIS; + CHA = npc_type_data->CHA; + npc_mana = npc_type_data->Mana; //quick fix of ordering if they screwed it up in the DB - if(max_dmg < min_dmg) { + if (max_dmg < min_dmg) { int tmp = min_dmg; min_dmg = max_dmg; max_dmg = tmp; } // Max Level and Stat Scaling if maxlevel is set - if(maxlevel > level) - { + if (maxlevel > level) { LevelScale(); } - // Set Resists if they are 0 in the DB - CalcNPCResists(); - - // Set Mana and HP Regen Rates if they are 0 in the DB - CalcNPCRegen(); - - // Set Min and Max Damage if they are 0 in the DB - if(max_dmg == 0){ - CalcNPCDamage(); - } - - base_damage = round((max_dmg - min_dmg) / 1.9); - min_damage = min_dmg - round(base_damage / 10.0); - - accuracy_rating = d->accuracy_rating; - avoidance_rating = d->avoidance_rating; - ATK = d->ATK; + base_damage = round((max_dmg - min_dmg) / 1.9); + min_damage = min_dmg - round(base_damage / 10.0); + accuracy_rating = npc_type_data->accuracy_rating; + avoidance_rating = npc_type_data->avoidance_rating; + ATK = npc_type_data->ATK; // used for when switch back to charm - default_ac = d->AC; - default_min_dmg = min_dmg; - default_max_dmg = max_dmg; - default_attack_delay = d->attack_delay; - default_accuracy_rating = d->accuracy_rating; - default_avoidance_rating = d->avoidance_rating; - default_atk = d->ATK; + default_ac = npc_type_data->AC; + default_min_dmg = min_dmg; + default_max_dmg = max_dmg; + default_attack_delay = npc_type_data->attack_delay; + default_accuracy_rating = npc_type_data->accuracy_rating; + default_avoidance_rating = npc_type_data->avoidance_rating; + default_atk = npc_type_data->ATK; // used for when getting charmed, if 0, doesn't swap - charm_ac = d->charm_ac; - charm_min_dmg = d->charm_min_dmg; - charm_max_dmg = d->charm_max_dmg; - charm_attack_delay = d->charm_attack_delay; - charm_accuracy_rating = d->charm_accuracy_rating; - charm_avoidance_rating = d->charm_avoidance_rating; - charm_atk = d->charm_atk; + charm_ac = npc_type_data->charm_ac; + charm_min_dmg = npc_type_data->charm_min_dmg; + charm_max_dmg = npc_type_data->charm_max_dmg; + charm_attack_delay = npc_type_data->charm_attack_delay; + charm_accuracy_rating = npc_type_data->charm_accuracy_rating; + charm_avoidance_rating = npc_type_data->charm_avoidance_rating; + charm_atk = npc_type_data->charm_atk; CalcMaxMana(); SetMana(GetMaxMana()); - MerchantType = d->merchanttype; - merchant_open = GetClass() == MERCHANT; - adventure_template_id = d->adventure_template; - flymode = iflymode; - guard_anim = eaStanding; - roambox_distance = 0; - roambox_max_x = -2; - roambox_max_y = -2; - roambox_min_x = -2; - roambox_min_y = -2; + MerchantType = npc_type_data->merchanttype; + merchant_open = GetClass() == MERCHANT; + adventure_template_id = npc_type_data->adventure_template; + flymode = iflymode; + guard_anim = eaStanding; + roambox_distance = 0; + roambox_max_x = -2; + roambox_max_y = -2; + roambox_min_x = -2; + roambox_min_y = -2; roambox_destination_x = -2; roambox_destination_y = -2; - roambox_min_delay = 1000; - roambox_delay = 1000; - p_depop = false; - loottable_id = d->loottable_id; - skip_global_loot = d->skip_global_loot; - rare_spawn = d->rare_spawn; + roambox_min_delay = 1000; + roambox_delay = 1000; + p_depop = false; + loottable_id = npc_type_data->loottable_id; + skip_global_loot = npc_type_data->skip_global_loot; + rare_spawn = npc_type_data->rare_spawn; + no_target_hotkey = npc_type_data->no_target_hotkey; + primary_faction = 0; - no_target_hotkey = d->no_target_hotkey; + SetNPCFactionID(npc_type_data->npc_faction_id); - primary_faction = 0; - SetNPCFactionID(d->npc_faction_id); - - npc_spells_id = 0; - HasAISpell = false; - HasAISpellEffects = false; + npc_spells_id = 0; + HasAISpell = false; + HasAISpellEffects = false; innate_proc_spell_id = 0; - if(GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs)) - { + if (GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs)) { LoadMercTypes(); LoadMercs(); } - SpellFocusDMG = 0; + SpellFocusDMG = 0; SpellFocusHeal = 0; pet_spell_id = 0; - delaytimer = false; - combat_event = false; - attack_speed = d->attack_speed; - attack_delay = d->attack_delay; - slow_mitigation = d->slow_mitigation; + delaytimer = false; + combat_event = false; + attack_speed = npc_type_data->attack_speed; + attack_delay = npc_type_data->attack_delay; + slow_mitigation = npc_type_data->slow_mitigation; EntityList::RemoveNumbers(name); entity_list.MakeNameUnique(name); - npc_aggro = d->npc_aggro; + npc_aggro = npc_type_data->npc_aggro; AI_Init(); AI_Start(); - d_melee_texture1 = d->d_melee_texture1; - d_melee_texture2 = d->d_melee_texture2; - herosforgemodel = d->herosforgemodel; + d_melee_texture1 = npc_type_data->d_melee_texture1; + d_melee_texture2 = npc_type_data->d_melee_texture2; + herosforgemodel = npc_type_data->herosforgemodel; - ammo_idfile = d->ammo_idfile; + ammo_idfile = npc_type_data->ammo_idfile; memset(equipment, 0, sizeof(equipment)); - prim_melee_type = d->prim_melee_type; - sec_melee_type = d->sec_melee_type; - ranged_type = d->ranged_type; + prim_melee_type = npc_type_data->prim_melee_type; + sec_melee_type = npc_type_data->sec_melee_type; + ranged_type = npc_type_data->ranged_type; // If Melee Textures are not set, set attack type to Hand to Hand as default - if(!d_melee_texture1) + if (!d_melee_texture1) { prim_melee_type = 28; - if(!d_melee_texture2) + } + if (!d_melee_texture2) { sec_melee_type = 28; + } //give NPCs skill values... int r; @@ -312,106 +300,87 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if // the theory of needing to be able to set skills for each mob separately if (moblevel > 50) { skills[EQEmu::skills::SkillDoubleAttack] = 250; - skills[EQEmu::skills::SkillDualWield] = 250; - } else if (moblevel > 3) { + skills[EQEmu::skills::SkillDualWield] = 250; + } + else if (moblevel > 3) { skills[EQEmu::skills::SkillDoubleAttack] = moblevel * 5; - skills[EQEmu::skills::SkillDualWield] = skills[EQEmu::skills::SkillDoubleAttack]; - } else { + skills[EQEmu::skills::SkillDualWield] = skills[EQEmu::skills::SkillDoubleAttack]; + } + else { skills[EQEmu::skills::SkillDoubleAttack] = moblevel * 5; } - if(d->trap_template > 0) - { - std::map >::iterator trap_ent_iter; - std::list trap_list; + ldon_trapped = false; + ldon_trap_type = 0; + ldon_spell_id = 0; + ldon_locked = false; + ldon_locked_skill = 0; + ldon_trap_detected = false; - trap_ent_iter = zone->ldon_trap_entry_list.find(d->trap_template); - if(trap_ent_iter != zone->ldon_trap_entry_list.end()) - { + if (npc_type_data->trap_template > 0) { + std::map >::iterator trap_ent_iter; + std::list trap_list; + + trap_ent_iter = zone->ldon_trap_entry_list.find(npc_type_data->trap_template); + if (trap_ent_iter != zone->ldon_trap_entry_list.end()) { trap_list = trap_ent_iter->second; - if(trap_list.size() > 0) - { + if (trap_list.size() > 0) { auto trap_list_iter = trap_list.begin(); std::advance(trap_list_iter, zone->random.Int(0, trap_list.size() - 1)); - LDoNTrapTemplate* tt = (*trap_list_iter); - if(tt) - { - if((uint8)tt->spell_id > 0) - { - ldon_trapped = true; - ldon_spell_id = tt->spell_id; + LDoNTrapTemplate *trap_template = (*trap_list_iter); + if (trap_template) { + if ((uint8) trap_template->spell_id > 0) { + ldon_trapped = true; + ldon_spell_id = trap_template->spell_id; } - else - { - ldon_trapped = false; + else { + ldon_trapped = false; ldon_spell_id = 0; } - ldon_trap_type = (uint8)tt->type; - if(tt->locked > 0) - { - ldon_locked = true; - ldon_locked_skill = tt->skill; + ldon_trap_type = (uint8) trap_template->type; + if (trap_template->locked > 0) { + ldon_locked = true; + ldon_locked_skill = trap_template->skill; } - else - { - ldon_locked = false; + else { + ldon_locked = false; ldon_locked_skill = 0; } + ldon_trap_detected = 0; } } - else - { - ldon_trapped = false; - ldon_trap_type = 0; - ldon_spell_id = 0; - ldon_locked = false; - ldon_locked_skill = 0; - ldon_trap_detected = 0; - } - } - else - { - ldon_trapped = false; - ldon_trap_type = 0; - ldon_spell_id = 0; - ldon_locked = false; - ldon_locked_skill = 0; - ldon_trap_detected = 0; } } - else - { - ldon_trapped = false; - ldon_trap_type = 0; - ldon_spell_id = 0; - ldon_locked = false; - ldon_locked_skill = 0; - ldon_trap_detected = 0; - } + reface_timer = new Timer(15000); reface_timer->Disable(); + qGlobals = nullptr; - SetEmoteID(d->emoteid); + + SetEmoteID(static_cast(npc_type_data->emoteid)); InitializeBuffSlots(); CalcBonuses(); - raid_target = d->raid_target; - ignore_despawn = d->ignore_despawn; - m_targetable = !d->untargetable; - AISpellVar.fail_recast = RuleI(Spells, AI_SpellCastFinishedFailRecast); - AISpellVar.engaged_no_sp_recast_min = RuleI(Spells, AI_EngagedNoSpellMinRecast); - AISpellVar.engaged_no_sp_recast_max = RuleI(Spells, AI_EngagedNoSpellMaxRecast); - AISpellVar.engaged_beneficial_self_chance = RuleI(Spells, AI_EngagedBeneficialSelfChance); - AISpellVar.engaged_beneficial_other_chance = RuleI(Spells, AI_EngagedBeneficialOtherChance); - AISpellVar.engaged_detrimental_chance = RuleI(Spells, AI_EngagedDetrimentalChance); - AISpellVar.pursue_no_sp_recast_min = RuleI(Spells, AI_PursueNoSpellMinRecast); - AISpellVar.pursue_no_sp_recast_max = RuleI(Spells, AI_PursueNoSpellMaxRecast); - AISpellVar.pursue_detrimental_chance = RuleI(Spells, AI_PursueDetrimentalChance); - AISpellVar.idle_no_sp_recast_min = RuleI(Spells, AI_IdleNoSpellMinRecast); - AISpellVar.idle_no_sp_recast_max = RuleI(Spells, AI_IdleNoSpellMaxRecast); - AISpellVar.idle_beneficial_chance = RuleI(Spells, AI_IdleBeneficialChance); + raid_target = npc_type_data->raid_target; + ignore_despawn = npc_type_data->ignore_despawn; + m_targetable = !npc_type_data->untargetable; + + npc_scale_manager->ScaleMob(this); + + AISpellVar.fail_recast = static_cast(RuleI(Spells, AI_SpellCastFinishedFailRecast)); + AISpellVar.engaged_no_sp_recast_min = static_cast(RuleI(Spells, AI_EngagedNoSpellMinRecast)); + AISpellVar.engaged_no_sp_recast_max = static_cast(RuleI(Spells, AI_EngagedNoSpellMaxRecast)); + AISpellVar.engaged_beneficial_self_chance = static_cast (RuleI(Spells, AI_EngagedBeneficialSelfChance)); + AISpellVar.engaged_beneficial_other_chance = static_cast (RuleI(Spells, AI_EngagedBeneficialOtherChance)); + AISpellVar.engaged_detrimental_chance = static_cast (RuleI(Spells, AI_EngagedDetrimentalChance)); + AISpellVar.pursue_no_sp_recast_min = static_cast(RuleI(Spells, AI_PursueNoSpellMinRecast)); + AISpellVar.pursue_no_sp_recast_max = static_cast(RuleI(Spells, AI_PursueNoSpellMaxRecast)); + AISpellVar.pursue_detrimental_chance = static_cast (RuleI(Spells, AI_PursueDetrimentalChance)); + AISpellVar.idle_no_sp_recast_min = static_cast(RuleI(Spells, AI_IdleNoSpellMinRecast)); + AISpellVar.idle_no_sp_recast_max = static_cast(RuleI(Spells, AI_IdleNoSpellMaxRecast)); + AISpellVar.idle_beneficial_chance = static_cast (RuleI(Spells, AI_IdleBeneficialChance)); } NPC::~NPC() @@ -582,10 +551,10 @@ void NPC::ClearItemList() { void NPC::QueryLoot(Client* to) { - to->Message(0, "Coin: %ip %ig %is %ic", platinum, gold, silver, copper); + to->Message(0, "# Loot %s", GetName()); - int x = 0; - for (auto cur = itemlist.begin(); cur != itemlist.end(); ++cur, ++x) { + int item_count = 0; + for (auto cur = itemlist.begin(); cur != itemlist.end(); ++cur, ++item_count) { if (!(*cur)) { Log(Logs::General, Logs::Error, "NPC::QueryLoot() - ItemList error, null item"); continue; @@ -599,10 +568,18 @@ void NPC::QueryLoot(Client* to) linker.SetLinkType(EQEmu::saylink::SayLinkLootItem); linker.SetLootData(*cur); - to->Message(0, "%s, ID: %u, Level: (min: %u, max: %u)", linker.GenerateLink().c_str(), (*cur)->item_id, (*cur)->min_level, (*cur)->max_level); + to->Message( + 0, + "-- Item %i: %s ID: %u min_level: %u max_level: %u", + item_count, + linker.GenerateLink().c_str(), + (*cur)->item_id, + (*cur)->min_level, + (*cur)->max_level + ); } - to->Message(0, "%i items on %s.", x, GetName()); + to->Message(0, "- %i Platinum %i Gold %i Silver %i Copper", platinum, gold, silver, copper); } void NPC::AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_platinum) { @@ -627,10 +604,11 @@ void NPC::AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_ platinum = 0; } -void NPC::AddCash() { - copper = zone->random.Int(1, 100); - silver = zone->random.Int(1, 50); - gold = zone->random.Int(1, 10); +void NPC::AddCash() +{ + copper = zone->random.Int(1, 100); + silver = zone->random.Int(1, 50); + gold = zone->random.Int(1, 10); platinum = zone->random.Int(1, 5); } @@ -1004,7 +982,7 @@ NPC * NPC::SpawnGridNodeNPC(std::string name, const glm::vec4 &position, uint32 npc_type->loottable_id = 0; npc_type->texture = 1; npc_type->light = 1; - npc_type->size = 3; + npc_type->size = 1; npc_type->runspeed = 0; npc_type->d_melee_texture1 = 1; npc_type->d_melee_texture2 = 1; @@ -2138,77 +2116,218 @@ void NPC::SetLevel(uint8 in_level, bool command) SendAppearancePacket(AT_WhoLevel, in_level); } -void NPC::ModifyNPCStat(const char *identifier, const char *newValue) +void NPC::ModifyNPCStat(const char *identifier, const char *new_value) { - std::string id = identifier; - std::string val = newValue; - for(int i = 0; i < id.length(); ++i) { - id[i] = std::tolower(id[i]); - } + std::string id = str_tolower(identifier); + std::string val = new_value; - if(id == "ac") { AC = atoi(val.c_str()); CalcAC(); return; } - else if(id == "str") { STR = atoi(val.c_str()); return; } - else if(id == "sta") { STA = atoi(val.c_str()); return; } - else if(id == "agi") { AGI = atoi(val.c_str()); CalcAC(); return; } - else if(id == "dex") { DEX = atoi(val.c_str()); return; } - else if(id == "wis") { WIS = atoi(val.c_str()); CalcMaxMana(); return; } - else if(id == "int" || id == "_int") { INT = atoi(val.c_str()); CalcMaxMana(); return; } - else if(id == "cha") { CHA = atoi(val.c_str()); return; } - else if(id == "max_hp") { base_hp = atoi(val.c_str()); CalcMaxHP(); if (cur_hp > max_hp) { cur_hp = max_hp; } return; } - else if(id == "max_mana") { npc_mana = atoi(val.c_str()); CalcMaxMana(); if (current_mana > max_mana){ current_mana = max_mana; } return; } - else if(id == "mr") { MR = atoi(val.c_str()); return; } - else if(id == "fr") { FR = atoi(val.c_str()); return; } - else if(id == "cr") { CR = atoi(val.c_str()); return; } - else if(id == "pr") { PR = atoi(val.c_str()); return; } - else if(id == "dr") { DR = atoi(val.c_str()); return; } - else if(id == "phr") { PhR = atoi(val.c_str()); return; } - else if(id == "runspeed") { - runspeed = (float)atof(val.c_str()); - base_runspeed = (int)((float)runspeed * 40.0f); + std::string variable_key = StringFormat("modify_stat_%s", id.c_str()); + SetEntityVariable(variable_key.c_str(), new_value); + + Log(Logs::Detail, Logs::NPCScaling, "NPC::ModifyNPCStat key: %s val: %s ", variable_key.c_str(), new_value); + + if (id == "ac") { + AC = atoi(val.c_str()); + CalcAC(); + return; + } + else if (id == "str") { + STR = atoi(val.c_str()); + return; + } + else if (id == "sta") { + STA = atoi(val.c_str()); + return; + } + else if (id == "agi") { + AGI = atoi(val.c_str()); + CalcAC(); + return; + } + else if (id == "dex") { + DEX = atoi(val.c_str()); + return; + } + else if (id == "wis") { + WIS = atoi(val.c_str()); + CalcMaxMana(); + return; + } + else if (id == "int" || id == "_int") { + INT = atoi(val.c_str()); + CalcMaxMana(); + return; + } + else if (id == "cha") { + CHA = atoi(val.c_str()); + return; + } + else if (id == "max_hp") { + base_hp = atoi(val.c_str()); + + CalcMaxHP(); + if (cur_hp > max_hp) { + cur_hp = max_hp; + } + + return; + } + else if (id == "max_mana") { + npc_mana = atoi(val.c_str()); + CalcMaxMana(); + if (current_mana > max_mana) { + current_mana = max_mana; + } + return; + } + else if (id == "mr") { + MR = atoi(val.c_str()); + return; + } + else if (id == "fr") { + FR = atoi(val.c_str()); + return; + } + else if (id == "cr") { + CR = atoi(val.c_str()); + return; + } + else if (id == "pr") { + PR = atoi(val.c_str()); + return; + } + else if (id == "dr") { + DR = atoi(val.c_str()); + return; + } + else if (id == "phr") { + PhR = atoi(val.c_str()); + return; + } + else if (id == "runspeed") { + runspeed = (float) atof(val.c_str()); + base_runspeed = (int) ((float) runspeed * 40.0f); base_walkspeed = base_runspeed * 100 / 265; - walkspeed = ((float)base_walkspeed) * 0.025f; + walkspeed = ((float) base_walkspeed) * 0.025f; base_fearspeed = base_runspeed * 100 / 127; - fearspeed = ((float)base_fearspeed) * 0.025f; - CalcBonuses(); return; - } - else if(id == "special_attacks") { NPCSpecialAttacks(val.c_str(), 0, 1); return; } - else if(id == "special_abilities") { ProcessSpecialAbilities(val.c_str()); return; } - else if(id == "attack_speed") { attack_speed = (float)atof(val.c_str()); CalcBonuses(); return; } - else if(id == "attack_delay") { /* TODO: fix DB */attack_delay = atoi(val.c_str()) * 100; CalcBonuses(); return; } - else if(id == "atk") { ATK = atoi(val.c_str()); return; } - else if(id == "accuracy") { accuracy_rating = atoi(val.c_str()); return; } - else if(id == "avoidance") { avoidance_rating = atoi(val.c_str()); return; } - else if(id == "trackable") { trackable = atoi(val.c_str()); return; } - else if(id == "min_hit") { - min_dmg = atoi(val.c_str()); - // TODO: fix DB - base_damage = round((max_dmg - min_dmg) / 1.9); - min_damage = min_dmg - round(base_damage / 10.0); + fearspeed = ((float) base_fearspeed) * 0.025f; + CalcBonuses(); return; } - else if(id == "max_hit") { - max_dmg = atoi(val.c_str()); - // TODO: fix DB - base_damage = round((max_dmg - min_dmg) / 1.9); - min_damage = min_dmg - round(base_damage / 10.0); + else if (id == "special_attacks") { + NPCSpecialAttacks(val.c_str(), 0, 1); + return; + } + else if (id == "special_abilities") { + ProcessSpecialAbilities(val.c_str()); + return; + } + else if (id == "attack_speed") { + attack_speed = (float) atof(val.c_str()); + CalcBonuses(); + return; + } + else if (id == "attack_delay") { + /* TODO: fix DB */ + attack_delay = atoi(val.c_str()) * 100; + CalcBonuses(); + return; + } + else if (id == "atk") { + ATK = atoi(val.c_str()); + return; + } + else if (id == "accuracy") { + accuracy_rating = atoi(val.c_str()); + return; + } + else if (id == "avoidance") { + avoidance_rating = atoi(val.c_str()); + return; + } + else if (id == "trackable") { + trackable = atoi(val.c_str()); + return; + } + else if (id == "min_hit") { + min_dmg = atoi(val.c_str()); + // TODO: fix DB + base_damage = round((max_dmg - min_dmg) / 1.9); + min_damage = min_dmg - round(base_damage / 10.0); + return; + } + else if (id == "max_hit") { + max_dmg = atoi(val.c_str()); + // TODO: fix DB + base_damage = round((max_dmg - min_dmg) / 1.9); + min_damage = min_dmg - round(base_damage / 10.0); + return; + } + else if (id == "attack_count") { + attack_count = atoi(val.c_str()); + return; + } + else if (id == "see_invis") { + see_invis = atoi(val.c_str()); + return; + } + else if (id == "see_invis_undead") { + see_invis_undead = atoi(val.c_str()); + return; + } + else if (id == "see_hide") { + see_hide = atoi(val.c_str()); + return; + } + else if (id == "see_improved_hide") { + see_improved_hide = atoi(val.c_str()); + return; + } + else if (id == "hp_regen") { + hp_regen = atoi(val.c_str()); + return; + } + else if (id == "mana_regen") { + mana_regen = atoi(val.c_str()); + return; + } + else if (id == "level") { + SetLevel(atoi(val.c_str())); + return; + } + else if (id == "aggro") { + pAggroRange = atof(val.c_str()); + return; + } + else if (id == "assist") { + pAssistRange = atof(val.c_str()); + return; + } + else if (id == "slow_mitigation") { + slow_mitigation = atoi(val.c_str()); + return; + } + else if (id == "loottable_id") { + loottable_id = atof(val.c_str()); + return; + } + else if (id == "healscale") { + healscale = atof(val.c_str()); + return; + } + else if (id == "spellscale") { + spellscale = atof(val.c_str()); + return; + } + else if (id == "npc_spells_id") { + AI_AddNPCSpells(atoi(val.c_str())); + return; + } + else if (id == "npc_spells_effects_id") { + AI_AddNPCSpellsEffects(atoi(val.c_str())); + CalcBonuses(); return; } - else if(id == "attack_count") { attack_count = atoi(val.c_str()); return; } - else if(id == "see_invis") { see_invis = atoi(val.c_str()); return; } - else if(id == "see_invis_undead") { see_invis_undead = atoi(val.c_str()); return; } - else if(id == "see_hide") { see_hide = atoi(val.c_str()); return; } - else if(id == "see_improved_hide") { see_improved_hide = atoi(val.c_str()); return; } - else if(id == "hp_regen") { hp_regen = atoi(val.c_str()); return; } - else if(id == "mana_regen") { mana_regen = atoi(val.c_str()); return; } - else if(id == "level") { SetLevel(atoi(val.c_str())); return; } - else if(id == "aggro") { pAggroRange = atof(val.c_str()); return; } - else if(id == "assist") { pAssistRange = atof(val.c_str()); return; } - else if(id == "slow_mitigation") { slow_mitigation = atoi(val.c_str()); return; } - else if(id == "loottable_id") { loottable_id = atof(val.c_str()); return; } - else if(id == "healscale") { healscale = atof(val.c_str()); return; } - else if(id == "spellscale") { spellscale = atof(val.c_str()); return; } - else if(id == "npc_spells_id") { AI_AddNPCSpells(atoi(val.c_str())); return; } - else if(id == "npc_spells_effects_id") { AI_AddNPCSpellsEffects(atoi(val.c_str())); CalcBonuses(); return; } } void NPC::LevelScale() { @@ -2308,120 +2427,9 @@ void NPC::LevelScale() { return; } -void NPC::CalcNPCResists() { - - if (!MR) - MR = (GetLevel() * 11)/10; - if (!CR) - CR = (GetLevel() * 11)/10; - if (!DR) - DR = (GetLevel() * 11)/10; - if (!FR) - FR = (GetLevel() * 11)/10; - if (!PR) - PR = (GetLevel() * 11)/10; - if (!Corrup) - Corrup = 15; - if (!PhR) - PhR = 10; - return; -} - -void NPC::CalcNPCRegen() { - - // Fix for lazy db-updaters (regen values left at 0) - if (GetCasterClass() != 'N' && mana_regen == 0) - mana_regen = (GetLevel() / 10) + 4; - else if(mana_regen < 0) - mana_regen = 0; - else - mana_regen = mana_regen; - - // Gives low end monsters no regen if set to 0 in database. Should make low end monsters killable - // Might want to lower this to /5 rather than 10. - if(hp_regen == 0) - { - if(GetLevel() <= 6) - hp_regen = 1; - else if(GetLevel() > 6 && GetLevel() <= 10) - hp_regen = 2; - else if(GetLevel() > 10 && GetLevel() <= 15) - hp_regen = 3; - else if(GetLevel() > 15 && GetLevel() <= 20) - hp_regen = 5; - else if(GetLevel() > 20 && GetLevel() <= 30) - hp_regen = 7; - else if(GetLevel() > 30 && GetLevel() <= 35) - hp_regen = 9; - else if(GetLevel() > 35 && GetLevel() <= 40) - hp_regen = 12; - else if(GetLevel() > 40 && GetLevel() <= 45) - hp_regen = 18; - else if(GetLevel() > 45 && GetLevel() <= 50) - hp_regen = 21; - else - hp_regen = 30; - } else if(hp_regen < 0) { - hp_regen = 0; - } else - hp_regen = hp_regen; - - return; -} - -void NPC::CalcNPCDamage() { - - int AC_adjust=12; - - if (GetLevel() >= 66) { - if (min_dmg==0) - min_dmg = 220; - if (max_dmg==0) - max_dmg = ((((99000)*(GetLevel()-64))/400)*AC_adjust/10); - } - else if (GetLevel() >= 60 && GetLevel() <= 65){ - if(min_dmg==0) - min_dmg = (GetLevel()+(GetLevel()/3)); - if(max_dmg==0) - max_dmg = (GetLevel()*3)*AC_adjust/10; - } - else if (GetLevel() >= 51 && GetLevel() <= 59){ - if(min_dmg==0) - min_dmg = (GetLevel()+(GetLevel()/3)); - if(max_dmg==0) - max_dmg = (GetLevel()*3)*AC_adjust/10; - } - else if (GetLevel() >= 40 && GetLevel() <= 50) { - if (min_dmg==0) - min_dmg = GetLevel(); - if(max_dmg==0) - max_dmg = (GetLevel()*3)*AC_adjust/10; - } - else if (GetLevel() >= 28 && GetLevel() <= 39) { - if (min_dmg==0) - min_dmg = GetLevel() / 2; - if (max_dmg==0) - max_dmg = ((GetLevel()*2)+2)*AC_adjust/10; - } - else if (GetLevel() <= 27) { - if (min_dmg==0) - min_dmg=1; - if (max_dmg==0) - max_dmg = (GetLevel()*2)*AC_adjust/10; - } - - int32 clfact = GetClassLevelFactor(); - min_dmg = (min_dmg * clfact) / 220; - max_dmg = (max_dmg * clfact) / 220; - - return; -} - - uint32 NPC::GetSpawnPointID() const { - if(respawn2) - { + if (respawn2) { return respawn2->GetID(); } return 0; @@ -2438,7 +2446,6 @@ void NPC::NPCSlotTexture(uint8 slot, uint16 texture) else if (slot < 6) { // Reserved for texturing individual armor slots } - return; } uint32 NPC::GetSwarmOwner() @@ -2468,14 +2475,15 @@ void NPC::SetSwarmTarget(int target_id) return; } -int32 NPC::CalcMaxMana() { - if(npc_mana == 0) { +int32 NPC::CalcMaxMana() +{ + if (npc_mana == 0) { switch (GetCasterClass()) { case 'I': - max_mana = (((GetINT()/2)+1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; + max_mana = (((GetINT() / 2) + 1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; break; case 'W': - max_mana = (((GetWIS()/2)+1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; + max_mana = (((GetWIS() / 2) + 1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; break; case 'N': default: @@ -2487,7 +2495,8 @@ int32 NPC::CalcMaxMana() { } return max_mana; - } else { + } + else { switch (GetCasterClass()) { case 'I': max_mana = npc_mana + spellbonuses.Mana + itembonuses.Mana; diff --git a/zone/npc.h b/zone/npc.h index ec2676f60..fb5f00b9f 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -106,7 +106,7 @@ public: static bool SpawnZoneController(); static int8 GetAILevel(bool iForceReRead = false); - NPC(const NPCType* data, Spawn2* respawn, const glm::vec4& position, int iflymode, bool IsCorpse = false); + NPC(const NPCType* npc_type_data, Spawn2* respawn, const glm::vec4& position, int iflymode, bool IsCorpse = false); virtual ~NPC(); @@ -142,9 +142,6 @@ public: virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); void LevelScale(); - void CalcNPCResists(); - void CalcNPCRegen(); - void CalcNPCDamage(); virtual void SetTarget(Mob* mob); virtual uint16 GetSkill(EQEmu::skills::SkillType skill_num) const { if (skill_num <= EQEmu::skills::HIGHEST_SKILL) { return skills[skill_num]; } return 0; } @@ -255,12 +252,23 @@ public: void SignalNPC(int _signal_id); - inline int32 GetNPCFactionID() const { return npc_faction_id; } - inline int32 GetPrimaryFaction() const { return primary_faction; } - int32 GetNPCHate(Mob* in_ent) {return hate_list.GetEntHateAmount(in_ent);} - bool IsOnHatelist(Mob*p) { return hate_list.IsEntOnHateList(p);} + inline int32 GetNPCFactionID() const + { return npc_faction_id; } - void SetNPCFactionID(int32 in) { npc_faction_id = in; database.GetFactionIdsForNPC(npc_faction_id, &faction_list, &primary_faction); } + inline int32 GetPrimaryFaction() const + { return primary_faction; } + + int32 GetNPCHate(Mob *in_ent) + { return hate_list.GetEntHateAmount(in_ent); } + + bool IsOnHatelist(Mob *p) + { return hate_list.IsEntOnHateList(p); } + + void SetNPCFactionID(int32 in) + { + npc_faction_id = in; + database.GetFactionIdsForNPC(npc_faction_id, &faction_list, &primary_faction); + } glm::vec4 m_SpawnPoint; @@ -357,7 +365,7 @@ public: void SetAvoidanceRating(int32 d) { avoidance_rating = d;} int32 GetRawAC() const { return AC; } - void ModifyNPCStat(const char *identifier, const char *newValue); + void ModifyNPCStat(const char *identifier, const char *new_value); virtual void SetLevel(uint8 in_level, bool command = false); bool IsLDoNTrapped() const { return (ldon_trapped); } diff --git a/zone/npc_scale_manager.cpp b/zone/npc_scale_manager.cpp new file mode 100644 index 000000000..ca818a1f1 --- /dev/null +++ b/zone/npc_scale_manager.cpp @@ -0,0 +1,439 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2018 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY except by those people which sell it, which + * are required to give you total support for your newly bought product; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "npc_scale_manager.h" +#include "../common/string_util.h" + +/** + * @param mob + */ +void NpcScaleManager::ScaleMob(Mob *mob) +{ + Log(Logs::General, Logs::NPCScaling, "Attempting scale on %s", mob->GetCleanName()); + + if (mob->IsClient()) { + return; + } + + NPC *npc = mob->CastToNPC(); + + int8 mob_type = 0; + int mob_level = npc->GetLevel(); + + if (npc->IsRareSpawn()) { + mob_type = 1; + } + + if (npc->IsRaidTarget()) { + mob_type = 2; + } + + global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(mob_type, mob_level); + + if (!scale_data.level) { + Log(Logs::General, Logs::NPCScaling, "NPC: %s - scaling data not found for type: %i level: %i", + mob->GetCleanName(), + mob_type, + mob_level + ); + + return; + } + + if (npc->GetAC() == 0) { + npc->ModifyNPCStat("ac", std::to_string(scale_data.ac).c_str()); + } + if (npc->GetMaxHP() == 0) { + npc->ModifyNPCStat("max_hp", std::to_string(scale_data.hp).c_str()); + npc->Heal(); + } + if (npc->GetAccuracyRating() == 0) { + npc->ModifyNPCStat("accuracy", std::to_string(scale_data.accuracy).c_str()); + } + if (npc->GetSlowMitigation() == 0) { + npc->ModifyNPCStat("slow_mitigation", std::to_string(scale_data.slow_mitigation).c_str()); + } + if (npc->GetATK() == 0) { + npc->ModifyNPCStat("atk", std::to_string(scale_data.attack).c_str()); + } + if (npc->GetSTR() == 0) { + npc->ModifyNPCStat("str", std::to_string(scale_data.strength).c_str()); + } + if (npc->GetSTA() == 0) { + npc->ModifyNPCStat("sta", std::to_string(scale_data.stamina).c_str()); + } + if (npc->GetDEX() == 0) { + npc->ModifyNPCStat("dex", std::to_string(scale_data.dexterity).c_str()); + } + if (npc->GetAGI() == 0) { + npc->ModifyNPCStat("agi", std::to_string(scale_data.agility).c_str()); + } + if (npc->GetINT() == 0) { + npc->ModifyNPCStat("int", std::to_string(scale_data.intelligence).c_str()); + } + if (npc->GetWIS() == 0) { + npc->ModifyNPCStat("wis", std::to_string(scale_data.wisdom).c_str()); + } + if (npc->GetCHA() == 0) { + npc->ModifyNPCStat("cha", std::to_string(scale_data.charisma).c_str()); + } + if (npc->GetMR() == 0) { + npc->ModifyNPCStat("mr", std::to_string(scale_data.magic_resist).c_str()); + } + if (npc->GetCR() == 0) { + npc->ModifyNPCStat("cr", std::to_string(scale_data.cold_resist).c_str()); + } + if (npc->GetFR() == 0) { + npc->ModifyNPCStat("fr", std::to_string(scale_data.fire_resist).c_str()); + } + if (npc->GetPR() == 0) { + npc->ModifyNPCStat("pr", std::to_string(scale_data.poison_resist).c_str()); + } + if (npc->GetDR() == 0) { + npc->ModifyNPCStat("dr", std::to_string(scale_data.disease_resist).c_str()); + } + if (npc->GetCR() == 0) { + npc->ModifyNPCStat("cr", std::to_string(scale_data.corruption_resist).c_str()); + } + if (npc->GetPR() == 0) { + npc->ModifyNPCStat("pr", std::to_string(scale_data.physical_resist).c_str()); + } + if (npc->GetMinDMG() == 0) { + int min_dmg = scale_data.min_dmg; + if (RuleB(Combat, UseNPCDamageClassLevelMods)) { + int32 class_level_damage_mod = GetClassLevelDamageMod(npc->GetLevel(), npc->GetClass()); + min_dmg = (min_dmg * class_level_damage_mod) / 220; + + Log(Logs::Moderate, + Logs::NPCScaling, + "ClassLevelDamageMod::min_dmg base: %i calc: %i", + scale_data.min_dmg, + min_dmg); + } + + npc->ModifyNPCStat("min_hit", std::to_string(min_dmg).c_str()); + } + if (npc->GetMaxDMG() == 0) { + int max_dmg = scale_data.max_dmg; + if (RuleB(Combat, UseNPCDamageClassLevelMods)) { + int32 class_level_damage_mod = GetClassLevelDamageMod(npc->GetLevel(), npc->GetClass()); + max_dmg = (scale_data.max_dmg * class_level_damage_mod) / 220; + + Log(Logs::Moderate, + Logs::NPCScaling, + "ClassLevelDamageMod::max_dmg base: %i calc: %i", + scale_data.max_dmg, + max_dmg + ); + } + + npc->ModifyNPCStat("max_hit", std::to_string(max_dmg).c_str()); + } + if (npc->GetHPRegen() == 0) { + npc->ModifyNPCStat("hp_regen", std::to_string(scale_data.hp_regen_rate).c_str()); + } + if (npc->GetAttackDelay() == 0) { + npc->ModifyNPCStat("attack_delay", std::to_string(scale_data.attack_delay).c_str()); + } + if (!npc->HasSpecialAbilities()) { + npc->ModifyNPCStat("special_abilities", scale_data.special_abilities.c_str()); + } + + ListStats(npc); +} + +void NpcScaleManager::ListStats(Mob *mob) +{ + std::string stats[] = { + "ac", + "max_hp", + "accuracy", + "slow_mitigation", + "atk", + "str", + "sta", + "dex", + "agi", + "int", + "wis", + "cha", + "mr", + "cr", + "fr", + "pr", + "dr", + "cr", + "pr", + "min_hit", + "max_hit", + "hp_regen", + "attack_delay", + "special_abilities" + }; + + int stat_elements = sizeof(stats) / sizeof(stats[0]); + + for (int i = 0; i < stat_elements; i++) { + std::string variable = StringFormat("modify_stat_%s", stats[i].c_str()); + if (mob->EntityVariableExists(variable.c_str())) { + Log(Logs::Detail, + Logs::NPCScaling, + "NpcScaleManager::ListStats: %s - %s ", + stats[i].c_str(), + mob->GetEntityVariable(variable.c_str())); + } + } +} + +bool NpcScaleManager::LoadScaleData() +{ + auto results = database.QueryDatabase( + "SELECT " + "type," + "level," + "ac," + "hp," + "accuracy," + "slow_mitigation," + "attack," + "strength," + "stamina," + "dexterity," + "agility," + "intelligence," + "wisdom," + "charisma," + "magic_resist," + "cold_resist," + "fire_resist," + "poison_resist," + "disease_resist," + "corruption_resist," + "physical_resist," + "min_dmg," + "max_dmg," + "hp_regen_rate," + "attack_delay," + "special_abilities" + " FROM `npc_scale_global_base`" + ); + + for (auto row = results.begin(); row != results.end(); ++row) { + global_npc_scale scale_data; + + scale_data.type = atoi(row[0]); + scale_data.level = atoi(row[1]); + scale_data.ac = atoi(row[2]); + scale_data.hp = atoi(row[3]); + scale_data.accuracy = atoi(row[4]); + scale_data.slow_mitigation = atoi(row[5]); + scale_data.attack = atoi(row[6]); + scale_data.strength = atoi(row[7]); + scale_data.stamina = atoi(row[8]); + scale_data.dexterity = atoi(row[9]); + scale_data.agility = atoi(row[10]); + scale_data.intelligence = atoi(row[11]); + scale_data.wisdom = atoi(row[12]); + scale_data.charisma = atoi(row[13]); + scale_data.magic_resist = atoi(row[14]); + scale_data.cold_resist = atoi(row[15]); + scale_data.fire_resist = atoi(row[16]); + scale_data.poison_resist = atoi(row[17]); + scale_data.disease_resist = atoi(row[18]); + scale_data.corruption_resist = atoi(row[19]); + scale_data.physical_resist = atoi(row[20]); + scale_data.min_dmg = atoi(row[21]); + scale_data.max_dmg = atoi(row[22]); + scale_data.hp_regen_rate = atoi(row[23]); + scale_data.attack_delay = atoi(row[24]); + + if (row[25]) { + scale_data.special_abilities = row[25]; + } + + npc_global_base_scaling_data.insert( + std::make_pair( + std::make_pair(scale_data.type, scale_data.level), + scale_data + ) + ); + } + + Log(Logs::General, Logs::NPCScaling, "Global Base Scaling Data Loaded..."); + + return true; +} + +/** + * @param mob_type + * @param mob_level + * @return NpcScaleManager::global_npc_scale + */ +NpcScaleManager::global_npc_scale NpcScaleManager::GetGlobalScaleDataForTypeLevel(int8 mob_type, int mob_level) +{ + auto iter = npc_global_base_scaling_data.find(std::make_pair(mob_type, mob_level)); + if (iter != npc_global_base_scaling_data.end()) { + return iter->second; + } + + return {}; +} + +/** + * @param level + * @param npc_class + * @return + */ +uint32 NpcScaleManager::GetClassLevelDamageMod(uint32 level, uint32 npc_class) +{ + uint32 multiplier = 0; + + switch (npc_class) { + case WARRIOR: { + if (level < 20) { + multiplier = 220; + } + else if (level < 30) { + multiplier = 230; + } + else if (level < 40) { + multiplier = 250; + } + else if (level < 53) { + multiplier = 270; + } + else if (level < 57) { + multiplier = 280; + } + else if (level < 60) { + multiplier = 290; + } + else if (level < 70) { + multiplier = 300; + } + else { + multiplier = 311; + } + break; + } + case DRUID: + case CLERIC: + case SHAMAN: { + if (level < 70) { + multiplier = 150; + } + else { + multiplier = 157; + } + break; + } + case BERSERKER: + case PALADIN: + case SHADOWKNIGHT: { + if (level < 35) { + multiplier = 210; + } + else if (level < 45) { + multiplier = 220; + } + else if (level < 51) { + multiplier = 230; + } + else if (level < 56) { + multiplier = 240; + } + else if (level < 60) { + multiplier = 250; + } + else if (level < 68) { + multiplier = 260; + } + else { + multiplier = 270; + } + break; + } + case MONK: + case BARD: + case ROGUE: + case BEASTLORD: { + if (level < 51) { + multiplier = 180; + } + else if (level < 58) { + multiplier = 190; + } + else if (level < 70) { + multiplier = 200; + } + else { + multiplier = 210; + } + break; + } + case RANGER: { + if (level < 58) { + multiplier = 200; + } + else if (level < 70) { + multiplier = 210; + } + else { + multiplier = 220; + } + break; + } + case MAGICIAN: + case WIZARD: + case NECROMANCER: + case ENCHANTER: { + if (level < 70) { + multiplier = 120; + } + else { + multiplier = 127; + } + break; + } + default: { + if (level < 35) { + multiplier = 210; + } + else if (level < 45) { + multiplier = 220; + } + else if (level < 51) { + multiplier = 230; + } + else if (level < 56) { + multiplier = 240; + } + else if (level < 60) { + multiplier = 250; + } + else { + multiplier = 260; + } + break; + } + } + + return multiplier; +} \ No newline at end of file diff --git a/zone/npc_scale_manager.h b/zone/npc_scale_manager.h new file mode 100644 index 000000000..5211c8de5 --- /dev/null +++ b/zone/npc_scale_manager.h @@ -0,0 +1,73 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2018 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY except by those people which sell it, which + * are required to give you total support for your newly bought product; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef EQEMU_NPC_SCALE_MANAGER_H +#define EQEMU_NPC_SCALE_MANAGER_H + + +#include "npc.h" + +class NpcScaleManager { +public: + struct global_npc_scale { + int type; + int level; + int ac; + int hp; + int accuracy; + int slow_mitigation; + int attack; + int strength; + int stamina; + int dexterity; + int agility; + int intelligence; + int wisdom; + int charisma; + int magic_resist; + int cold_resist; + int fire_resist; + int poison_resist; + int disease_resist; + int corruption_resist; + int physical_resist; + int min_dmg; + int max_dmg; + int hp_regen_rate; + int attack_delay; + + std::string special_abilities; + }; + + void ScaleMob(Mob *mob); + bool LoadScaleData(); + + global_npc_scale GetGlobalScaleDataForTypeLevel(int8 mob_type, int mob_level); + + std::map, global_npc_scale> npc_global_base_scaling_data; + + void ListStats(Mob * mob); + + uint32 GetClassLevelDamageMod(uint32 level, uint32 npc_class); +}; + +extern NpcScaleManager *npc_scale_manager; + +#endif //EQEMU_NPC_SCALE_MANAGER_H diff --git a/zone/object.h b/zone/object.h index 1afe574ea..ce64ee47e 100644 --- a/zone/object.h +++ b/zone/object.h @@ -179,20 +179,20 @@ protected: void ResetState(); // Set state back to original void RandomSpawn(bool send_packet = false); //spawn this ground spawn at a random place - Object_Struct m_data; // Packet data - EQEmu::ItemInstance* m_inst; // Item representing object - bool m_inuse; // Currently in use by a client? - uint32 m_id; // Database key, different than drop_id - uint32 m_type; // Object Type, ie, forge, oven, dropped item, etc (ref: ContainerUseTypes) - uint32 m_icon; // Icon to use for forge, oven, etc - float m_max_x; - float m_max_y; - float m_min_x; - float m_min_y; - float m_z; - float m_heading; - bool m_ground_spawn; - char m_display_name[64]; + Object_Struct m_data; // Packet data + EQEmu::ItemInstance *m_inst; // Item representing object + bool m_inuse; // Currently in use by a client? + uint32 m_id; // Database key, different than drop_id + uint32 m_type; // Object Type, ie, forge, oven, dropped item, etc (ref: ContainerUseTypes) + uint32 m_icon; // Icon to use for forge, oven, etc + float m_max_x; + float m_max_y; + float m_min_x; + float m_min_y; + float m_z; + float m_heading; + bool m_ground_spawn; + char m_display_name[64]; std::map o_EntityVariables; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 1393b0221..3bbd9f6ac 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2761,47 +2761,11 @@ void QuestManager::FlagInstanceByRaidLeader(uint32 zone, int16 version) } } -const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkName) { +std::string QuestManager::saylink(char *saylink_text, bool silent, const char *link_name) +{ QuestManagerCurrentQuestVars(); - int sayid = 0; - - int sz = strlen(Phrase); - auto escaped_string = new char[sz * 2]; - database.DoEscapeString(escaped_string, Phrase, sz); - - // Query for an existing phrase and id in the saylink table - std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string); - auto results = database.QueryDatabase(query); - if (results.Success()) { - if (results.RowCount() >= 1) { - for (auto row = results.begin();row != results.end(); ++row) - sayid = atoi(row[0]); - } else { - std::string insert_query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string); - results = database.QueryDatabase(insert_query); - if (!results.Success()) { - Log(Logs::General, Logs::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str()); - } - else { - sayid = results.LastInsertedID(); - } - } - } - safe_delete_array(escaped_string); - - //Create the say link as an item link hash - EQEmu::SayLinkEngine linker; - linker.SetProxyItemID(SAYLINK_ITEM_ID); - if (silent) - linker.SetProxyAugment2ID(sayid); - else - linker.SetProxyAugment1ID(sayid); - linker.SetProxyText(LinkName); - - strcpy(Phrase, linker.GenerateLink().c_str()); - - return Phrase; + return EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink_text, silent, link_name); } const char* QuestManager::getguildnamebyid(int guild_id) { diff --git a/zone/questmgr.h b/zone/questmgr.h index a3b9fac43..68a51ae08 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -245,7 +245,7 @@ public: void FlagInstanceByGroupLeader(uint32 zone, int16 version); void FlagInstanceByRaidLeader(uint32 zone, int16 version); const char* varlink(char* perltext, int item_id); - const char* saylink(char* Phrase, bool silent, const char* LinkName); + std::string saylink(char *saylink_text, bool silent, const char *link_name); const char* getguildnamebyid(int guild_id); void SetRunning(bool val); bool IsRunning(); diff --git a/zone/spells.cpp b/zone/spells.cpp index 0fcb7fe2b..f1b8824f1 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -5644,9 +5644,8 @@ void NPC::InitializeBuffSlots() { int max_slots = GetMaxTotalSlots(); buffs = new Buffs_Struct[max_slots]; - for(int x = 0; x < max_slots; ++x) - { - buffs[x].spellid = SPELL_UNKNOWN; + for (int x = 0; x < max_slots; ++x) { + buffs[x].spellid = SPELL_UNKNOWN; buffs[x].UpdateClient = false; } current_buff_count = 0; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 027225243..ba3d7ff45 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2637,7 +2637,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load temp_npctype_data->spellscale = atoi(row[86]); temp_npctype_data->healscale = atoi(row[87]); temp_npctype_data->no_target_hotkey = atoi(row[88]) == 1 ? true: false; - temp_npctype_data->raid_target = atoi(row[89]) == 0 ? false: true; + temp_npctype_data->raid_target = atoi(row[89]) == 0 ? false : true; temp_npctype_data->attack_delay = atoi(row[90]) * 100; // TODO: fix DB temp_npctype_data->light = (atoi(row[91]) & 0x0F);