diff --git a/.travis.yml b/.travis.yml index faed5b846..38db9d797 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,26 +1,18 @@ language: cpp compiler: gcc -dist: trusty +dist: bionic -before_install: - - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - - sudo apt-get update -qq - - mkdir $HOME/usr - - export PATH="$HOME/usr/bin:$PATH" - - wget https://cmake.org/files/v3.11/cmake-3.11.2-Linux-x86_64.sh - - chmod +x cmake-3.11.2-Linux-x86_64.sh - - ./cmake-3.11.2-Linux-x86_64.sh --prefix=$HOME/usr --exclude-subdir --skip-license +addons: + apt: + packages: + - libmysqlclient-dev + - libperl-dev + - libboost-dev + - liblua5.1-0-dev + - zlib1g-dev + - uuid-dev + - libssl-dev -install: - - sudo apt-get install -qq g++-7 - - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-7 90 - - sudo apt-get install libmysqlclient-dev - - sudo apt-get install libperl-dev - - sudo apt-get install libboost-dev - - sudo apt-get install liblua5.1-0-dev - - sudo apt-get install zlib1g-dev - - sudo apt-get install uuid-dev - - sudo apt-get install libssl-dev script: - cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON -DEQEMU_BUILD_LOGIN=ON - make -j2 diff --git a/common/database.cpp b/common/database.cpp index fde091322..ac5ae16bf 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -341,65 +341,89 @@ bool Database::ReserveName(uint32 account_id, char* name) { return true; } -/* - Delete the character with the name "name" - returns false on failure, true otherwise -*/ -bool Database::DeleteCharacter(char *name) { - uint32 charid = 0; - if(!name || !strlen(name)) { +/** + * @param character_name + * @return + */ +bool Database::DeleteCharacter(char *character_name) { + uint32 character_id = 0; + if(!character_name || !strlen(character_name)) { LogInfo("DeleteCharacter: request to delete without a name (empty char slot)"); return false; } - LogInfo("Database::DeleteCharacter name : [{}]", name); - /* Get id from character_data before deleting record so we can clean up the rest of the tables */ - std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", name); - auto results = QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) { charid = atoi(row[0]); } - if (charid <= 0){ - LogError("Database::DeleteCharacter :: Character ({}) not found, stopping delete...", name); + std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", character_name); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + character_id = atoi(row[0]); + } + + if (character_id <= 0) { + LogError("DeleteCharacter | Invalid Character ID [{}]", character_name); return false; } - query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_inspect_messages` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", charid); QueryDatabase(query); - query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", charid); QueryDatabase(query); + std::string delete_type = "hard-deleted"; + if (RuleB(Character, SoftDeletes)) { + delete_type = "soft-deleted"; + std::string query = fmt::format( + SQL( + UPDATE + character_data + SET + name = SUBSTRING(CONCAT(name, '-deleted-', UNIX_TIMESTAMP()), 1, 64), + deleted_at = NOW() + WHERE + id = '{}' + ), + character_id + ); + + QueryDatabase(query); + + return true; + } + + LogInfo("DeleteCharacter | Character [{}] ({}) is being [{}]", character_name, character_id, delete_type); + + query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_tasks` WHERE `charid` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_inspect_messages` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", character_id); QueryDatabase(query); + query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", character_id); QueryDatabase(query); #ifdef BOTS - query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", charid); // note: only use of GetMobTypeById() + query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", character_id); // note: only use of GetMobTypeById() #else - query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", charid); + query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", character_id); #endif QueryDatabase(query); diff --git a/common/database.h b/common/database.h index c84b5301c..f569da20b 100644 --- a/common/database.h +++ b/common/database.h @@ -107,7 +107,7 @@ public: bool AddToNameFilter(const char* name); bool CreateCharacter(uint32 account_id, char* name, uint16 gender, uint16 race, uint16 class_, uint8 str, uint8 sta, uint8 cha, uint8 dex, uint8 int_, uint8 agi, uint8 wis, uint8 face); - bool DeleteCharacter(char* name); + bool DeleteCharacter(char* character_name); bool MoveCharacterToZone(const char* charname, const char* zonename); bool MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid); bool MoveCharacterToZone(uint32 iCharID, const char* iZonename); diff --git a/common/database_schema.h b/common/database_schema.h index 6033e85b5..73637c877 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -33,7 +33,6 @@ namespace DatabaseSchema { static std::vector GetPlayerTables() { std::vector tables = { - "aa_timers", "account", "account_ip", "account_flags", diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 53d7021ba..3fe2430db 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -35,6 +35,7 @@ static const uint32 MAX_MERC = 100; static const uint32 MAX_MERC_GRADES = 10; static const uint32 MAX_MERC_STANCES = 10; static const uint32 BLOCKED_BUFF_COUNT = 20; +static const uint32 QUESTREWARD_COUNT = 8; /* @@ -2180,14 +2181,7 @@ struct QuestReward_Struct /*024*/ uint32 silver; // Gives silver to the client /*028*/ uint32 gold; // Gives gold to the client /*032*/ uint32 platinum; // Gives platinum to the client - /*036*/ uint32 item_id; - /*040*/ uint32 unknown040; - /*044*/ uint32 unknown044; - /*048*/ uint32 unknown048; - /*052*/ uint32 unknown052; - /*056*/ uint32 unknown056; - /*060*/ uint32 unknown060; - /*064*/ uint32 unknown064; + /*036*/ int32 item_id[QUESTREWARD_COUNT]; // -1 for nothing /*068*/ }; diff --git a/common/eqemu_config.h b/common/eqemu_config.h index e75737d76..a4fb7797f 100644 --- a/common/eqemu_config.h +++ b/common/eqemu_config.h @@ -165,7 +165,7 @@ class EQEmuConfig fconfig >> _config->_root; _config->parse_config(); } - catch (std::exception) { + catch (std::exception &) { return false; } return true; diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 184ccffe2..9fa164d98 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -113,6 +113,7 @@ namespace Logs { AoeCast, EntityManagement, Flee, + Aura, MaxCategoryID /* Don't Remove this */ }; @@ -185,6 +186,7 @@ namespace Logs { "AOE Cast", "Entity Management", "Flee", + "Aura", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 138a6f851..ccfc54f3a 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -551,6 +551,16 @@ OutF(LogSys, Logs::Detail, Logs::Flee, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogAura(message, ...) do {\ + if (LogSys.log_settings[Logs::Aura].is_category_enabled == 1)\ + OutF(LogSys, Logs::General, Logs::Aura, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogAuraDetail(message, ...) do {\ + if (LogSys.log_settings[Logs::Aura].is_category_enabled == 1)\ + OutF(LogSys, Logs::Detail, Logs::Aura, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.log_settings[log_category].is_category_enabled == 1)\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ @@ -842,6 +852,48 @@ #define LogStatusDetail(message, ...) do {\ } while (0) +#define LogAIScanClose(message, ...) do {\ +} while (0) + +#define LogAIScanCloseDetail(message, ...) do {\ +} while (0) + +#define LogAIYellForHelp(message, ...) do {\ +} while (0) + +#define LogAIYellForHelpDetail(message, ...) do {\ +} while (0) + +#define LogAICastBeneficialClose(message, ...) do {\ +} while (0) + +#define LogAICastBeneficialCloseDetail(message, ...) do {\ +} while (0) + +#define LogAoeCast(message, ...) do {\ +} while (0) + +#define LogAoeCastDetail(message, ...) do {\ +} while (0) + +#define LogEntityManagement(message, ...) do {\ +} while (0) + +#define LogEntityManagementDetail(message, ...) do {\ +} while (0) + +#define LogFlee(message, ...) do {\ +} while (0) + +#define LogFleeDetail(message, ...) do {\ +} while (0) + +#define LogAura(message, ...) do {\ +} while (0) + +#define LogAuraDetail(message, ...) do {\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ } while (0) diff --git a/common/json_config.cpp b/common/json_config.cpp index d262038f6..5f4c47b71 100644 --- a/common/json_config.cpp +++ b/common/json_config.cpp @@ -32,7 +32,7 @@ EQ::JsonConfigFile EQ::JsonConfigFile::Load( try { ifs >> ret.m_root; } - catch (std::exception) { + catch (std::exception &) { return ret; } @@ -81,7 +81,7 @@ std::string EQ::JsonConfigFile::GetVariableString( return m_root[title][parameter].asString(); } } - catch (std::exception) { + catch (std::exception &) { return default_value; } @@ -105,7 +105,7 @@ int EQ::JsonConfigFile::GetVariableInt( return m_root[title][parameter].asInt(); } } - catch (std::exception) { + catch (std::exception &) { return default_value; } @@ -129,7 +129,7 @@ bool EQ::JsonConfigFile::GetVariableBool( return m_root[title][parameter].asBool(); } } - catch (std::exception) { + catch (std::exception &) { return default_value; } @@ -153,7 +153,7 @@ double EQ::JsonConfigFile::GetVariableDouble( return m_root[title][parameter].asDouble(); } } - catch (std::exception) { + catch (std::exception &) { return default_value; } diff --git a/common/linked_list.h b/common/linked_list.h index 246677db4..8ac4b51d8 100644 --- a/common/linked_list.h +++ b/common/linked_list.h @@ -278,12 +278,6 @@ void LinkedListIterator::Replace(const TYPE& new_data) template void LinkedListIterator::Reset() { - if (!(&list)) - { - current_element=0; - return; - } - if (dir == FORWARD) { current_element = list.first; diff --git a/common/misc.cpp b/common/misc.cpp index 792dfcbab..b9dc31d28 100644 --- a/common/misc.cpp +++ b/common/misc.cpp @@ -19,9 +19,6 @@ #include "types.h" #include -#define ENC(c) (((c) & 0x3f) + ' ') -#define DEC(c) (((c) - ' ') & 0x3f) - std::map DBFieldNames; #ifndef WIN32 @@ -333,64 +330,6 @@ void LoadItemDBFieldNames() { DBFieldNames[113]="unknown115"; // ? (end quote) } -void encode_length(unsigned long length, char *out) -{ -char buf[4]; - memcpy(buf,&length,sizeof(unsigned long)); - encode_chunk(buf,3,out); -} - -unsigned long encode(char *in, unsigned long length, char *out) -{ -unsigned long used=0,len=0; - while(used> 2); - *(out+1)=ENC((in[0] << 4)|(((len<2 ? 0 : in[1]) >> 4) & 0xF)); - *(out+2)=ENC(((len<2 ? 0 : in[1]) << 2)|(((len<3 ? 0 : in[2]) >> 6) & 0x3)); - *(out+3)=ENC((len<3 ? 0 : in[2])); -} - -void decode_chunk(char *in, char *out) -{ - *out = DEC(*in) << 2 | DEC(in[1]) >> 4; - *(out+1) = DEC(in[1]) << 4 | DEC(in[2]) >> 2; - *(out+2) = DEC(in[2]) << 6 | DEC(in[3]); -} - void dump_message_column(unsigned char *buffer, unsigned long length, std::string leader, FILE *to) { unsigned long i,j; diff --git a/common/misc.h b/common/misc.h index b33f2f32d..a099ab823 100644 --- a/common/misc.h +++ b/common/misc.h @@ -17,13 +17,6 @@ int Tokenize(std::string s, std::map & tokens, char delim='|'); void LoadItemDBFieldNames(); -void encode_length(unsigned long length, char *out); -unsigned long decode_length(char *in); -unsigned long encode(char *in, unsigned long length, char *out); -void decode(char *in, char *out); -void encode_chunk(char *in, int len, char *out); -void decode_chunk(char *in, char *out); - #ifndef WIN32 int print_stacktrace(); #endif diff --git a/common/net/console_server_connection.cpp b/common/net/console_server_connection.cpp index 795c73140..aab26d188 100644 --- a/common/net/console_server_connection.cpp +++ b/common/net/console_server_connection.cpp @@ -116,17 +116,21 @@ bool EQ::Net::ConsoleServerConnection::SendChannelMessage(const ServerChannelMes } switch (scm->chan_num) { - if (RuleB(Chat, ServerWideAuction)) { - case 4: { + case 4: { + if (RuleB(Chat, ServerWideAuction)) { QueueMessage(fmt::format("{0} auctions, '{1}'", scm->from, scm->message)); break; + } else { // I think we want default action in this case? + return false; } } - if (RuleB(Chat, ServerWideOOC)) { - case 5: { + case 5: { + if (RuleB(Chat, ServerWideOOC)) { QueueMessage(fmt::format("{0} says ooc, '{1}'", scm->from, scm->message)); break; + } else { // I think we want default action in this case? + return false; } } diff --git a/common/net/daybreak_connection.cpp b/common/net/daybreak_connection.cpp index b10203854..8448049f5 100644 --- a/common/net/daybreak_connection.cpp +++ b/common/net/daybreak_connection.cpp @@ -399,7 +399,7 @@ void EQ::Net::DaybreakConnection::Process() ProcessQueue(); } - catch (std::exception ex) { + catch (std::exception &ex) { if (m_owner->m_on_error_message) { m_owner->m_on_error_message(fmt::format("Error processing connection: {0}", ex.what())); } diff --git a/common/net/websocket_server.cpp b/common/net/websocket_server.cpp index de1a0cf99..b46fc7953 100644 --- a/common/net/websocket_server.cpp +++ b/common/net/websocket_server.cpp @@ -61,7 +61,7 @@ EQ::Net::WebsocketServer::WebsocketServer(const std::string &addr, int port) auto &connection = iter->second; connection->GetWebsocketConnection()->ping("keepalive"); } - catch (std::exception) { + catch (std::exception &) { iter->second->GetTCPConnection()->Disconnect(); } @@ -157,7 +157,7 @@ void EQ::Net::WebsocketServer::DispatchEvent(WebsocketSubscriptionEvent evt, Jso } } } - catch (std::exception) { + catch (std::exception &) { } } @@ -190,7 +190,7 @@ Json::Value EQ::Net::WebsocketServer::Login(WebsocketServerConnection *connectio return ret; } - catch (std::exception) { + catch (std::exception &) { throw WebsocketException("Unable to process login request"); } } @@ -212,7 +212,7 @@ Json::Value EQ::Net::WebsocketServer::Subscribe(WebsocketServerConnection *conne catch (WebsocketException &ex) { throw ex; } - catch (std::exception) { + catch (std::exception &) { throw WebsocketException("Unable to process unsubscribe request"); } } @@ -234,7 +234,7 @@ Json::Value EQ::Net::WebsocketServer::Unsubscribe(WebsocketServerConnection *con catch (WebsocketException &ex) { throw ex; } - catch (std::exception) { + catch (std::exception &) { throw WebsocketException("Unable to process unsubscribe request"); } } diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 11bb99af7..36257771b 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -37,6 +37,7 @@ #include #include #include +#include namespace RoF2 @@ -3548,7 +3549,7 @@ namespace RoF2 { eq->items[i].Unknown18 = 0; if (i < 80) { - snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", emu->SerialNumber[i]); + snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016" PRId64, emu->SerialNumber[i]); eq->ItemCost[i] = emu->ItemCost[i]; } else { diff --git a/common/ptimer.cpp b/common/ptimer.cpp index 032ac5945..e6338bdf9 100644 --- a/common/ptimer.cpp +++ b/common/ptimer.cpp @@ -190,10 +190,6 @@ bool PersistentTimer::Clear(Database *db) { /* This function checks if the timer triggered */ bool PersistentTimer::Expired(Database *db, bool iReset) { - if (this == nullptr) { - LogError("Null timer during ->Check()!?\n"); - return(true); - } uint32 current_time = get_current_time(); if (current_time-start_time >= timer_time) { if (enabled && iReset) { diff --git a/common/ruletypes.h b/common/ruletypes.h index 8acbb2c14..8d3b85d3e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -158,6 +158,7 @@ RULE_BOOL(Character, AllowCrossClassTrainers, false, "") RULE_BOOL(Character, PetsUseReagents, true, "Pets use reagent on spells") RULE_BOOL(Character, DismountWater, true, "Dismount horses when entering water") RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishing") +RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) @@ -519,7 +520,7 @@ RULE_INT(NPC, NPCToNPCAggroTimerMin, 500, "") RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000, "") RULE_BOOL(NPC, UseClassAsLastName, true, "Uses class archetype as LastName for npcs with none") RULE_BOOL(NPC, NewLevelScaling, true, "Better level scaling, use old if new formulas would break your server") -RULE_INT(NPC, NPCGatePercent, 5, "% at which the NPC Will attempt to gate at") +RULE_INT(NPC, NPCGatePercent, 20, "% at which the NPC Will attempt to gate at") RULE_BOOL(NPC, NPCGateNearBind, false, "Will NPC attempt to gate when near bind location?") RULE_INT(NPC, NPCGateDistanceBind, 75, "Distance from bind before NPC will attempt to gate") RULE_BOOL(NPC, NPCHealOnGate, true, "Will the NPC Heal on Gate") diff --git a/common/servertalk.h b/common/servertalk.h index 3aef636ca..b9cb6202e 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -262,9 +262,6 @@ public: } ServerPacket* Copy() { - if (this == 0) { - return 0; - } ServerPacket* ret = new ServerPacket(this->opcode, this->size); if (this->size) memcpy(ret->pBuffer, this->pBuffer, this->size); diff --git a/common/spdat.h b/common/spdat.h index 416f63561..c7c6027c6 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -607,7 +607,7 @@ typedef enum { #define SE_LimitSpellGroup 385 // implemented - Limits to spell group(ie type 3 reuse reduction augs that are class specific and thus all share s SG) #define SE_CastOnCurer 386 // implemented - Casts a spell on the person curing #define SE_CastOnCure 387 // implemented - Casts a spell on the cured person -//#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA) +#define SE_SummonCorpseZone 388 // implemented - summons a corpse from any zone(nec AA) #define SE_FcTimerRefresh 389 // implemented - Refresh spell icons //#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited. #define SE_LimitManaMax 391 // implemented diff --git a/common/string_util.cpp b/common/string_util.cpp index b48bee1b3..df3790def 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -222,7 +222,7 @@ bool StringIsNumber(const std::string &s) { auto r = stod(s); return true; } - catch (std::exception) { + catch (std::exception &) { return false; } } diff --git a/common/version.h b/common/version.h index 17e69601a..5d00daaf4 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9145 +#define CURRENT_BINARY_DATABASE_VERSION 9147 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026 diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index 658f3709d..2c769e811 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -813,7 +813,7 @@ sub fetch_utility_scripts { sub setup_bots { if ($OS eq "Windows") { - fetch_latest_windows_binaries_bots(); + fetch_latest_windows_appveyor_bots(); } if ($OS eq "Linux") { build_linux_source("bots"); @@ -821,7 +821,7 @@ sub setup_bots { bots_db_management(); run_database_check(); - print "Bots should be setup, run your server and the #bot command should be available in-game\n"; + print "Bots should be setup, run your server and the bot command should be available in-game (type '^help')\n"; } sub show_menu_prompt { @@ -2216,11 +2216,18 @@ sub get_bots_db_version { } sub bots_db_management { + + my $world_path = "world"; + if (-e "bin/world") { + $world_path = "bin/world"; + } + + #::: Get Binary DB version if ($OS eq "Windows") { - @db_version = split(': ', `world db_version`); + @db_version = split(': ', `$world_path db_version`); } if ($OS eq "Linux") { - @db_version = split(': ', `./world db_version`); + @db_version = split(': ', `./$world_path db_version`); } #::: Main Binary Database version diff --git a/utils/scripts/schema.xml b/utils/scripts/schema.xml deleted file mode 100644 index dcfa687be..000000000 --- a/utils/scripts/schema.xml +++ /dev/null @@ -1,5738 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - -
diff --git a/utils/sql/character_table_list.txt b/utils/sql/character_table_list.txt index 8a47abf67..ab6610dab 100644 --- a/utils/sql/character_table_list.txt +++ b/utils/sql/character_table_list.txt @@ -1,9 +1,15 @@ +account +account_ip +account_flags +account_rewards +adventure_details adventure_stats +buyer char_recipe_list -character_auras character_activities character_alt_currency character_alternate_abilities +character_auras character_bandolier character_bind character_buffs @@ -20,15 +26,22 @@ character_leadership_abilities character_material character_memmed_spells character_pet_buffs +character_pet_info character_pet_inventory character_potionbelt character_skills character_spells +character_tasks character_tribute completed_tasks +data_buckets faction_values friends +guild_bank guild_members +guild_ranks +guild_relations +guilds instance_list_player inventory inventory_snapshots @@ -36,6 +49,9 @@ keyring mail player_titlesets quest_globals +sharedbank timers titles -zone_flags +trader +trader_audit +zone_flags" \ No newline at end of file diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index e68450e7d..53a71e109 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -399,6 +399,8 @@ 9143|2019_09_16_account_table_changes.sql|SHOW COLUMNS FROM `account` LIKE 'ls_id'|empty| 9144|2019_11_09_logsys_description_update.sql|SELECT * FROM db_version WHERE version >= 9143|empty| 9145|2019_12_24_banned_ips_update.sql|SHOW TABLES LIKE 'Banned_IPs'|not_empty| +9146|2020_01_10_character_soft_deletes.sql|SHOW COLUMNS FROM `character_data` LIKE 'deleted_at'|empty| +9147|2020_01_24_grid_centerpoint_wp.sql|SHOW COLUMNS FROM `grid_entries` LIKE 'centerpoint'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 0a1e4466a..1e18516b7 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -21,9 +21,9 @@ 9020|2018_08_13_bots_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `bot_step` = 0|not_empty| 9021|2018_10_09_bots_owner_options.sql|SHOW TABLES LIKE 'bot_owner_options'|empty| 9022|2019_02_07_bots_stance_type_update.sql|SELECT * FROM `bot_spell_casting_chances` WHERE `spell_type_index` = '255' AND `class_id` = '255' AND `stance_index` = '0'|not_empty| -9023|2019_06_22_bots_owner_option_stats_update.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'stats_update'|empty| +9023|2019_06_22_bots_owner_option_stats_update.sql|SELECT * FROM db_version WHERE bots_version >= 9023|empty| 9024|2019_06_27_bots_pet_get_lost.sql|SELECT `bot_command` FROM `bot_command_settings` WHERE `bot_command` LIKE 'petgetlost'|empty| -9025|2019_08_26_bots_owner_option_spawn_message.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'spawn_message_enabled'|empty| +9025|2019_08_26_bots_owner_option_spawn_message.sql|SELECT * FROM db_version WHERE bots_version >= 9025|empty| 9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|empty| # Upgrade conditions: diff --git a/utils/sql/git/optional/2020_01_26_soft_delete_retro.sql b/utils/sql/git/optional/2020_01_26_soft_delete_retro.sql new file mode 100644 index 000000000..d3d0c7c4b --- /dev/null +++ b/utils/sql/git/optional/2020_01_26_soft_delete_retro.sql @@ -0,0 +1,12 @@ +-- Run this to un-reserve deleted characters +UPDATE + character_data +SET + name = SUBSTRING( + CONCAT(name, '-deleted-', UNIX_TIMESTAMP()), + 1, + 64 + ) +WHERE + deleted_at IS NOT NULL + AND name NOT LIKE '%-deleted-%'; \ No newline at end of file diff --git a/utils/sql/git/required/2020_01_10_character_soft_deletes.sql b/utils/sql/git/required/2020_01_10_character_soft_deletes.sql new file mode 100644 index 000000000..17496b141 --- /dev/null +++ b/utils/sql/git/required/2020_01_10_character_soft_deletes.sql @@ -0,0 +1 @@ +ALTER TABLE `character_data` ADD COLUMN `deleted_at` datetime NULL DEFAULT NULL; \ No newline at end of file diff --git a/utils/sql/git/required/2020_01_24_grid_centerpoint_wp.sql b/utils/sql/git/required/2020_01_24_grid_centerpoint_wp.sql new file mode 100644 index 000000000..b4a118242 --- /dev/null +++ b/utils/sql/git/required/2020_01_24_grid_centerpoint_wp.sql @@ -0,0 +1,2 @@ +alter table grid_entries add column `centerpoint` tinyint(4) not null default 0; +alter table spawngroup add column `wp_spawns` tinyint(1) unsigned not null default 0; \ No newline at end of file diff --git a/world/adventure_manager.cpp b/world/adventure_manager.cpp index 65e264a19..c3e70b7ed 100644 --- a/world/adventure_manager.cpp +++ b/world/adventure_manager.cpp @@ -2119,102 +2119,3 @@ void AdventureManager::Save() } } -void AdventureManager::Load() -{ - //disabled for now - return; - - char *data = nullptr; - FILE *f = fopen("adventure_state.dat", "r"); - if(f) - { - fseek(f, 0, SEEK_END); - long length = ftell(f); - if(length > 0) - { - data = new char[length]; - fseek(f, 0, SEEK_SET); - fread(data, length, 1, f); - } - fclose(f); - } - - if(data) - { - char *ptr = data; - - int number_of_adventures = *((int*)ptr); - ptr += sizeof(int); - - for(int i = 0; i < number_of_adventures; ++i) - { - int count = *((int*)ptr); - ptr += sizeof(int); - - int a_count = *((int*)ptr); - ptr += sizeof(int); - - int template_id = *((int*)ptr); - ptr += sizeof(int); - - int status = *((int*)ptr); - ptr += sizeof(int); - - int instance_id = *((int*)ptr); - ptr += sizeof(int); - - int rem_time = *((int*)ptr); - ptr += sizeof(int); - - int num_players = *((int*)ptr); - ptr += sizeof(int); - - AdventureTemplate *t = GetAdventureTemplate(template_id); - if(t) - { - auto adv = - new Adventure(t, count, a_count, (AdventureStatus)status, instance_id, rem_time); - for(int j = 0; j < num_players; ++j) - { - adv->AddPlayer((const char*)ptr, false); - ptr += strlen((const char*)ptr); - ptr += 1; - } - adventure_list.push_back(adv); - } - else - { - for(int j = 0; j < num_players; ++j) - { - ptr += strlen((const char*)ptr); - ptr += 1; - } - } - } - - int number_of_finished = *((int*)ptr); - ptr += sizeof(int); - - for(int k = 0; k < number_of_finished; ++k) - { - AdventureFinishEvent afe; - afe.win = *((bool*)ptr); - ptr += sizeof(bool); - - afe.points = *((int*)ptr); - ptr += sizeof(int); - - afe.theme = *((int*)ptr); - ptr += sizeof(int); - - afe.name = (const char*)ptr; - ptr += strlen((const char*)ptr); - ptr += 1; - - finished_list.push_back(afe); - } - - safe_delete_array(data); - } -} - diff --git a/world/adventure_manager.h b/world/adventure_manager.h index 5c9a4e560..ae4bf950f 100644 --- a/world/adventure_manager.h +++ b/world/adventure_manager.h @@ -34,7 +34,6 @@ public: void AddFinishedEvent(AdventureFinishEvent fe) { finished_list.push_back(fe); Save(); } bool PopFinishedEvent(const char *name, AdventureFinishEvent &fe); void Save(); - void Load(); Adventure **GetFinishedAdventures(const char *player, int &count); Adventure *GetActiveAdventure(const char *player); diff --git a/world/main.cpp b/world/main.cpp index 0ab5f2089..72cc6d391 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -396,7 +396,6 @@ int main(int argc, char** argv) { LogInfo("Unable to load adventure templates"); } - adventure_manager.Load(); adventure_manager.LoadLeaderboardInfo(); LogInfo("Purging expired instances"); diff --git a/world/web_interface.cpp b/world/web_interface.cpp index 191d8cca1..773e955b6 100644 --- a/world/web_interface.cpp +++ b/world/web_interface.cpp @@ -24,7 +24,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p) std::stringstream ss(json_str); ss >> root; } - catch (std::exception) { + catch (std::exception &) { SendError("Could not parse request"); return; } @@ -40,7 +40,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p) return; } } - catch (std::exception) { + catch (std::exception &) { SendError("Invalid request: method not supplied"); return; } @@ -49,7 +49,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p) try { params = root["params"]; } - catch (std::exception) { + catch (std::exception &) { params = nullptr; } @@ -57,7 +57,7 @@ void WebInterface::OnCall(uint16 opcode, EQ::Net::Packet &p) try { id = root["id"].asString(); } - catch (std::exception) { + catch (std::exception &) { id = ""; } @@ -82,7 +82,7 @@ void WebInterface::Send(const Json::Value &value) p.PutString(0, ss.str()); m_connection->Send(ServerOP_WebInterfaceCall, p); } - catch (std::exception) { + catch (std::exception &) { //Log error } } @@ -116,7 +116,7 @@ void WebInterface::SendEvent(const Json::Value &value) p.PutString(0, ss.str()); m_connection->Send(ServerOP_WebInterfaceEvent, p); } - catch (std::exception) { + catch (std::exception &) { //Log error } } diff --git a/world/worlddb.cpp b/world/worlddb.cpp index fbd4cdf94..f45207273 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -31,23 +31,27 @@ extern std::vector character_create_allocations; extern std::vector character_create_race_class_combos; -// the current stuff is at the bottom of this function -void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit) +/** + * @param account_id + * @param out_app + * @param client_version_bit + */ +void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit) { - /* Set Character Creation Limit */ - EQEmu::versions::ClientVersion client_version = EQEmu::versions::ConvertClientVersionBitToClientVersion(clientVersionBit); + EQEmu::versions::ClientVersion + client_version = EQEmu::versions::ConvertClientVersionBitToClientVersion(client_version_bit); size_t character_limit = EQEmu::constants::StaticLookup(client_version)->CharacterCreationLimit; - - // Validate against absolute server max - if (character_limit > EQEmu::constants::CHARACTER_CREATION_LIMIT) + + if (character_limit > EQEmu::constants::CHARACTER_CREATION_LIMIT) { character_limit = EQEmu::constants::CHARACTER_CREATION_LIMIT; + } // Force Titanium clients to use '8' - if (client_version == EQEmu::versions::ClientVersion::Titanium) + if (client_version == EQEmu::versions::ClientVersion::Titanium) { character_limit = 8; - - /* Get Character Info */ - std::string cquery = StringFormat( + } + + std::string character_list_query = StringFormat( "SELECT " "`id`, " // 0 "name, " // 1 @@ -71,237 +75,281 @@ void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **ou "zone_id " // 19 "FROM " "character_data " - "WHERE `account_id` = %i ORDER BY `name` LIMIT %u", accountID, character_limit); - auto results = database.QueryDatabase(cquery); + "WHERE `account_id` = %i AND deleted_at IS NULL ORDER BY `name` LIMIT %u", + account_id, + character_limit + ); + + auto results = database.QueryDatabase(character_list_query); size_t character_count = results.RowCount(); if (character_count == 0) { - *outApp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); - CharacterSelect_Struct *cs = (CharacterSelect_Struct *)(*outApp)->pBuffer; - cs->CharCount = 0; + *out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); + CharacterSelect_Struct *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer; + cs->CharCount = 0; cs->TotalChars = character_limit; return; } size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count); - *outApp = new EQApplicationPacket(OP_SendCharInfo, packet_size); + *out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size); - unsigned char *buff_ptr = (*outApp)->pBuffer; - CharacterSelect_Struct *cs = (CharacterSelect_Struct *)buff_ptr; + unsigned char *buff_ptr = (*out_app)->pBuffer; + CharacterSelect_Struct *cs = (CharacterSelect_Struct *) buff_ptr; - cs->CharCount = character_count; + cs->CharCount = character_count; cs->TotalChars = character_limit; buff_ptr += sizeof(CharacterSelect_Struct); for (auto row = results.begin(); row != results.end(); ++row) { - CharacterSelectEntry_Struct *cse = (CharacterSelectEntry_Struct *)buff_ptr; - PlayerProfile_Struct pp; - EQEmu::InventoryProfile inv; + CharacterSelectEntry_Struct *p_character_select_entry_struct = (CharacterSelectEntry_Struct *) buff_ptr; + PlayerProfile_Struct player_profile_struct; + EQEmu::InventoryProfile inventory_profile; - pp.SetPlayerProfileVersion(EQEmu::versions::ConvertClientVersionToMobVersion(client_version)); - inv.SetInventoryVersion(client_version); - inv.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support + player_profile_struct.SetPlayerProfileVersion(EQEmu::versions::ConvertClientVersionToMobVersion(client_version)); + inventory_profile.SetInventoryVersion(client_version); + inventory_profile.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support - uint32 character_id = (uint32)atoi(row[0]); - uint8 has_home = 0; - uint8 has_bind = 0; + uint32 character_id = (uint32) atoi(row[0]); + uint8 has_home = 0; + uint8 has_bind = 0; - memset(&pp, 0, sizeof(PlayerProfile_Struct)); - - /* Fill CharacterSelectEntry_Struct */ - memset(cse->Name, 0, sizeof(cse->Name)); - strcpy(cse->Name, row[1]); - cse->Class = (uint8)atoi(row[4]); - cse->Race = (uint32)atoi(row[3]); - cse->Level = (uint8)atoi(row[5]); - cse->ShroudClass = cse->Class; - cse->ShroudRace = cse->Race; - cse->Zone = (uint16)atoi(row[19]); - cse->Instance = 0; - cse->Gender = (uint8)atoi(row[2]); - cse->Face = (uint8)atoi(row[15]); + memset(&player_profile_struct, 0, sizeof(PlayerProfile_Struct)); - for (uint32 matslot = 0; matslot < EQEmu::textures::materialCount; matslot++) { // Processed below - cse->Equip[matslot].Material = 0; - cse->Equip[matslot].Unknown1 = 0; - cse->Equip[matslot].EliteModel = 0; - cse->Equip[matslot].HerosForgeModel = 0; - cse->Equip[matslot].Unknown2 = 0; - cse->Equip[matslot].Color = 0; - } + memset(p_character_select_entry_struct->Name, 0, sizeof(p_character_select_entry_struct->Name)); + strcpy(p_character_select_entry_struct->Name, row[1]); + p_character_select_entry_struct->Class = (uint8) atoi(row[4]); + p_character_select_entry_struct->Race = (uint32) atoi(row[3]); + p_character_select_entry_struct->Level = (uint8) atoi(row[5]); + p_character_select_entry_struct->ShroudClass = p_character_select_entry_struct->Class; + p_character_select_entry_struct->ShroudRace = p_character_select_entry_struct->Race; + p_character_select_entry_struct->Zone = (uint16) atoi(row[19]); + p_character_select_entry_struct->Instance = 0; + p_character_select_entry_struct->Gender = (uint8) atoi(row[2]); + p_character_select_entry_struct->Face = (uint8) atoi(row[15]); - cse->Unknown15 = 0xFF; - cse->Unknown19 = 0xFF; - cse->DrakkinTattoo = (uint32)atoi(row[17]); - cse->DrakkinDetails = (uint32)atoi(row[18]); - cse->Deity = (uint32)atoi(row[6]); - cse->PrimaryIDFile = 0; // Processed Below - cse->SecondaryIDFile = 0; // Processed Below - cse->HairColor = (uint8)atoi(row[9]); - cse->BeardColor = (uint8)atoi(row[10]); - cse->EyeColor1 = (uint8)atoi(row[11]); - cse->EyeColor2 = (uint8)atoi(row[12]); - cse->HairStyle = (uint8)atoi(row[13]); - cse->Beard = (uint8)atoi(row[14]); - cse->GoHome = 0; // Processed Below - cse->Tutorial = 0; // Processed Below - cse->DrakkinHeritage = (uint32)atoi(row[16]); - cse->Unknown1 = 0; - cse->Enabled = 1; - cse->LastLogin = (uint32)atoi(row[7]); // RoF2 value: 1212696584 - cse->Unknown2 = 0; - /* Fill End */ + for (uint32 material_slot = 0; material_slot < EQEmu::textures::materialCount; material_slot++) { + p_character_select_entry_struct->Equip[material_slot].Material = 0; + p_character_select_entry_struct->Equip[material_slot].Unknown1 = 0; + p_character_select_entry_struct->Equip[material_slot].EliteModel = 0; + p_character_select_entry_struct->Equip[material_slot].HerosForgeModel = 0; + p_character_select_entry_struct->Equip[material_slot].Unknown2 = 0; + p_character_select_entry_struct->Equip[material_slot].Color = 0; + } + + p_character_select_entry_struct->Unknown15 = 0xFF; + p_character_select_entry_struct->Unknown19 = 0xFF; + p_character_select_entry_struct->DrakkinTattoo = (uint32) atoi(row[17]); + p_character_select_entry_struct->DrakkinDetails = (uint32) atoi(row[18]); + p_character_select_entry_struct->Deity = (uint32) atoi(row[6]); + p_character_select_entry_struct->PrimaryIDFile = 0; // Processed Below + p_character_select_entry_struct->SecondaryIDFile = 0; // Processed Below + p_character_select_entry_struct->HairColor = (uint8) atoi(row[9]); + p_character_select_entry_struct->BeardColor = (uint8) atoi(row[10]); + p_character_select_entry_struct->EyeColor1 = (uint8) atoi(row[11]); + p_character_select_entry_struct->EyeColor2 = (uint8) atoi(row[12]); + p_character_select_entry_struct->HairStyle = (uint8) atoi(row[13]); + p_character_select_entry_struct->Beard = (uint8) atoi(row[14]); + p_character_select_entry_struct->GoHome = 0; // Processed Below + p_character_select_entry_struct->Tutorial = 0; // Processed Below + p_character_select_entry_struct->DrakkinHeritage = (uint32) atoi(row[16]); + p_character_select_entry_struct->Unknown1 = 0; + p_character_select_entry_struct->Enabled = 1; + p_character_select_entry_struct->LastLogin = (uint32) atoi(row[7]); // RoF2 value: 1212696584 + p_character_select_entry_struct->Unknown2 = 0; if (RuleB(World, EnableReturnHomeButton)) { int now = time(nullptr); - if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) - cse->GoHome = 1; + if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) { + p_character_select_entry_struct->GoHome = 1; + } } - if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) { - cse->Tutorial = 1; + if (RuleB(World, EnableTutorialButton) && (p_character_select_entry_struct->Level <= RuleI(World, MaxLevelForTutorial))) { + p_character_select_entry_struct->Tutorial = 1; } - /* Set Bind Point Data for any character that may possibly be missing it for any reason */ - cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` FROM `character_bind` WHERE `id` = %i LIMIT 5", character_id); - auto results_bind = database.QueryDatabase(cquery); - auto bind_count = results_bind.RowCount(); - for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { + /** + * Bind + */ + character_list_query = StringFormat( + "SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot` FROM `character_bind` WHERE `id` = %i LIMIT 5", + character_id + ); + auto results_bind = database.QueryDatabase(character_list_query); + auto bind_count = results_bind.RowCount(); + for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { if (row_b[6] && atoi(row_b[6]) == 4) { has_home = 1; // If our bind count is less than 5, we need to actually make use of this data so lets parse it if (bind_count < 5) { - pp.binds[4].zoneId = atoi(row_b[0]); - pp.binds[4].instance_id = atoi(row_b[1]); - pp.binds[4].x = atof(row_b[2]); - pp.binds[4].y = atof(row_b[3]); - pp.binds[4].z = atof(row_b[4]); - pp.binds[4].heading = atof(row_b[5]); + player_profile_struct.binds[4].zoneId = atoi(row_b[0]); + player_profile_struct.binds[4].instance_id = atoi(row_b[1]); + player_profile_struct.binds[4].x = atof(row_b[2]); + player_profile_struct.binds[4].y = atof(row_b[3]); + player_profile_struct.binds[4].z = atof(row_b[4]); + player_profile_struct.binds[4].heading = atof(row_b[5]); } } - if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; } + if (row_b[6] && atoi(row_b[6]) == 0) { has_bind = 1; } } if (has_home == 0 || has_bind == 0) { - cquery = StringFormat("SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i", - cse->Class, cse->Deity, cse->Race); - auto results_bind = database.QueryDatabase(cquery); - for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { + character_list_query = StringFormat( + "SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i", + p_character_select_entry_struct->Class, + p_character_select_entry_struct->Deity, + p_character_select_entry_struct->Race + ); + auto results_bind = database.QueryDatabase(character_list_query); + for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { /* If a bind_id is specified, make them start there */ if (atoi(row_d[1]) != 0) { - pp.binds[4].zoneId = (uint32)atoi(row_d[1]); - GetSafePoints(pp.binds[4].zoneId, 0, &pp.binds[4].x, &pp.binds[4].y, &pp.binds[4].z); + player_profile_struct.binds[4].zoneId = (uint32) atoi(row_d[1]); + GetSafePoints(player_profile_struct.binds[4].zoneId, 0, &player_profile_struct.binds[4].x, &player_profile_struct.binds[4].y, &player_profile_struct.binds[4].z); } - /* Otherwise, use the zone and coordinates given */ + /* Otherwise, use the zone and coordinates given */ else { - pp.binds[4].zoneId = (uint32)atoi(row_d[0]); + player_profile_struct.binds[4].zoneId = (uint32) atoi(row_d[0]); float x = atof(row_d[2]); float y = atof(row_d[3]); float z = atof(row_d[4]); - if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); } - pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z; + if (x == 0 && y == 0 && z == 0) { GetSafePoints(player_profile_struct.binds[4].zoneId, 0, &x, &y, &z); } + player_profile_struct.binds[4].x = x; + player_profile_struct.binds[4].y = y; + player_profile_struct.binds[4].z = z; } } - pp.binds[0] = pp.binds[4]; + player_profile_struct.binds[0] = player_profile_struct.binds[4]; /* If no home bind set, set it */ if (has_home == 0) { - std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" + std::string query = StringFormat( + "REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", - character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 4); - auto results_bset = QueryDatabase(query); + character_id, + player_profile_struct.binds[4].zoneId, + 0, + player_profile_struct.binds[4].x, + player_profile_struct.binds[4].y, + player_profile_struct.binds[4].z, + player_profile_struct.binds[4].heading, + 4 + ); + auto results_bset = QueryDatabase(query); } /* If no regular bind set, set it */ if (has_bind == 0) { - std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" + std::string query = StringFormat( + "REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", - character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0); - auto results_bset = QueryDatabase(query); + character_id, + player_profile_struct.binds[0].zoneId, + 0, + player_profile_struct.binds[0].x, + player_profile_struct.binds[0].y, + player_profile_struct.binds[0].z, + player_profile_struct.binds[0].heading, + 0 + ); + auto results_bset = QueryDatabase(query); } } /* If our bind count is less than 5, then we have null data that needs to be filled in. */ if (bind_count < 5) { // we know that home and main bind must be valid here, so we don't check those // we also use home to fill in the null data like live does. - for (int i = 1; i < 4; i++) { - if (pp.binds[i].zoneId != 0) // we assume 0 is the only invalid one ... + for (int i = 1; i < 4; i++) { + if (player_profile_struct.binds[i].zoneId != 0) { // we assume 0 is the only invalid one ... continue; + } - std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" + std::string query = StringFormat( + "REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, slot)" " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", - character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, i); - auto results_bset = QueryDatabase(query); + character_id, + player_profile_struct.binds[4].zoneId, + 0, + player_profile_struct.binds[4].x, + player_profile_struct.binds[4].y, + player_profile_struct.binds[4].z, + player_profile_struct.binds[4].heading, + i + ); + auto results_bset = QueryDatabase(query); } } - /* Bind End */ - /* Load Character Material Data for Char Select */ - cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id); - auto results_b = database.QueryDatabase(cquery); uint8 slot = 0; - for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { + character_list_query = StringFormat( + "SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", + character_id + ); + auto results_b = database.QueryDatabase(character_list_query); + uint8 slot = 0; + for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) { slot = atoi(row_b[0]); - pp.item_tint.Slot[slot].Red = atoi(row_b[1]); - pp.item_tint.Slot[slot].Green = atoi(row_b[2]); - pp.item_tint.Slot[slot].Blue = atoi(row_b[3]); - pp.item_tint.Slot[slot].UseTint = atoi(row_b[4]); + player_profile_struct.item_tint.Slot[slot].Red = atoi(row_b[1]); + player_profile_struct.item_tint.Slot[slot].Green = atoi(row_b[2]); + player_profile_struct.item_tint.Slot[slot].Blue = atoi(row_b[3]); + player_profile_struct.item_tint.Slot[slot].UseTint = atoi(row_b[4]); } - /* Character Material Data End */ - /* Load Inventory */ - // If we ensure that the material data is updated appropriately, we can do away with inventory loads - if (GetCharSelInventory(accountID, cse->Name, &inv)) { - const EQEmu::ItemData* item = nullptr; - const EQEmu::ItemInstance* inst = nullptr; - int16 invslot = 0; + if (GetCharSelInventory(account_id, p_character_select_entry_struct->Name, &inventory_profile)) { + const EQEmu::ItemData *item = nullptr; + const EQEmu::ItemInstance *inst = nullptr; + int16 inventory_slot = 0; for (uint32 matslot = EQEmu::textures::textureBegin; matslot < EQEmu::textures::materialCount; matslot++) { - invslot = EQEmu::InventoryProfile::CalcSlotFromMaterial(matslot); - if (invslot == INVALID_INDEX) { continue; } - inst = inv.GetItem(invslot); - if (inst == nullptr) { continue; } + inventory_slot = EQEmu::InventoryProfile::CalcSlotFromMaterial(matslot); + if (inventory_slot == INVALID_INDEX) { continue; } + inst = inventory_profile.GetItem(inventory_slot); + if (inst == nullptr) { + continue; + } item = inst->GetItem(); - if (item == nullptr) { continue; } + if (item == nullptr) { + continue; + } if (matslot > 6) { - uint32 idfile = 0; + uint32 item_id_file = 0; // Weapon Models if (inst->GetOrnamentationIDFile() != 0) { - idfile = inst->GetOrnamentationIDFile(); - cse->Equip[matslot].Material = idfile; + item_id_file = inst->GetOrnamentationIDFile(); + p_character_select_entry_struct->Equip[matslot].Material = item_id_file; } else { if (strlen(item->IDFile) > 2) { - idfile = atoi(&item->IDFile[2]); - cse->Equip[matslot].Material = idfile; + item_id_file = atoi(&item->IDFile[2]); + p_character_select_entry_struct->Equip[matslot].Material = item_id_file; } } if (matslot == EQEmu::textures::weaponPrimary) { - cse->PrimaryIDFile = idfile; + p_character_select_entry_struct->PrimaryIDFile = item_id_file; } else { - cse->SecondaryIDFile = idfile; + p_character_select_entry_struct->SecondaryIDFile = item_id_file; } } else { uint32 color = 0; - if (pp.item_tint.Slot[matslot].UseTint) { - color = pp.item_tint.Slot[matslot].Color; + if (player_profile_struct.item_tint.Slot[matslot].UseTint) { + color = player_profile_struct.item_tint.Slot[matslot].Color; } else { color = inst->GetColor(); } // Armor Materials/Models - cse->Equip[matslot].Material = item->Material; - cse->Equip[matslot].EliteModel = item->EliteMaterial; - cse->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot); - cse->Equip[matslot].Color = color; + p_character_select_entry_struct->Equip[matslot].Material = item->Material; + p_character_select_entry_struct->Equip[matslot].EliteModel = item->EliteMaterial; + p_character_select_entry_struct->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot); + p_character_select_entry_struct->Equip[matslot].Color = color; } } } else { - printf("Error loading inventory for %s\n", cse->Name); + printf("Error loading inventory for %s\n", p_character_select_entry_struct->Name); } - /* Load Inventory End */ buff_ptr += sizeof(CharacterSelectEntry_Struct); } diff --git a/world/worlddb.h b/world/worlddb.h index 036c0dc40..e367803ec 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -30,7 +30,7 @@ struct CharacterSelect_Struct; class WorldDatabase : public SharedDatabase { public: bool GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc, bool isTitanium); - void GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit); + void GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit); int MoveCharacterToBind(int CharID, uint8 bindnum = 0); void GetLauncherList(std::vector &result); diff --git a/zone/attack.cpp b/zone/attack.cpp index a48f4008e..308592041 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -724,20 +724,20 @@ int Mob::GetClassRaceACBonus() hardcap = 32; softcap = 15; } - int weight = IsClient() ? CastToClient()->CalcCurrentWeight() : 0; + int weight = IsClient() ? CastToClient()->CalcCurrentWeight()/10 : 0; if (weight < hardcap - 1) { - int temp = level + 5; + double temp = level + 5; if (weight > softcap) { - double redux = (weight - softcap) * 6.66667; + double redux = static_cast(weight - softcap) * 6.66667; redux = (100.0 - std::min(100.0, redux)) * 0.01; - temp = std::max(0, static_cast(temp * redux)); + temp = std::max(0.0, temp * redux); } - ac_bonus = (4 * temp) / 3; + ac_bonus = static_cast((4.0 * temp) / 3.0); } else if (weight > hardcap + 1) { - int temp = level + 5; - double multiplier = std::min(1.0, (weight - (hardcap - 10.0)) / 100.0); - temp = (4 * temp) / 3; + double temp = level + 5; + double multiplier = std::min(1.0, (weight - (static_cast(hardcap) - 10.0)) / 100.0); + temp = (4.0 * temp) / 3.0; ac_bonus -= static_cast(temp * multiplier); } } @@ -5491,4 +5491,4 @@ int32 Mob::GetHPRegen() const int32 Mob::GetManaRegen() const { return mana_regen; -} \ No newline at end of file +} diff --git a/zone/aura.cpp b/zone/aura.cpp index 0830816c1..50026f998 100644 --- a/zone/aura.cpp +++ b/zone/aura.cpp @@ -6,8 +6,9 @@ #include "raids.h" Aura::Aura(NPCType *type_data, Mob *owner, AuraRecord &record) - : NPC(type_data, 0, owner->GetPosition(), GravityBehavior::Flying), spell_id(record.spell_id), distance(record.distance), - remove_timer(record.duration), movement_timer(100), process_timer(1000), aura_id(-1) + : NPC(type_data, 0, owner->GetPosition(), GravityBehavior::Flying), spell_id(record.spell_id), + distance(record.distance), + remove_timer(record.duration), movement_timer(100), process_timer(1000), aura_id(-1) { GiveNPCTypeData(type_data); // we will delete this later on m_owner = owner->GetID(); @@ -17,42 +18,48 @@ Aura::Aura(NPCType *type_data, Mob *owner, AuraRecord &record) cast_timer.Disable(); // we don't want to be enabled yet } - if (record.aura_type < static_cast(AuraType::Max)) + if (record.aura_type < static_cast(AuraType::Max)) { type = static_cast(record.aura_type); - else + } + else { type = AuraType::OnAllGroupMembers; + } - if (record.spawn_type < static_cast(AuraSpawns::Max)) + if (record.spawn_type < static_cast(AuraSpawns::Max)) { spawn_type = static_cast(record.spawn_type); - else + } + else { spawn_type = AuraSpawns::GroupMembers; + } - if (record.movement < static_cast(AuraMovement::Max)) + if (record.movement < static_cast(AuraMovement::Max)) { movement_type = static_cast(record.movement); - else + } + else { movement_type = AuraMovement::Follow; + } switch (type) { - case AuraType::OnAllFriendlies: - process_func = &Aura::ProcessOnAllFriendlies; - break; - case AuraType::OnAllGroupMembers: - process_func = &Aura::ProcessOnAllGroupMembers; - break; - case AuraType::OnGroupMembersPets: - process_func = &Aura::ProcessOnGroupMembersPets; - break; - case AuraType::Totem: - process_func = &Aura::ProcessTotem; - break; - case AuraType::EnterTrap: - process_func = &Aura::ProcessEnterTrap; - break; - case AuraType::ExitTrap: - process_func = &Aura::ProcessExitTrap; - break; - default: - process_func = nullptr; + case AuraType::OnAllFriendlies: + process_func = &Aura::ProcessOnAllFriendlies; + break; + case AuraType::OnAllGroupMembers: + process_func = &Aura::ProcessOnAllGroupMembers; + break; + case AuraType::OnGroupMembersPets: + process_func = &Aura::ProcessOnGroupMembersPets; + break; + case AuraType::Totem: + process_func = &Aura::ProcessTotem; + break; + case AuraType::EnterTrap: + process_func = &Aura::ProcessEnterTrap; + break; + case AuraType::ExitTrap: + process_func = &Aura::ProcessExitTrap; + break; + default: + process_func = nullptr; } } @@ -64,9 +71,9 @@ Mob *Aura::GetOwner() // not 100% sure how this one should work and PVP affects ... void Aura::ProcessOnAllFriendlies(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this, distance); std::set delayed_remove; - bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter for (auto &e : mob_list) { auto mob = e.second; @@ -74,13 +81,16 @@ void Aura::ProcessOnAllFriendlies(Mob *owner) auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // we are already on the list, let's check for removal - if (DistanceSquared(GetPosition(), mob->GetPosition()) > distance) + if (DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { delayed_remove.insert(mob->GetID()); - } else { // not on list, lets check if we're in range + } + } + else { // not on list, lets check if we're in range if (DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } @@ -88,30 +98,34 @@ void Aura::ProcessOnAllFriendlies(Mob *owner) for (auto &e : delayed_remove) { auto mob = entity_list.GetMob(e); - if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + if (mob != nullptr && is_buff) { // some auras cast instant spells so no need to remove mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } casted_on.erase(e); } // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it - if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) { cast_timer.Start(); + } - if (!cast_timer.Enabled() || !cast_timer.Check()) + if (!cast_timer.Enabled() || !cast_timer.Check()) { return; + } for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); - if (mob != nullptr) + if (mob != nullptr) { SpellFinished(spell_id, mob); + } } } void Aura::ProcessOnAllGroupMembers(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this, distance); std::set delayed_remove; - bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter if (owner->IsRaidGrouped() && owner->IsClient()) { // currently raids are just client, but safety check auto raid = owner->GetRaid(); @@ -126,9 +140,12 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) auto idx = raid->GetPlayerIndex(c); if (c->GetID() == m_owner) { return DistanceSquared(GetPosition(), c->GetPosition()) <= distance; - } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + } + else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || + raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; - } else if (DistanceSquared(GetPosition(), c->GetPosition()) > distance) { + } + else if (DistanceSquared(GetPosition(), c->GetPosition()) > distance) { return false; } return true; @@ -138,9 +155,12 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) auto idx = raid->GetPlayerIndex(m->GetOwner()->CastToClient()); if (m->GetOwner()->GetID() == m_owner) { return DistanceSquared(GetPosition(), m->GetPosition()) <= distance; - } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + } + else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || + raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; - } else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { + } + else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { return false; } return true; @@ -148,14 +168,18 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) auto verify_raid_client_swarm = [&raid, &group_id, this](NPC *n) { auto owner = entity_list.GetMob(n->GetSwarmOwner()); - if (owner == nullptr) + if (owner == nullptr) { return false; + } auto idx = raid->GetPlayerIndex(owner->CastToClient()); if (owner->GetID() == m_owner) { return DistanceSquared(GetPosition(), n->GetPosition()) <= distance; - } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + } + else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || + raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; - } else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { + } + else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { return false; } return true; @@ -164,40 +188,52 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) for (auto &e : mob_list) { auto mob = e.second; // step 1: check if we're already managing this NPC's buff - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // verify still good! if (mob->IsClient()) { - if (!verify_raid_client(mob->CastToClient())) - delayed_remove.insert(mob->GetID()); - } else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { - if (!verify_raid_client_pet(mob)) - delayed_remove.insert(mob->GetID()); - } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { - auto npc = mob->CastToNPC(); - if (!verify_raid_client_swarm(npc)) + if (!verify_raid_client(mob->CastToClient())) { delayed_remove.insert(mob->GetID()); + } } - } else { // we're not on it! + else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { + if (!verify_raid_client_pet(mob)) { + delayed_remove.insert(mob->GetID()); + } + } + else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + auto npc = mob->CastToNPC(); + if (!verify_raid_client_swarm(npc)) { + delayed_remove.insert(mob->GetID()); + } + } + } + else { // we're not on it! if (mob->IsClient() && verify_raid_client(mob->CastToClient())) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { + } + } + else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + } + } + else if (mob->IsNPC() && mob->IsPetOwnerClient()) { auto npc = mob->CastToNPC(); if (verify_raid_client_swarm(npc)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } } - } else if (owner->IsGrouped()) { + } + else if (owner->IsGrouped()) { auto group = owner->GetGroup(); if (group == nullptr) { // uh oh owner->RemoveAura(GetID(), false, true); @@ -207,107 +243,133 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) // lambdas to make for loop less ugly auto verify_group_pet = [&group, this](Mob *m) { auto owner = m->GetOwner(); - if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), m->GetPosition()) <= distance) + if (owner != nullptr && group->IsGroupMember(owner) && + DistanceSquared(GetPosition(), m->GetPosition()) <= distance) { return true; + } return false; }; auto verify_group_swarm = [&group, this](NPC *n) { auto owner = entity_list.GetMob(n->GetSwarmOwner()); - if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), n->GetPosition()) <= distance) + if (owner != nullptr && group->IsGroupMember(owner) && + DistanceSquared(GetPosition(), n->GetPosition()) <= distance) { return true; + } return false; }; for (auto &e : mob_list) { auto mob = e.second; - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // make sure we're still valid if (mob->IsPet()) { - if (!verify_group_pet(mob)) + if (!verify_group_pet(mob)) { delayed_remove.insert(mob->GetID()); - } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { - if (!verify_group_swarm(mob->CastToNPC())) + } + } + else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { + if (!verify_group_swarm(mob->CastToNPC())) { delayed_remove.insert(mob->GetID()); - } else if (!group->IsGroupMember(mob) || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { + } + } + else if (!group->IsGroupMember(mob) || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { delayed_remove.insert(mob->GetID()); } - } else { // not on, check if we should be! + } + else { // not on, check if we should be! if (mob->IsPet() && verify_group_pet(mob)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { + } + } + else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (group->IsGroupMember(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + } + } + else if (group->IsGroupMember(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } - } else { - auto verify_solo = [&owner, this](Mob *m) { - if (m->IsPet() && m->GetOwnerID() == owner->GetID()) + } + else { + auto verify_solo = [&owner, this](Mob *m) { + if (m->IsPet() && m->GetOwnerID() == owner->GetID()) { return true; - else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == owner->GetID()) + } + else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == owner->GetID()) { return true; - else if (m->GetID() == owner->GetID()) + } + else if (m->GetID() == owner->GetID()) { return true; - else + } + else { return false; + } }; for (auto &e : mob_list) { - auto mob = e.second; - auto it = casted_on.find(mob->GetID()); + auto mob = e.second; + auto it = casted_on.find(mob->GetID()); bool good = verify_solo(mob); if (it != casted_on.end()) { // make sure still valid if (!good || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { delayed_remove.insert(mob->GetID()); } - } else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + } + else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } for (auto &e : delayed_remove) { auto mob = entity_list.GetMob(e); - if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + if (mob != nullptr && is_buff) { // some auras cast instant spells so no need to remove mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } casted_on.erase(e); } // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it - if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) { cast_timer.Start(); + } - if (!cast_timer.Enabled() || !cast_timer.Check()) + if (!cast_timer.Enabled() || !cast_timer.Check()) { return; + } // some auras have to recast (DRU for example, non-buff too) for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); - if (mob != nullptr) + if (mob != nullptr) { SpellFinished(spell_id, mob); + } } } void Aura::ProcessOnGroupMembersPets(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this,distance); std::set delayed_remove; - bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter // This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura) - auto group_member = owner->GetOwnerOrSelf(); + auto group_member = owner->GetOwnerOrSelf(); - if (group_member->IsRaidGrouped() && group_member->IsClient()) { // currently raids are just client, but safety check + if (group_member->IsRaidGrouped() && + group_member->IsClient()) { // currently raids are just client, but safety check auto raid = group_member->GetRaid(); if (raid == nullptr) { // well shit owner->RemoveAura(GetID(), false, true); @@ -320,9 +382,12 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) auto idx = raid->GetPlayerIndex(m->GetOwner()->CastToClient()); if (m->GetOwner()->GetID() == group_member->GetID()) { return DistanceSquared(GetPosition(), m->GetPosition()) <= distance; - } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + } + else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || + raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; - } else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { + } + else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) { return false; } return true; @@ -330,14 +395,18 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) auto verify_raid_client_swarm = [&raid, &group_id, &group_member, this](NPC *n) { auto owner = entity_list.GetMob(n->GetSwarmOwner()); - if (owner == nullptr) + if (owner == nullptr) { return false; + } auto idx = raid->GetPlayerIndex(owner->CastToClient()); if (owner->GetID() == group_member->GetID()) { return DistanceSquared(GetPosition(), n->GetPosition()) <= distance; - } else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) { + } + else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || + raid->members[idx].GroupNumber == 0xFFFFFFFF) { return false; - } else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { + } + else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) { return false; } return true; @@ -346,35 +415,44 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) for (auto &e : mob_list) { auto mob = e.second; // step 1: check if we're already managing this NPC's buff - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // verify still good! if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { - if (!verify_raid_client_pet(mob)) - delayed_remove.insert(mob->GetID()); - } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { - auto npc = mob->CastToNPC(); - if (!verify_raid_client_swarm(npc)) + if (!verify_raid_client_pet(mob)) { delayed_remove.insert(mob->GetID()); + } } - } else { // we're not on it! + else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + auto npc = mob->CastToNPC(); + if (!verify_raid_client_swarm(npc)) { + delayed_remove.insert(mob->GetID()); + } + } + } + else { // we're not on it! if (mob->IsClient()) { continue; // never hit client - } else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { + } + else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + } + } + else if (mob->IsNPC() && mob->IsPetOwnerClient()) { auto npc = mob->CastToNPC(); if (verify_raid_client_swarm(npc)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } } - } else if (group_member->IsGrouped()) { + } + else if (group_member->IsGrouped()) { auto group = group_member->GetGroup(); if (group == nullptr) { // uh oh owner->RemoveAura(GetID(), false, true); @@ -384,111 +462,131 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) // lambdas to make for loop less ugly auto verify_group_pet = [&group, this](Mob *m) { auto owner = m->GetOwner(); - if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), m->GetPosition()) <= distance) - return true; - return false; + return owner != nullptr && group->IsGroupMember(owner) && + DistanceSquared(GetPosition(), m->GetPosition()) <= distance; }; auto verify_group_swarm = [&group, this](NPC *n) { auto owner = entity_list.GetMob(n->GetSwarmOwner()); - if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), n->GetPosition()) <= distance) - return true; - return false; + return owner != nullptr && group->IsGroupMember(owner) && + DistanceSquared(GetPosition(), n->GetPosition()) <= distance; }; for (auto &e : mob_list) { auto mob = e.second; - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // make sure we're still valid if (mob->IsPet()) { - if (!verify_group_pet(mob)) - delayed_remove.insert(mob->GetID()); - } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { - if (!verify_group_swarm(mob->CastToNPC())) + if (!verify_group_pet(mob)) { delayed_remove.insert(mob->GetID()); + } } - } else { // not on, check if we should be! + else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) { + if (!verify_group_swarm(mob->CastToNPC())) { + delayed_remove.insert(mob->GetID()); + } + } + } + else { // not on, check if we should be! if (mob->IsClient()) { continue; - } else if (mob->IsPet() && verify_group_pet(mob)) { + } + else if (mob->IsPet() && verify_group_pet(mob)) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); - } else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { + } + } + else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } - } else { - auto verify_solo = [&group_member, this](Mob *m) { - if (m->IsPet() && m->GetOwnerID() == group_member->GetID()) + } + else { + auto verify_solo = [&group_member, this](Mob *m) { + if (m->IsPet() && m->GetOwnerID() == group_member->GetID()) { return true; - else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == group_member->GetID()) + } + else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == group_member->GetID()) { return true; - else + } + else { return false; + } }; for (auto &e : mob_list) { - auto mob = e.second; - auto it = casted_on.find(mob->GetID()); + auto mob = e.second; + auto it = casted_on.find(mob->GetID()); bool good = verify_solo(mob); if (it != casted_on.end()) { // make sure still valid if (!good || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) { delayed_remove.insert(mob->GetID()); } - } else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { + } + else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { casted_on.insert(mob->GetID()); - if (is_buff) + if (is_buff) { SpellFinished(spell_id, mob); + } } } } for (auto &e : delayed_remove) { auto mob = entity_list.GetMob(e); - if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + if (mob != nullptr && is_buff) { // some auras cast instant spells so no need to remove mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } casted_on.erase(e); } // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it - if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) { cast_timer.Start(); + } - if (!cast_timer.Enabled() || !cast_timer.Check()) + if (!cast_timer.Enabled() || !cast_timer.Check()) { return; + } // some auras have to recast (DRU for example, non-buff too) for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); - if (mob != nullptr) + if (mob != nullptr) { SpellFinished(spell_id, mob); + } } } void Aura::ProcessTotem(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this, distance); std::set delayed_remove; - bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter + bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter for (auto &e : mob_list) { auto mob = e.second; - if (mob == this) + if (mob == this) { continue; - if (mob == owner) + } + if (mob == owner) { continue; + } if (owner->IsAttackAllowed(mob)) { // might need more checks ... bool in_range = DistanceSquared(GetPosition(), mob->GetPosition()) <= distance; - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { - if (!in_range) + if (!in_range) { delayed_remove.insert(mob->GetID()); - } else if (in_range) { + } + } + else if (in_range) { casted_on.insert(mob->GetID()); SpellFinished(spell_id, mob); } @@ -497,33 +595,38 @@ void Aura::ProcessTotem(Mob *owner) for (auto &e : delayed_remove) { auto mob = entity_list.GetMob(e); - if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove + if (mob != nullptr && is_buff) { // some auras cast instant spells so no need to remove mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } casted_on.erase(e); } // so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it - if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) + if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty()) { cast_timer.Start(); + } - if (!cast_timer.Enabled() || !cast_timer.Check()) + if (!cast_timer.Enabled() || !cast_timer.Check()) { return; + } for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); - if (mob != nullptr) + if (mob != nullptr) { SpellFinished(spell_id, mob); + } } } void Aura::ProcessEnterTrap(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this, distance); for (auto &e : mob_list) { auto mob = e.second; - if (mob == this) + if (mob == this) { continue; + } // might need more checks ... if (owner->IsAttackAllowed(mob) && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) { SpellFinished(spell_id, mob); @@ -535,23 +638,25 @@ void Aura::ProcessEnterTrap(Mob *owner) void Aura::ProcessExitTrap(Mob *owner) { - auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline + auto &mob_list = entity_list.GetCloseMobList(this, distance); for (auto &e : mob_list) { auto mob = e.second; - if (mob == this) + if (mob == this) { continue; + } // might need more checks ... if (owner->IsAttackAllowed(mob)) { bool in_range = DistanceSquared(GetPosition(), mob->GetPosition()) <= distance; - auto it = casted_on.find(mob->GetID()); + auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { if (!in_range) { SpellFinished(spell_id, mob); owner->RemoveAura(GetID(), false); // if we're a buff we don't want to strip :P break; } - } else if (in_range) { + } + else if (in_range) { casted_on.insert(mob->GetID()); } } @@ -562,9 +667,14 @@ void Aura::ProcessExitTrap(Mob *owner) // and hard to reason about void Aura::ProcessSpawns() { - const auto &clients = entity_list.GetClientList(); - for (auto &e : clients) { - auto c = e.second; + const auto &clients = entity_list.GetCloseMobList(this, distance); + for (auto &e : clients) { + if (!e.second->IsClient()) { + continue; + } + + auto c = e.second->CastToClient(); + bool spawned = spawned_for.find(c->GetID()) != spawned_for.end(); if (ShouldISpawnFor(c)) { if (!spawned) { @@ -574,21 +684,22 @@ void Aura::ProcessSpawns() SendArmorAppearance(c); spawned_for.insert(c->GetID()); } - } else if (spawned) { + } + else if (spawned) { EQApplicationPacket app; CreateDespawnPacket(&app, false); c->QueuePacket(&app); spawned_for.erase(c->GetID()); } } - return; } bool Aura::Process() { // Aura::Depop clears buffs - if (p_depop) + if (p_depop) { return false; + } auto owner = entity_list.GetMob(m_owner); if (owner == nullptr) { @@ -604,7 +715,7 @@ bool Aura::Process() if (movement_type == AuraMovement::Follow && GetPosition() != owner->GetPosition() && movement_timer.Check()) { m_Position = owner->GetPosition(); auto app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); - auto spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; + auto spu = (PlayerPositionUpdateServer_Struct *) app->pBuffer; MakeSpawnUpdate(spu); auto it = spawned_for.begin(); while (it != spawned_for.end()) { @@ -612,7 +723,8 @@ bool Aura::Process() if (client) { client->QueuePacket(app); ++it; - } else { + } + else { it = spawned_for.erase(it); } } @@ -620,14 +732,17 @@ bool Aura::Process() } // TODO: waypoints? - if (!process_timer.Check()) + if (!process_timer.Check()) { return true; + } - if (spawn_type != AuraSpawns::Noone) - ProcessSpawns(); // bit of a hack + if (spawn_type != AuraSpawns::Noone) { + ProcessSpawns(); + } // bit of a hack - if (process_func) + if (process_func) { process_func(*this, owner); + } // TODO: quest calls return true; @@ -635,49 +750,61 @@ bool Aura::Process() bool Aura::ShouldISpawnFor(Client *c) { - if (spawn_type == AuraSpawns::Noone) + if (spawn_type == AuraSpawns::Noone) { return false; + } - if (spawn_type == AuraSpawns::Everyone) + if (spawn_type == AuraSpawns::Everyone) { return true; + } // hey, it's our owner! - if (c->GetID() == m_owner) + if (c->GetID() == m_owner) { return true; + } // so this one is a bit trickier auto owner = GetOwner(); - if (owner == nullptr) - return false; // hmm + if (owner == nullptr) { + return false; + } // hmm owner = owner->GetOwnerOrSelf(); // pet auras we need the pet's owner - if (owner == nullptr) // shouldn't really be needed + if (owner == nullptr) { // shouldn't really be needed return false; + } // gotta check again for pet aura case -.- - if (owner == c) + if (owner == c) { return true; + } if (owner->IsRaidGrouped() && owner->IsClient()) { auto raid = owner->GetRaid(); - if (raid == nullptr) - return false; // hmm - auto group_id = raid->GetGroup(owner->CastToClient()); - if (group_id == 0xFFFFFFFF) // owner handled above, and they're in a raid and groupless + if (raid == nullptr) { return false; + } // hmm + auto group_id = raid->GetGroup(owner->CastToClient()); + if (group_id == 0xFFFFFFFF) { // owner handled above, and they're in a raid and groupless + return false; + } auto idx = raid->GetPlayerIndex(c); - if (idx == 0xFFFFFFFF) // they're not in our raid! + if (idx == 0xFFFFFFFF) { // they're not in our raid! return false; + } - if (raid->members[idx].GroupNumber != group_id) // in our raid, but not our group + if (raid->members[idx].GroupNumber != group_id) { // in our raid, but not our group return false; + } return true; // we got here so we know that 1 they're in our raid and 2 they're in our group! - } else if (owner->IsGrouped()) { + } + else if (owner->IsGrouped()) { auto group = owner->GetGroup(); - if (group == nullptr) - return false; // hmm + if (group == nullptr) { + return false; + } // hmm // easy, in our group return group->IsGroupMember(c); @@ -693,22 +820,23 @@ void Aura::Depop(bool skip_strip) if (!skip_strip && IsBuffSpell(spell_id)) { for (auto &e : casted_on) { auto mob = entity_list.GetMob(e); - if (mob != nullptr) + if (mob != nullptr) { mob->BuffFadeBySpellIDAndCaster(spell_id, GetID()); + } } } casted_on.clear(); p_depop = true; } -// This creates an aura from a casted spell void Mob::MakeAura(uint16 spell_id) { // TODO: verify room in AuraMgr - if (!IsValidSpell(spell_id)) + if (!IsValidSpell(spell_id)) { return; + } - AuraRecord record; + AuraRecord record{}; if (!database.GetAuraEntry(spell_id, record)) { Message(Chat::Red, "Unable to find data for aura %s", spells[spell_id].name); LogError("Unable to find data for aura [{}], check auras table", spell_id); @@ -718,7 +846,7 @@ void Mob::MakeAura(uint16 spell_id) if (!IsValidSpell(record.spell_id)) { Message(Chat::Red, "Casted spell (%d) is not valid for aura %s", record.spell_id, spells[spell_id].name); LogError("Casted spell ([{}]) is not valid for aura [{}], check auras table", - record.spell_id, spell_id); + record.spell_id, spell_id); return; } @@ -729,23 +857,28 @@ void Mob::MakeAura(uint16 spell_id) bool trap = false; switch (static_cast(record.aura_type)) { - case AuraType::ExitTrap: - case AuraType::EnterTrap: - case AuraType::Totem: - trap = true; - break; - default: - trap = false; - break; + case AuraType::ExitTrap: + case AuraType::EnterTrap: + case AuraType::Totem: + trap = true; + break; + default: + trap = false; + break; } - if (!CanSpawnAura(trap)) + if (!CanSpawnAura(trap)) { return; + } const auto base = database.LoadNPCTypesData(record.npc_type); if (base == nullptr) { Message(Chat::Red, "Unable to load NPC data for aura %s", spells[spell_id].teleport_zone); - LogError("Unable to load NPC data for aura [{}] (NPC ID [{}]), check auras and npc_types tables", spells[spell_id].teleport_zone, record.npc_type); + LogError( + "Unable to load NPC data for aura [{}] (NPC ID [{}]), check auras and npc_types tables", + spells[spell_id].teleport_zone, + record.npc_type + ); return; } @@ -756,65 +889,82 @@ void Mob::MakeAura(uint16 spell_id) auto npc = new Aura(npc_type, this, record); npc->SetAuraID(spell_id); - if (trap) - npc->TryMoveAlong(5.0f, 0.0f, false); // try to place 5 units in front + if (trap) { + npc->TryMoveAlong(5.0f, 0.0f, false); + } // try to place 5 units in front entity_list.AddNPC(npc, false); - if (trap) + if (trap) { AddTrap(npc, record); - else + } + else { AddAura(npc, record); + } } bool ZoneDatabase::GetAuraEntry(uint16 spell_id, AuraRecord &record) { - auto query = StringFormat("SELECT npc_type, name, spell_id, distance, aura_type, spawn_type, movement, " - "duration, icon, cast_time FROM auras WHERE type='%d'", - spell_id); + auto query = StringFormat( + "SELECT npc_type, name, spell_id, distance, aura_type, spawn_type, movement, " + "duration, icon, cast_time FROM auras WHERE type='%d'", + spell_id + ); auto results = QueryDatabase(query); - if (!results.Success()) + if (!results.Success()) { return false; + } - if (results.RowCount() != 1) + if (results.RowCount() != 1) { return false; + } auto row = results.begin(); record.npc_type = atoi(row[0]); strn0cpy(record.name, row[1], 64); - record.spell_id = atoi(row[2]); - record.distance = atoi(row[3]); + record.spell_id = atoi(row[2]); + record.distance = atoi(row[3]); record.distance *= record.distance; // so we can avoid sqrt - record.aura_type = atoi(row[4]); + record.aura_type = atoi(row[4]); record.spawn_type = atoi(row[5]); - record.movement = atoi(row[6]); - record.duration = atoi(row[7]) * 1000; // DB is in seconds - record.icon = atoi(row[8]); - record.cast_time = atoi(row[9]) * 1000; // DB is in seconds + record.movement = atoi(row[6]); + record.duration = atoi(row[7]) * 1000; // DB is in seconds + record.icon = atoi(row[8]); + record.cast_time = atoi(row[9]) * 1000; // DB is in seconds return true; } void Mob::AddAura(Aura *aura, AuraRecord &record) { + LogAura( + "[AddAura] aura owner [{}] spawn_id [{}] aura_name [{}]", + GetCleanName(), + aura->GetID(), + aura->GetCleanName() + ); + // this is called only when it's safe assert(aura != nullptr); strn0cpy(aura_mgr.auras[aura_mgr.count].name, aura->GetCleanName(), 64); aura_mgr.auras[aura_mgr.count].spawn_id = aura->GetID(); - aura_mgr.auras[aura_mgr.count].aura = aura; - if (record.icon == -1) + aura_mgr.auras[aura_mgr.count].aura = aura; + if (record.icon == -1) { aura_mgr.auras[aura_mgr.count].icon = spells[record.spell_id].new_icon; - else + } + else { aura_mgr.auras[aura_mgr.count].icon = record.icon; + } + if (IsClient()) { - auto outapp = new EQApplicationPacket(OP_UpdateAura, sizeof(AuraCreate_Struct)); - auto aura_create = (AuraCreate_Struct *)outapp->pBuffer; + auto outapp = new EQApplicationPacket(OP_UpdateAura, sizeof(AuraCreate_Struct)); + auto aura_create = (AuraCreate_Struct *) outapp->pBuffer; aura_create->action = 0; - aura_create->type = 1; // this can be 0 sometimes too + aura_create->type = 1; // this can be 0 sometimes too strn0cpy(aura_create->aura_name, aura_mgr.auras[aura_mgr.count].name, 64); aura_create->entity_id = aura_mgr.auras[aura_mgr.count].spawn_id; - aura_create->icon = aura_mgr.auras[aura_mgr.count].icon; + aura_create->icon = aura_mgr.auras[aura_mgr.count].icon; CastToClient()->FastQueuePacket(&outapp); } // we can increment this now @@ -823,15 +973,24 @@ void Mob::AddAura(Aura *aura, AuraRecord &record) void Mob::AddTrap(Aura *aura, AuraRecord &record) { + LogAura( + "[AddTrap] aura owner [{}] spawn_id [{}] aura_name [{}]", + GetCleanName(), + aura->GetID(), + aura->GetCleanName() + ); + // this is called only when it's safe assert(aura != nullptr); strn0cpy(trap_mgr.auras[trap_mgr.count].name, aura->GetCleanName(), 64); trap_mgr.auras[trap_mgr.count].spawn_id = aura->GetID(); - trap_mgr.auras[trap_mgr.count].aura = aura; - if (record.icon == -1) + trap_mgr.auras[trap_mgr.count].aura = aura; + if (record.icon == -1) { trap_mgr.auras[trap_mgr.count].icon = spells[record.spell_id].new_icon; - else + } + else { trap_mgr.auras[trap_mgr.count].icon = record.icon; + } // doesn't send to client trap_mgr.count++; } @@ -841,7 +1000,8 @@ bool Mob::CanSpawnAura(bool trap) if (trap && !HasFreeTrapSlots()) { MessageString(Chat::SpellFailure, NO_MORE_TRAPS); return false; - } else if (!trap && !HasFreeAuraSlots()) { + } + else if (!trap && !HasFreeAuraSlots()) { MessageString(Chat::SpellFailure, NO_MORE_AURAS); return false; } @@ -861,8 +1021,16 @@ void Mob::RemoveAllAuras() // this is sent on camp/zone, so it just despawns? if (aura_mgr.count) { for (auto &e : aura_mgr.auras) { - if (e.aura) + if (e.aura) { + LogAura( + "[RemoveAllAuras] aura owner [{}] spawn_id [{}] aura_name [{}]", + GetCleanName(), + e.spawn_id, + e.name + ); + e.aura->Depop(); + } } } @@ -870,8 +1038,16 @@ void Mob::RemoveAllAuras() if (trap_mgr.count) { for (auto &e : trap_mgr.auras) { - if (e.aura) + if (e.aura) { + LogAura( + "[RemoveAllAuras] trap owner [{}] spawn_id [{}] aura_name [{}]", + GetCleanName(), + e.spawn_id, + e.name + ); + e.aura->Depop(); + } } } @@ -883,24 +1059,36 @@ void Mob::RemoveAura(int spawn_id, bool skip_strip, bool expired) for (int i = 0; i < aura_mgr.count; ++i) { auto &aura = aura_mgr.auras[i]; if (aura.spawn_id == spawn_id) { - if (aura.aura) + LogAura( + "[RemoveAura] mob [{}] spawn_id [{}] skip_strip [{}] expired [{}]", + GetCleanName(), + spawn_id, + skip_strip ? "true" : "false", + expired ? "true" : "false" + ); + + if (aura.aura) { aura.aura->Depop(skip_strip); + } if (expired && IsClient()) { + // TODO: verify color CastToClient()->SendColoredText( - Chat::Yellow, StringFormat("%s has expired.", aura.name)); // TODO: verify color + Chat::Yellow, + StringFormat("%s has expired.", aura.name) + ); // need to update client UI too auto app = new EQApplicationPacket(OP_UpdateAura, sizeof(AuraDestory_Struct)); - auto ads = (AuraDestory_Struct *)app->pBuffer; - ads->action = 1; // delete + auto ads = (AuraDestory_Struct *) app->pBuffer; + ads->action = 1; // delete ads->entity_id = spawn_id; CastToClient()->QueuePacket(app); safe_delete(app); } while (aura_mgr.count - 1 > i) { i++; - aura.spawn_id = aura_mgr.auras[i].spawn_id; - aura.icon = aura_mgr.auras[i].icon; - aura.aura = aura_mgr.auras[i].aura; + aura.spawn_id = aura_mgr.auras[i].spawn_id; + aura.icon = aura_mgr.auras[i].icon; + aura.aura = aura_mgr.auras[i].aura; aura_mgr.auras[i].aura = nullptr; strn0cpy(aura.name, aura_mgr.auras[i].name, 64); } @@ -912,16 +1100,18 @@ void Mob::RemoveAura(int spawn_id, bool skip_strip, bool expired) for (int i = 0; i < trap_mgr.count; ++i) { auto &aura = trap_mgr.auras[i]; if (aura.spawn_id == spawn_id) { - if (aura.aura) + if (aura.aura) { aura.aura->Depop(skip_strip); - if (expired && IsClient()) + } + if (expired && IsClient()) { CastToClient()->SendColoredText( - Chat::Yellow, StringFormat("%s has expired.", aura.name)); // TODO: verify color + Chat::Yellow, StringFormat("%s has expired.", aura.name)); + } // TODO: verify color while (trap_mgr.count - 1 > i) { i++; - aura.spawn_id = trap_mgr.auras[i].spawn_id; - aura.icon = trap_mgr.auras[i].icon; - aura.aura = trap_mgr.auras[i].aura; + aura.spawn_id = trap_mgr.auras[i].spawn_id; + aura.icon = trap_mgr.auras[i].icon; + aura.aura = trap_mgr.auras[i].aura; trap_mgr.auras[i].aura = nullptr; strn0cpy(aura.name, trap_mgr.auras[i].name, 64); } @@ -930,6 +1120,5 @@ void Mob::RemoveAura(int spawn_id, bool skip_strip, bool expired) } } - return; } diff --git a/zone/aura.h b/zone/aura.h index ff4f2d51c..ae4cd0a4a 100644 --- a/zone/aura.h +++ b/zone/aura.h @@ -73,7 +73,7 @@ private: int m_owner; int aura_id; // spell ID of the aura spell -1 if aura isn't from a casted spell int spell_id; // spell we cast - int distance; // distance we remove + float distance; // distance we remove Timer remove_timer; // when we depop Timer process_timer; // rate limit process calls Timer cast_timer; // some auras pulse diff --git a/zone/bot.cpp b/zone/bot.cpp index b5628ecb4..5579b4bc5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5234,7 +5234,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b return false; } -int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 spell_id) +int32 Bot::CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 spell_id) { const SPDat_Spell_Struct &spell = spells[spell_id]; int32 value = 0; @@ -5388,6 +5388,10 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 if (type == focusImprovedDamage && base1 > value) value = base1; break; + case SE_ImprovedDamage2: + if (type == focusImprovedDamage2 && base1 > value) + value = base1; + break; case SE_ImprovedHeal: if (type == focusImprovedHeal && base1 > value) value = base1; @@ -5499,6 +5503,11 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 value = base1; break; } + case SE_FcDamageAmt2: { + if(type == focusFcDamageAmt2) + value = base1; + break; + } case SE_FcDamageAmtCrit: { if(type == focusFcDamageAmtCrit) value = base1; @@ -5557,8 +5566,8 @@ int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 return (value * lvlModifier / 100); } -int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { - if (IsBardSong(spell_id) && bottype != BotfocusFcBaseEffects) +int32 Bot::GetBotFocusEffect(focusType bottype, uint16 spell_id) { + if (IsBardSong(spell_id) && bottype != focusFcBaseEffects) return 0; int32 realTotal = 0; @@ -5567,7 +5576,7 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { bool rand_effectiveness = false; //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance - if((bottype == BotfocusManaCost || bottype == BotfocusImprovedHeal || bottype == BotfocusImprovedDamage) && RuleB(Spells, LiveLikeFocusEffects)) + if(RuleB(Spells, LiveLikeFocusEffects) && (bottype == focusManaCost || bottype == focusImprovedHeal || bottype == focusImprovedDamage || bottype == focusImprovedDamage2 || bottype == focusResistRate)) rand_effectiveness = true; //Check if item focus effect exists for the client. @@ -5708,16 +5717,16 @@ int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { } } - if(bottype == BotfocusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact)) + if(bottype == focusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact)) return 100; - if(bottype == BotfocusReagentCost && (IsEffectInSpell(spell_id, SE_SummonItem) || IsSacrificeSpell(spell_id))) + if(bottype == focusReagentCost && (IsEffectInSpell(spell_id, SE_SummonItem) || IsSacrificeSpell(spell_id))) return 0; return (realTotal + realTotal2); } -int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus) { +int32 Bot::CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus) { if(!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) return 0; @@ -5847,7 +5856,21 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel return 0; break; case SE_ImprovedDamage: - if (bottype == BotfocusImprovedDamage) { + if (bottype == focusImprovedDamage) { + if(best_focus) { + if (focus_spell.base2[i] != 0) + value = focus_spell.base2[i]; + else + value = focus_spell.base[i]; + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) + value = focus_spell.base[i]; + else + value = zone->random.Int(focus_spell.base[i], focus_spell.base2[i]); + } + break; + case SE_ImprovedDamage2: + if (bottype == focusImprovedDamage2) { if(best_focus) { if (focus_spell.base2[i] != 0) value = focus_spell.base2[i]; @@ -5861,7 +5884,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } break; case SE_ImprovedHeal: - if (bottype == BotfocusImprovedHeal) { + if (bottype == focusImprovedHeal) { if(best_focus) { if (focus_spell.base2[i] != 0) value = focus_spell.base2[i]; @@ -5875,7 +5898,7 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } break; case SE_ReduceManaCost: - if (bottype == BotfocusManaCost) { + if (bottype == focusManaCost) { if(best_focus) { if (focus_spell.base2[i] != 0) value = focus_spell.base2[i]; @@ -5889,39 +5912,39 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } break; case SE_IncreaseSpellHaste: - if (bottype == BotfocusSpellHaste && focus_spell.base[i] > value) + if (bottype == focusSpellHaste && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_IncreaseSpellDuration: - if (bottype == BotfocusSpellDuration && focus_spell.base[i] > value) + if (bottype == focusSpellDuration && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_SpellDurationIncByTic: - if (bottype == BotfocusSpellDurByTic && focus_spell.base[i] > value) + if (bottype == focusSpellDurByTic && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_SwarmPetDuration: - if (bottype == BotfocusSwarmPetDuration && focus_spell.base[i] > value) + if (bottype == focusSwarmPetDuration && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_IncreaseRange: - if (bottype == BotfocusRange && focus_spell.base[i] > value) + if (bottype == focusRange && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_ReduceReagentCost: - if (bottype == BotfocusReagentCost && focus_spell.base[i] > value) + if (bottype == focusReagentCost && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_PetPowerIncrease: - if (bottype == BotfocusPetPower && focus_spell.base[i] > value) + if (bottype == focusPetPower && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_SpellResistReduction: - if (bottype == BotfocusResistRate && focus_spell.base[i] > value) + if (bottype == focusResistRate && focus_spell.base[i] > value) value = focus_spell.base[i]; break; case SE_SpellHateMod: - if (bottype == BotfocusSpellHateMod) { + if (bottype == focusSpellHateMod) { if(value != 0) { if(value > 0) { if(focus_spell.base[i] > value) @@ -5936,12 +5959,12 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel } break; case SE_ReduceReuseTimer: { - if(bottype == BotfocusReduceRecastTime) + if(bottype == focusReduceRecastTime) value = (focus_spell.base[i] / 1000); break; } case SE_TriggerOnCast: { - if(bottype == BotfocusTriggerOnCast) { + if(bottype == focusTriggerOnCast) { if(zone->random.Int(0, 100) <= focus_spell.base[i]) value = focus_spell.base2[i]; else @@ -5950,24 +5973,24 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel break; } case SE_FcSpellVulnerability: { - if(bottype == BotfocusSpellVulnerability) + if(bottype == focusSpellVulnerability) value = focus_spell.base[i]; break; } case SE_BlockNextSpellFocus: { - if(bottype == BotfocusBlockNextSpell) { + if(bottype == focusBlockNextSpell) { if(zone->random.Int(1, 100) <= focus_spell.base[i]) value = 1; } break; } case SE_FcTwincast: { - if(bottype == BotfocusTwincast) + if(bottype == focusTwincast) value = focus_spell.base[i]; break; } case SE_SympatheticProc: { - if(bottype == BotfocusSympatheticProc) { + if(bottype == focusSympatheticProc) { float ProcChance = GetSympatheticProcChances(spell_id, focus_spell.base[i]); if(zone->random.Real(0, 1) <= ProcChance) value = focus_id; @@ -5977,49 +6000,54 @@ int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel break; } case SE_FcDamageAmt: { - if(bottype == BotfocusFcDamageAmt) + if(bottype == focusFcDamageAmt) + value = focus_spell.base[i]; + break; + } + case SE_FcDamageAmt2: { + if(bottype == focusFcDamageAmt2) value = focus_spell.base[i]; break; } case SE_FcDamageAmtCrit: { - if(bottype == BotfocusFcDamageAmtCrit) + if(bottype == focusFcDamageAmtCrit) value = focus_spell.base[i]; break; } case SE_FcHealAmtIncoming: - if(bottype == BotfocusFcHealAmtIncoming) + if(bottype == focusFcHealAmtIncoming) value = focus_spell.base[i]; break; case SE_FcHealPctCritIncoming: - if (bottype == BotfocusFcHealPctCritIncoming) + if (bottype == focusFcHealPctCritIncoming) value = focus_spell.base[i]; break; case SE_FcHealAmtCrit: - if(bottype == BotfocusFcHealAmtCrit) + if(bottype == focusFcHealAmtCrit) value = focus_spell.base[i]; break; case SE_FcHealAmt: - if(bottype == BotfocusFcHealAmt) + if(bottype == focusFcHealAmt) value = focus_spell.base[i]; break; case SE_FcHealPctIncoming: - if(bottype == BotfocusFcHealPctIncoming) + if(bottype == focusFcHealPctIncoming) value = focus_spell.base[i]; break; case SE_FcBaseEffects: { - if (bottype == BotfocusFcBaseEffects) + if (bottype == focusFcBaseEffects) value = focus_spell.base[i]; break; } case SE_FcDamagePctCrit: { - if(bottype == BotfocusFcDamagePctCrit) + if(bottype == focusFcDamagePctCrit) value = focus_spell.base[i]; break; } case SE_FcIncreaseNumHits: { - if(bottype == BotfocusIncreaseNumHits) + if(bottype == focusIncreaseNumHits) value = focus_spell.base[i]; break; @@ -6559,14 +6587,14 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { int32 Bot::CheckAggroAmount(uint16 spellid) { int32 AggroAmount = Mob::CheckAggroAmount(spellid, nullptr); - int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); + int32 focusAggro = GetBotFocusEffect(focusSpellHateMod, spellid); AggroAmount = (AggroAmount * (100 + focusAggro) / 100); return AggroAmount; } int32 Bot::CheckHealAggroAmount(uint16 spellid, Mob *target, uint32 heal_possible) { int32 AggroAmount = Mob::CheckHealAggroAmount(spellid, target, heal_possible); - int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); + int32 focusAggro = GetBotFocusEffect(focusSpellHateMod, spellid); AggroAmount = (AggroAmount * (100 + focusAggro) / 100); return AggroAmount; } @@ -6840,7 +6868,7 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { bool Critical = false; int32 value_BaseEffect = 0; - value_BaseEffect = (value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id) / 100)); + value_BaseEffect = (value + (value*GetBotFocusEffect(focusFcBaseEffects, spell_id) / 100)); // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40. if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40) value -= ((GetLevel() - 40) * 20); @@ -6868,16 +6896,18 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { ratio += RuleI(Spells, WizCritRatio); if (Critical) { value = (value_BaseEffect * ratio / 100); - value += (value_BaseEffect * GetBotFocusEffect(BotfocusImprovedDamage, spell_id) / 100); - value += (int(value_BaseEffect * GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id) / 100) * ratio / 100); + value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage, spell_id) / 100); + value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage2, spell_id) / 100); + value += (int(value_BaseEffect * GetBotFocusEffect(focusFcDamagePctCrit, spell_id) / 100) * ratio / 100); if (target) { value += (int(value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100) * ratio / 100); value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= (GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id) * ratio / 100); + value -= (GetBotFocusEffect(focusFcDamageAmtCrit, spell_id) * ratio / 100); - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(focusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(focusFcDamageAmt2, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) value += (GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value) * ratio / 100); @@ -6889,15 +6919,17 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } value = value_BaseEffect; - value += (value_BaseEffect * GetBotFocusEffect(BotfocusImprovedDamage, spell_id) / 100); - value += (value_BaseEffect * GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id) / 100); + value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage, spell_id) / 100); + value += (value_BaseEffect * GetBotFocusEffect(focusImprovedDamage2, spell_id) / 100); + value += (value_BaseEffect * GetBotFocusEffect(focusFcDamagePctCrit, spell_id) / 100); if (target) { value += (value_BaseEffect * target->GetVulnerability(this, spell_id, 0) / 100); value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(focusFcDamageAmtCrit, spell_id); + value -= GetBotFocusEffect(focusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(focusFcDamageAmt2, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); @@ -6912,9 +6944,9 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int32 chance = 0; int8 modifier = 1; bool Critical = false; - value_BaseEffect = (value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id) / 100)); + value_BaseEffect = (value + (value*GetBotFocusEffect(focusFcBaseEffects, spell_id) / 100)); value = value_BaseEffect; - value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id) / 100); + value += int(value_BaseEffect*GetBotFocusEffect(focusImprovedHeal, spell_id) / 100); if(spells[spell_id].buffduration < 1) { chance += (itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance); chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); @@ -6927,8 +6959,8 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { } value *= modifier; - value += (GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier); - value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id); + value += (GetBotFocusEffect(focusFcHealAmtCrit, spell_id) * modifier); + value += GetBotFocusEffect(focusFcHealAmt, spell_id); value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) @@ -6953,7 +6985,7 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { int32 cast_reducer = 0; - cast_reducer += GetBotFocusEffect(BotfocusSpellHaste, spell_id); + cast_reducer += GetBotFocusEffect(focusSpellHaste, spell_id); uint8 botlevel = GetLevel(); uint8 botclass = GetClass(); if (botlevel >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id) && (botclass == SHADOWKNIGHT || botclass == RANGER || botclass == PALADIN || botclass == BEASTLORD )) @@ -7088,7 +7120,7 @@ int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) { } } - int32 focus_redux = GetBotFocusEffect(BotfocusManaCost, spell_id); + int32 focus_redux = GetBotFocusEffect(focusManaCost, spell_id); if(focus_redux > 0) PercentManaReduction += zone->random.Real(1, (double)focus_redux); @@ -7115,14 +7147,14 @@ int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) { float Bot::GetActSpellRange(uint16 spell_id, float range) { float extrange = 100; - extrange += GetBotFocusEffect(BotfocusRange, spell_id); + extrange += GetBotFocusEffect(focusRange, spell_id); return ((range * extrange) / 100); } int32 Bot::GetActSpellDuration(uint16 spell_id, int32 duration) { int increase = 100; - increase += GetBotFocusEffect(BotfocusSpellDuration, spell_id); - int tic_inc = 0; tic_inc = GetBotFocusEffect(BotfocusSpellDurByTic, spell_id); + increase += GetBotFocusEffect(focusSpellDuration, spell_id); + int tic_inc = 0; tic_inc = GetBotFocusEffect(focusSpellDurByTic, spell_id); if(IsBeneficialSpell(spell_id)) { switch (GetAA(aaSpellCastingReinforcement)) { diff --git a/zone/bot.h b/zone/bot.h index 97bb6892c..b456fd77f 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -99,40 +99,6 @@ class Bot : public NPC { friend class Mob; public: // Class enums - enum BotfocusType { //focus types - BotfocusSpellHaste = 1, - BotfocusSpellDuration, - BotfocusRange, - BotfocusReagentCost, - BotfocusManaCost, - BotfocusImprovedHeal, - BotfocusImprovedDamage, - BotfocusImprovedDOT, //i dont know about this... - BotfocusFcDamagePctCrit, - BotfocusImprovedUndeadDamage, - BotfocusPetPower, - BotfocusResistRate, - BotfocusSpellHateMod, - BotfocusTriggerOnCast, - BotfocusSpellVulnerability, - BotfocusTwincast, - BotfocusSympatheticProc, - BotfocusFcDamageAmt, - BotfocusFcDamageAmtCrit, - BotfocusSpellDurByTic, - BotfocusSwarmPetDuration, - BotfocusReduceRecastTime, - BotfocusBlockNextSpell, - BotfocusFcHealPctIncoming, - BotfocusFcDamageAmtIncoming, - BotfocusFcHealAmtIncoming, - BotfocusFcBaseEffects, - BotfocusIncreaseNumHits, - BotfocusFcHealPctCritIncoming, - BotfocusFcHealAmt, - BotfocusFcHealAmtCrit, - }; - enum BotTradeType { // types of trades a bot can do BotTradeClientNormal, BotTradeClientNoDropNoTrade @@ -636,9 +602,9 @@ protected: virtual void PetAIProcess(); virtual void BotMeditate(bool isSitting); virtual bool CheckBotDoubleAttack(bool Triple = false); - virtual int32 GetBotFocusEffect(BotfocusType bottype, uint16 spell_id); - virtual int32 CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false); - virtual int32 CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 spell_id); + virtual int32 GetBotFocusEffect(focusType bottype, uint16 spell_id); + virtual int32 CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false); + virtual int32 CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 spell_id); virtual void PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client); virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 8030d4898..dc9d40aab 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -3414,6 +3414,12 @@ void bot_command_help(Client *c, const Seperator *sep) c->Message(m_usage, "%c%s - %s", BOT_COMMAND_CHAR, command_iter.first.c_str(), command_iter.second->desc == nullptr ? "[no description]" : command_iter.second->desc); ++bot_commands_shown; } + if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { + int i = parse->EventPlayer(EVENT_BOT_COMMAND, c, sep->msg, 0); + if (i >= 1) { + bot_commands_shown += i; + } + } c->Message(m_message, "%d bot command%s listed.", bot_commands_shown, bot_commands_shown != 1 ? "s" : ""); c->Message(m_note, "type %ccommand [help | usage] for more information", BOT_COMMAND_CHAR); } diff --git a/zone/client.cpp b/zone/client.cpp index f77dc1fd4..6b366afba 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1106,40 +1106,56 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s case ChatChannel_Say: { /* Say */ if(message[0] == COMMAND_CHAR) { if(command_dispatch(this, message) == -2) { - if(parse->PlayerHasQuestSub(EVENT_COMMAND)) { + if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0); - if(i == 0 && !RuleB(Chat, SuppressCommandErrors)) { + if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { Message(Chat::Red, "Command '%s' not recognized.", message); } - } else { - if(!RuleB(Chat, SuppressCommandErrors)) + } + else if (parse->PlayerHasQuestSub(EVENT_SAY)) { + int i = parse->EventPlayer(EVENT_SAY, this, message, 0); + if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { Message(Chat::Red, "Command '%s' not recognized.", message); + } + } + else { + if (!RuleB(Chat, SuppressCommandErrors)) { + Message(Chat::Red, "Command '%s' not recognized.", message); + } } } break; } - if (EQEmu::ProfanityManager::IsCensorshipActive()) - EQEmu::ProfanityManager::RedactMessage(message); - #ifdef BOTS if (message[0] == BOT_COMMAND_CHAR) { if (bot_command_dispatch(this, message) == -2) { - if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { - int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0); + if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { + int i = parse->EventPlayer(EVENT_BOT_COMMAND, this, message, 0); + if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { + Message(Chat::Red, "Bot command '%s' not recognized.", message); + } + } + else if (parse->PlayerHasQuestSub(EVENT_SAY)) { + int i = parse->EventPlayer(EVENT_SAY, this, message, 0); if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { Message(Chat::Red, "Bot command '%s' not recognized.", message); } } else { - if (!RuleB(Chat, SuppressCommandErrors)) + if (!RuleB(Chat, SuppressCommandErrors)) { Message(Chat::Red, "Bot command '%s' not recognized.", message); + } } } break; } #endif + if (EQEmu::ProfanityManager::IsCensorshipActive()) { + EQEmu::ProfanityManager::RedactMessage(message); + } + Mob* sender = this; if (GetPet() && GetTarget() == GetPet() && GetPet()->FindType(SE_VoiceGraft)) sender = GetPet(); @@ -8517,13 +8533,13 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct)); QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer; - qr->mob_id = target->GetID(); // Entity ID for the from mob name + qr->mob_id = target ? target->GetID() : 0; // Entity ID for the from mob name qr->target_id = GetID(); // The Client ID (this) qr->copper = copper; qr->silver = silver; qr->gold = gold; qr->platinum = platinum; - qr->item_id = itemid; + qr->item_id[0] = itemid; qr->exp_reward = exp; if (copper > 0 || silver > 0 || gold > 0 || platinum > 0) @@ -8534,7 +8550,7 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, if (faction) { - if (target->IsNPC()) + if (target && target->IsNPC()) { int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); @@ -8550,6 +8566,42 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, safe_delete(outapp); } +void Client::QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction) +{ + auto outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct)); + memset(outapp->pBuffer, 0, sizeof(QuestReward_Struct)); + QuestReward_Struct* qr = (QuestReward_Struct*)outapp->pBuffer; + + memcpy(qr, &reward, sizeof(QuestReward_Struct)); + + // not set in caller because reasons + qr->mob_id = target ? target->GetID() : 0; // Entity ID for the from mob name + + if (reward.copper > 0 || reward.silver > 0 || reward.gold > 0 || reward.platinum > 0) + AddMoneyToPP(reward.copper, reward.silver, reward.gold, reward.platinum, false); + + for (int i = 0; i < QUESTREWARD_COUNT; ++i) + if (reward.item_id[i] > 0) + SummonItem(reward.item_id[i], 0, 0, 0, 0, 0, 0, false, EQEmu::invslot::slotCursor); + + if (faction) + { + if (target && target->IsNPC()) + { + int32 nfl_id = target->CastToNPC()->GetNPCFactionID(); + SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true); + qr->faction = target->CastToNPC()->GetPrimaryFaction(); + qr->faction_mod = 1; // Too lazy to get real value, not sure if this is even used by client anyhow. + } + } + + if (reward.exp_reward> 0) + AddEXP(reward.exp_reward); + + QueuePacket(outapp, true, Client::CLIENT_CONNECTED); + safe_delete(outapp); +} + void Client::SendHPUpdateMarquee(){ if (!this || !this->IsClient() || !this->current_hp || !this->max_hp) return; diff --git a/zone/client.h b/zone/client.h index 4ff431520..300073bb5 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1282,6 +1282,7 @@ public: int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false); void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false); + void QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction); // TODO: Fix faction processing void ResetHPUpdateTimer() { hpupdate_timer.Start(); } diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 1b8735d7d..255201090 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -336,6 +336,10 @@ int32 Client::CalcMaxHP() current_hp = curHP_cap; } } + + // hack fix for client health not reflecting server value + last_max_hp = 0; + return max_hp; } @@ -610,14 +614,13 @@ int32 Client::CalcBaseMana() case 'I': WisInt = GetINT(); if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + ConvertedWisInt = WisInt; + int over200 = WisInt; if (WisInt > 100) { - ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); - if (WisInt > 201) { - ConvertedWisInt -= ((WisInt - 201) * 5 / 4); + if (WisInt > 200) { + over200 = (WisInt - 200) / -2 + WisInt; } - } - else { - ConvertedWisInt = WisInt; + ConvertedWisInt = (3 * over200 - 300) / 2 + over200; } auto base_data = database.GetBaseData(GetLevel(), GetClass()); if (base_data) { @@ -643,14 +646,13 @@ int32 Client::CalcBaseMana() case 'W': WisInt = GetWIS(); if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + ConvertedWisInt = WisInt; + int over200 = WisInt; if (WisInt > 100) { - ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); - if (WisInt > 201) { - ConvertedWisInt -= ((WisInt - 201) * 5 / 4); + if (WisInt > 200) { + over200 = (WisInt - 200) / -2 + WisInt; } - } - else { - ConvertedWisInt = WisInt; + ConvertedWisInt = (3 * over200 - 300) / 2 + over200; } auto base_data = database.GetBaseData(GetLevel(), GetClass()); if (base_data) { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4291cd054..dfb7ab673 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -905,6 +905,8 @@ void Client::CompleteConnect() entity_list.RefreshClientXTargets(this); worldserver.RequestTellQueue(GetName()); + + entity_list.ScanCloseMobs(close_mobs, this); } // connecting opcode handlers @@ -8286,7 +8288,18 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) if (GetTarget() && GetTarget()->IsNPC()) { if (silentsaylink) { parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + + if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) { + parse->EventPlayer(EVENT_COMMAND, this, response.c_str(), 0); + } +#ifdef BOTS + else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { + parse->EventPlayer(EVENT_BOT_COMMAND, this, response.c_str(), 0); + } +#endif + else { + parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + } } else { Message(Chat::LightGray, "You say, '%s'", response.c_str()); @@ -8296,7 +8309,17 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) } else { if (silentsaylink) { - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + if (response[0] == '#' && parse->PlayerHasQuestSub(EVENT_COMMAND)) { + parse->EventPlayer(EVENT_COMMAND, this, response.c_str(), 0); + } +#ifdef BOTS + else if (response[0] == '^' && parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { + parse->EventPlayer(EVENT_BOT_COMMAND, this, response.c_str(), 0); + } +#endif + else { + parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + } } else { Message(Chat::LightGray, "You say, '%s'", response.c_str()); @@ -11107,6 +11130,11 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) break; } + if (player_to_invite_group && player_to_invite_group->IsGroupMember(this)) { + MessageString(Chat::Red, ALREADY_IN_PARTY); + break; + } + if (player_to_invite_group && !player_to_invite_group->IsLeader(player_to_invite)) { Message(Chat::Red, "You can only invite an ungrouped player or group leader to join your raid."); break; diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 14b2036cf..d8ec451a9 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -256,28 +256,7 @@ bool Client::Process() { * Used in aggro checks */ if (mob_close_scan_timer.Check()) { - close_mobs.clear(); - - float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); - auto &mob_list = entity_list.GetMobList(); - - for (auto itr : mob_list) { - Mob *mob = itr.second; - float distance = DistanceSquared(m_Position, mob->GetPosition()); - - if (mob->GetID() <= 0) { - continue; - } - - if (mob->IsNPC() || mob->IsClient()) { - if (distance <= scan_range) { - close_mobs.insert(std::pair(mob->GetID(), mob)); - } - else if ((mob->GetAggroRange() * mob->GetAggroRange()) > scan_range) { - close_mobs.insert(std::pair(mob->GetID(), mob)); - } - } - } + entity_list.ScanCloseMobs(close_mobs, this); } bool may_use_attacks = false; diff --git a/zone/command.cpp b/zone/command.cpp index cf8e47d6a..877529c2b 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -783,6 +783,12 @@ void command_help(Client *c, const Seperator *sep) commands_shown++; c->Message(Chat::White, " %c%s %s", COMMAND_CHAR, cur->first.c_str(), cur->second->desc == nullptr?"":cur->second->desc); } + if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { + int i = parse->EventPlayer(EVENT_COMMAND, c, sep->msg, 0); + if (i >= 1) { + commands_shown += i; + } + } c->Message(Chat::White, "%d command%s listed.", commands_shown, commands_shown!=1?"s":""); } @@ -2167,25 +2173,6 @@ void command_spoff(Client *c, const Seperator *sep) safe_delete(outapp); } -void command_itemtest(Client *c, const Seperator *sep) -{ - char chBuffer[8192] = {0}; - //Using this to determine new item layout - FILE* f = nullptr; - if (!(f = fopen("c:\\EQEMUcvs\\ItemDump.txt", "rb"))) { - c->Message(Chat::Red, "Error: Could not open c:\\EQEMUcvs\\ItemDump.txt"); - return; - } - - fread(chBuffer, sizeof(chBuffer), sizeof(char), f); - fclose(f); - - auto outapp = new EQApplicationPacket(OP_ItemLinkResponse, strlen(chBuffer) + 5); - memcpy(&outapp->pBuffer[4], chBuffer, strlen(chBuffer)); - c->QueuePacket(outapp); - safe_delete(outapp); -} - void command_gassign(Client *c, const Seperator *sep) { if (sep->IsNumber(1) && c->GetTarget() && c->GetTarget()->IsNPC() && c->GetTarget()->CastToNPC()->GetSpawnPointID() > 0) { @@ -13264,8 +13251,8 @@ void command_bot(Client *c, const Seperator *sep) } if (bot_command_dispatch(c, bot_message.c_str()) == -2) { - if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { - int i = parse->EventPlayer(EVENT_COMMAND, c, bot_message, 0); + if (parse->PlayerHasQuestSub(EVENT_BOT_COMMAND)) { + int i = parse->EventPlayer(EVENT_BOT_COMMAND, c, bot_message, 0); if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) { c->Message(Chat::Red, "Bot command '%s' not recognized.", bot_message.c_str()); } diff --git a/zone/command.h b/zone/command.h index 1c9d8fe7c..d64950f9c 100644 --- a/zone/command.h +++ b/zone/command.h @@ -150,7 +150,6 @@ void command_ipc(Client *c, const Seperator *sep); void command_iplookup(Client *c, const Seperator *sep); void command_iteminfo(Client *c, const Seperator *sep); void command_itemsearch(Client *c, const Seperator *sep); -void command_itemtest(Client *c, const Seperator *sep); void command_kick(Client *c, const Seperator *sep); void command_killallnpcs(Client *c, const Seperator *sep); void command_kill(Client *c, const Seperator *sep); diff --git a/zone/common.h b/zone/common.h index e06fa134a..2d97527fc 100644 --- a/zone/common.h +++ b/zone/common.h @@ -647,6 +647,19 @@ enum { SKILLUP_FAILURE = 2 }; +enum { + GridCircular, + GridRandom10, + GridRandom, + GridPatrol, + GridOneWayRepop, + GridRand5LoS, + GridOneWayDepop, + GridCenterPoint, + GridRandomCenterPoint, + GridRandomPath +}; + typedef enum { petFamiliar, //only listens to /pet get lost petAnimation, //does not listen to any commands diff --git a/zone/effects.cpp b/zone/effects.cpp index 5528f784a..7e2ecdbc7 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -809,11 +809,13 @@ void EntityList::AESpell( * 1 = PC * 2 = NPC */ - if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc()) { + if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsClient() && !current_mob->IsMerc() && + !current_mob->IsBot()) { continue; } - if (spells[spell_id].pcnpc_only_flag == 2 && (current_mob->IsClient() || current_mob->IsMerc())) { + if (spells[spell_id].pcnpc_only_flag == 2 && + (current_mob->IsClient() || current_mob->IsMerc() || current_mob->IsBot())) { continue; } diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 920d3a249..f89c09ad4 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -98,7 +98,7 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_DUEL_LOSE", "EVENT_ENCOUNTER_LOAD", "EVENT_ENCOUNTER_UNLOAD", - "EVENT_SAY", + "EVENT_COMMAND", "EVENT_DROP_ITEM", "EVENT_DESTROY_ITEM", "EVENT_FEIGN_DEATH", @@ -119,6 +119,7 @@ const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_DEATH_ZONE", "EVENT_USE_SKILL", "EVENT_COMBINE_VALIDATE", + "EVENT_BOT_COMMAND" }; PerlembParser::PerlembParser() : perl(nullptr) @@ -1542,9 +1543,12 @@ void PerlembParser::ExportEventVariables( } case EVENT_COMMAND: { + Seperator sep(data); + ExportVar(package_name.c_str(), "command", (sep.arg[0] + 1)); + ExportVar(package_name.c_str(), "args", (sep.argnum >= 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0")); + ExportVar(package_name.c_str(), "data", objid); ExportVar(package_name.c_str(), "text", data); - ExportVar(package_name.c_str(), "data", "0"); - ExportVar(package_name.c_str(), "langid", "0"); + ExportVar(package_name.c_str(), "langid", extradata); break; } @@ -1610,6 +1614,15 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "tradeskill_id", tradeskill_id.c_str()); break; } + case EVENT_BOT_COMMAND: { + Seperator sep(data); + ExportVar(package_name.c_str(), "bot_command", (sep.arg[0] + 1)); + ExportVar(package_name.c_str(), "args", (sep.argnum >= 1 ? (&data[strlen(sep.arg[0]) + 1]) : "0")); + ExportVar(package_name.c_str(), "data", objid); + ExportVar(package_name.c_str(), "text", data); + ExportVar(package_name.c_str(), "langid", extradata); + break; + } default: { break; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index c29c28e20..871cc0ced 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1812,6 +1812,43 @@ XS(XS__summonallplayercorpses) { XSRETURN(1); } +XS(XS__getplayercorpsecount); +XS(XS__getplayercorpsecount) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getplayercorpsecount(uint32 char_id)"); + + uint32 RETVAL; + dXSTARG; + + uint32 char_id = (int) SvIV(ST(0)); + + RETVAL = quest_manager.getplayercorpsecount(char_id); + XSprePUSH; + PUSHu((IV) RETVAL); + + XSRETURN(1); +} + +XS(XS__getplayercorpsecountbyzoneid); +XS(XS__getplayercorpsecountbyzoneid) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: quest::getplayercorpsecountbyzoneid(uint32 char_id, uint32 zone_id)"); + + uint32 RETVAL; + dXSTARG; + + uint32 char_id = (int) SvIV(ST(0)); + uint32 zone_id = (int)SvIV(ST(1)); + + RETVAL = quest_manager.getplayercorpsecountbyzoneid(char_id, zone_id); + XSprePUSH; + PUSHu((IV) RETVAL); + + XSRETURN(1); +} + XS(XS__getplayerburiedcorpsecount); XS(XS__getplayerburiedcorpsecount) { dXSARGS; @@ -3907,6 +3944,8 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); newXS(strcpy(buf, "getlevel"), XS__getlevel, file); newXS(strcpy(buf, "getplayerburiedcorpsecount"), XS__getplayerburiedcorpsecount, file); + newXS(strcpy(buf, "getplayercorpsecount"), XS__getplayercorpsecount, file); + newXS(strcpy(buf, "getplayercorpsecountbyzoneid"), XS__getplayercorpsecountbyzoneid, file); newXS(strcpy(buf, "gettaskactivitydonecount"), XS__gettaskactivitydonecount, file); newXS(strcpy(buf, "givecash"), XS__givecash, file); newXS(strcpy(buf, "gmmove"), XS__gmmove, file); diff --git a/zone/entity.cpp b/zone/entity.cpp index adb29011f..b13fb5649 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2624,6 +2624,24 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob) return false; } +/** + * @param mob + * @return + */ +void EntityList::RemoveAuraFromMobs(Mob *aura) +{ + LogEntityManagement( + "Attempting to remove aura [{}] from mobs entity_id ({})", + aura->GetCleanName(), + aura->GetID() + ); + + for (auto &it : mob_list) { + auto mob = it.second; + mob->RemoveAura(aura->GetID()); + } +} + /** * @param close_mobs * @param scanning_mob @@ -2654,7 +2672,12 @@ void EntityList::ScanCloseMobs(std::unordered_map &close_mobs, Mo } } - LogAIScanClose("Close List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName()); + LogAIScanClose( + "[{}] Scanning Close List | list_size [{}] moving [{}]", + scanning_mob->GetCleanName(), + close_mobs.size(), + scanning_mob->IsMoving() ? "true" : "false" + ); } bool EntityList::RemoveMerc(uint16 delete_id) @@ -4955,10 +4978,10 @@ void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radiu continue; } // check PC/NPC only flag 1 = PCs, 2 = NPCs - if (pcnpc == 1 && !ptr->IsClient() && !ptr->IsMerc()) { + if (pcnpc == 1 && !ptr->IsClient() && !ptr->IsMerc() && !ptr->IsBot()) { ++it; continue; - } else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc())) { + } else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc() || ptr->IsBot())) { ++it; continue; } diff --git a/zone/entity.h b/zone/entity.h index a296ca794..505f34963 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -293,6 +293,7 @@ public: bool RemoveObject(uint16 delete_id); bool RemoveProximity(uint16 delete_npc_id); bool RemoveMobFromCloseLists(Mob *mob); + void RemoveAuraFromMobs(Mob *aura); void RemoveAllMobs(); void RemoveAllClients(); void RemoveAllNPCs(); @@ -584,7 +585,6 @@ private: private: std::list bot_list; #endif - }; class BulkZoneSpawnPacket { diff --git a/zone/event_codes.h b/zone/event_codes.h index 4560cc767..110101cf9 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -87,6 +87,7 @@ typedef enum { EVENT_DEATH_ZONE, EVENT_USE_SKILL, EVENT_COMBINE_VALIDATE, + EVENT_BOT_COMMAND, _LargestEventID } QuestEventID; diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 64ce0cafa..9d0873360 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1385,59 +1385,87 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { return; } - uint32 copper = 0; - uint32 silver = 0; - uint32 gold = 0; - uint32 platinum = 0; - uint32 itemid = 0; - uint32 exp = 0; + QuestReward_Struct quest_reward; + quest_reward.mob_id = 0; + quest_reward.target_id = self->GetID(); + quest_reward.copper = 0; + quest_reward.silver = 0; + quest_reward.gold = 0; + quest_reward.platinum = 0; + quest_reward.exp_reward = 0; + quest_reward.faction = 0; + quest_reward.faction_mod = 0; bool faction = false; + std::fill(std::begin(quest_reward.item_id), std::end(quest_reward.item_id), -1); auto cur = reward["copper"]; if (luabind::type(cur) != LUA_TNIL) { try { - copper = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + quest_reward.copper = luabind::object_cast(cur); + } catch (luabind::cast_failed &) { } } cur = reward["silver"]; if (luabind::type(cur) != LUA_TNIL) { try { - silver = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + quest_reward.silver = luabind::object_cast(cur); + } catch (luabind::cast_failed &) { } } cur = reward["gold"]; if (luabind::type(cur) != LUA_TNIL) { try { - gold = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + quest_reward.gold = luabind::object_cast(cur); + } catch (luabind::cast_failed &) { } } cur = reward["platinum"]; if (luabind::type(cur) != LUA_TNIL) { try { - platinum = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + quest_reward.platinum = luabind::object_cast(cur); + } catch (luabind::cast_failed &) { } } cur = reward["itemid"]; if (luabind::type(cur) != LUA_TNIL) { try { - itemid = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + quest_reward.item_id[0] = luabind::object_cast(cur); + } catch (luabind::cast_failed &) { + } + } + + // if you define both an itemid and items table, the itemid is thrown away + // should we error? + cur = reward["items"]; + if (luabind::type(cur) == LUA_TTABLE) { + try { + // assume they defined a compatible table + for (int i = 1; i <= QUESTREWARD_COUNT; ++i) { + auto item = cur[i]; + int cur_value = -1; + if (luabind::type(item) != LUA_TNIL) { + try { + cur_value = luabind::object_cast(item); + } catch (luabind::cast_failed &) { + } + } else { + break; + } + quest_reward.item_id[i - 1] = cur_value; + } + } catch (luabind::cast_failed &) { } } cur = reward["exp"]; if (luabind::type(cur) != LUA_TNIL) { try { - exp = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + quest_reward.exp_reward = luabind::object_cast(cur); + } catch (luabind::cast_failed &) { } } @@ -1445,11 +1473,11 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) { if (luabind::type(cur) != LUA_TNIL) { try { faction = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } - self->QuestReward(target, copper, silver, gold, platinum, itemid, exp, faction); + self->QuestReward(target, quest_reward, faction); } bool Lua_Client::IsDead() { diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 7f714fd6b..0d1086ad1 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -549,6 +549,14 @@ void lua_summon_all_player_corpses(uint32 char_id, float x, float y, float z, fl quest_manager.summonallplayercorpses(char_id, glm::vec4(x, y, z, h)); } +int lua_get_player_corpse_count(uint32 char_id) { + return database.CountCharacterCorpses(char_id); +} + +int lua_get_player_corpse_count_by_zone_id(uint32 char_id, uint32 zone_id) { + return database.CountCharacterCorpsesByZoneID(char_id, zone_id); +} + int lua_get_player_buried_corpse_count(uint32 char_id) { return quest_manager.getplayerburiedcorpsecount(char_id); } @@ -571,7 +579,7 @@ void lua_task_selector(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { cur_value = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } else { count = i - 1; @@ -601,7 +609,7 @@ void lua_enable_task(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { cur_value = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } else { count = i - 1; @@ -628,7 +636,7 @@ void lua_disable_task(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { cur_value = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } else { count = i - 1; @@ -1156,7 +1164,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { spawn2_id = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1167,7 +1175,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { spawngroup_id = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1178,7 +1186,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { x = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1189,7 +1197,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { y = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1200,7 +1208,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { z = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1211,7 +1219,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { heading = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1222,7 +1230,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { respawn = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1233,7 +1241,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { variance = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { return; } } else { @@ -1244,7 +1252,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { timeleft = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1252,7 +1260,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { grid = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1260,7 +1268,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { condition_id = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1268,7 +1276,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { condition_min_value = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1276,7 +1284,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { enabled = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1284,7 +1292,7 @@ void lua_add_spawn_point(luabind::adl::object table) { if(luabind::type(cur) != LUA_TNIL) { try { animation = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1399,7 +1407,7 @@ void lua_update_zone_header(std::string type, std::string value) { try { \ npc_type->name = luabind::object_cast(cur); \ } \ - catch(luabind::cast_failed) { \ + catch(luabind::cast_failed &) { \ npc_type->size = default_value; \ } \ } \ @@ -1415,7 +1423,7 @@ void lua_update_zone_header(std::string type, std::string value) { std::string tmp = luabind::object_cast(cur); \ strncpy(npc_type->name, tmp.c_str(), str_length); \ } \ - catch(luabind::cast_failed) { \ + catch(luabind::cast_failed &) { \ strncpy(npc_type->name, default_value, str_length); \ } \ } \ @@ -1663,6 +1671,8 @@ luabind::scope lua_register_general() { luabind::def("toggle_spawn_event", &lua_toggle_spawn_event), luabind::def("summon_buried_player_corpse", &lua_summon_buried_player_corpse), luabind::def("summon_all_player_corpses", &lua_summon_all_player_corpses), + luabind::def("get_player_corpse_count", &lua_get_player_corpse_count), + luabind::def("get_player_corpse_count_by_zone_id", &lua_get_player_corpse_count_by_zone_id), luabind::def("get_player_buried_corpse_count", &lua_get_player_buried_corpse_count), luabind::def("bury_player_corpse", &lua_bury_player_corpse), luabind::def("task_selector", &lua_task_selector), diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 7d769b8dd..4860ae1e8 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -113,7 +113,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.armor_pen_flat = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -121,7 +121,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.crit_flat = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -129,7 +129,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.damage_flat = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -137,7 +137,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.hate_flat = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -145,7 +145,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.armor_pen_percent = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -153,7 +153,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.crit_percent = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -161,7 +161,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.damage_percent = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -169,7 +169,7 @@ bool Lua_Mob::Attack(Lua_Mob other, int hand, bool from_riposte, bool is_striket if(luabind::type(cur) != LUA_TNIL) { try { options.hate_percent = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } } @@ -785,7 +785,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj if (luabind::type(cur) != LUA_TNIL) { try { journal_opts.speak_mode = static_cast(luabind::object_cast(cur)); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -793,7 +793,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj if (luabind::type(cur) != LUA_TNIL) { try { journal_opts.journal_mode = static_cast(luabind::object_cast(cur)); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -801,7 +801,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj if (luabind::type(cur) != LUA_TNIL) { try { journal_opts.language = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } @@ -809,7 +809,7 @@ void Lua_Mob::QuestSay(Lua_Client client, const char *message, luabind::adl::obj if (luabind::type(cur) != LUA_TNIL) { try { journal_opts.message_type = luabind::object_cast(cur); - } catch (luabind::cast_failed) { + } catch (luabind::cast_failed &) { } } } @@ -1568,7 +1568,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { race = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1576,7 +1576,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { gender = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1584,7 +1584,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { texture = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1592,7 +1592,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { helmtexture = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1600,7 +1600,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { haircolor = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1608,7 +1608,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { beardcolor = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1616,7 +1616,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { eyecolor1 = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1624,7 +1624,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { eyecolor2 = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1632,7 +1632,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { hairstyle = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1640,7 +1640,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { luclinface = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1648,7 +1648,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { beard = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1656,7 +1656,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { aa_title = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1664,7 +1664,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { drakkin_heritage = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1672,7 +1672,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { drakkin_tattoo = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1680,7 +1680,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { drakkin_details = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } @@ -1688,7 +1688,7 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) { if(luabind::type(cur) != LUA_TNIL) { try { size = luabind::object_cast(cur); - } catch(luabind::cast_failed) { + } catch(luabind::cast_failed &) { } } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 437bc5121..620ee14d1 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -124,7 +124,8 @@ const char *LuaEvents[_LargestEventID] = { "event_spawn_zone", "event_death_zone", "event_use_skill", - "event_combine_validate" + "event_combine_validate", + "event_bot_command" }; extern Zone *zone; @@ -208,6 +209,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[EVENT_UNHANDLED_OPCODE] = handle_player_packet; PlayerArgumentDispatch[EVENT_USE_SKILL] = handle_player_use_skill; PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate; + PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command; ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click; diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index f4f1dd3af..3ca9edad7 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -539,6 +539,25 @@ void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* lua_setfield(L, -2, "tradeskill_id"); } +void handle_player_bot_command(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector* extra_pointers) { + Seperator sep(data.c_str(), ' ', 10, 100, true); + std::string bot_command(sep.arg[0] + 1); + lua_pushstring(L, bot_command.c_str()); + lua_setfield(L, -2, "bot_command"); + + luabind::adl::object args = luabind::newtable(L); + int max_args = sep.GetMaxArgNum(); + for (int i = 1; i < max_args; ++i) { + if (strlen(sep.arg[i]) > 0) { + args[i] = std::string(sep.arg[i]); + } + } + + args.push(L); + lua_setfield(L, -2, "args"); +} + //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, std::vector *extra_pointers) { diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 0054b31e1..9eeeb11cb 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -99,6 +99,8 @@ void handle_player_use_skill(QuestInterface *parse, lua_State* L, Client* client std::vector *extra_pointers); void handle_player_combine_validate(QuestInterface* parse, lua_State* L, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers); +void handle_player_bot_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, + std::vector *extra_pointers); //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, EQEmu::ItemInstance* item, Mob *mob, std::string data, uint32 extra_data, diff --git a/zone/mob.cpp b/zone/mob.cpp index 8b8758771..a6de82eee 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -186,6 +186,7 @@ Mob::Mob( last_hp_percent = 0; last_hp = 0; + last_max_hp = 0; current_speed = base_runspeed; @@ -459,6 +460,8 @@ Mob::Mob( #ifdef BOTS m_manual_follow = false; #endif + + mob_scan_close.Trigger(); } Mob::~Mob() @@ -501,6 +504,8 @@ Mob::~Mob() UninitializeBuffSlots(); entity_list.RemoveMobFromCloseLists(this); + entity_list.RemoveAuraFromMobs(this); + close_mobs.clear(); #ifdef BOTS @@ -1330,6 +1335,16 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal * If our HP is different from last HP update call - let's update selves */ if (IsClient()) { + + // delay to allow the client to catch up on buff states + if (max_hp != last_max_hp) { + + last_max_hp = max_hp; + CastToClient()->hp_self_update_throttle_timer.Trigger(); + + return; + } + if (current_hp != last_hp || force_update_all) { /** @@ -1337,10 +1352,12 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal */ if (this->CastToClient()->hp_self_update_throttle_timer.Check() || force_update_all) { Log(Logs::General, Logs::HPUpdate, - "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i last: %i skip_self: %s", + "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i/%i last: %i/%i skip_self: %s", this->GetCleanName(), current_hp, + max_hp, last_hp, + last_max_hp, (skip_self ? "true" : "false") ); @@ -1367,7 +1384,7 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal } } - int8 current_hp_percent = static_cast(max_hp == 0 ? 0 : static_cast(current_hp * 100 / max_hp)); + auto current_hp_percent = GetIntHPRatio(); Log(Logs::General, Logs::HPUpdate, diff --git a/zone/mob.h b/zone/mob.h index f0fc79548..7c095a001 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -624,8 +624,7 @@ public: //AI static uint32 GetLevelCon(uint8 mylevel, uint8 iOtherLevel); - inline uint32 GetLevelCon(uint8 iOtherLevel) const { - return this ? GetLevelCon(GetLevel(), iOtherLevel) : CON_GRAY; } + inline uint32 GetLevelCon(uint8 iOtherLevel) const { return GetLevelCon(GetLevel(), iOtherLevel); } virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false, uint16 spell_id = SPELL_UNKNOWN, bool pet_comand = false); bool RemoveFromHateList(Mob* mob); @@ -1527,6 +1526,7 @@ protected: int8 last_hp_percent; int32 last_hp; + int32 last_max_hp; int cur_wp; glm::vec4 m_CurrentWayPoint; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 525948bb2..43d85c450 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1693,12 +1693,32 @@ void NPC::AI_DoMovement() { GetZ(), GetGrid()); + if (wandertype == GridRandomPath) + { + if (cur_wp == patrol) + { + // reached our randomly selected destination; force a pause + if (cur_wp_pause == 0) + { + if (Waypoints.size() > 0 && Waypoints[0].pause) + cur_wp_pause = Waypoints[0].pause; + else + cur_wp_pause = 38; + } + Log(Logs::Detail, Logs::AI, "NPC using wander type GridRandomPath on grid %d at waypoint %d has reached its random destination; pause time is %d", GetGrid(), cur_wp, cur_wp_pause); + } + else + cur_wp_pause = 0; // skipping pauses until destination + } + SetWaypointPause(); - SetAppearance(eaStanding, false); - if (cur_wp_pause > 0) { + if (GetAppearance() != eaStanding) { + SetAppearance(eaStanding, false); + } + if (cur_wp_pause > 0 && m_CurrentWayPoint.w >= 0.0) { RotateTo(m_CurrentWayPoint.w); } - + //kick off event_waypoint arrive char temp[16]; sprintf(temp, "%d", cur_wp); @@ -1789,12 +1809,12 @@ void NPC::AI_SetupNextWaypoint() { } } - if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { + if (wandertype == GridOneWayRepop && cur_wp == CastToNPC()->GetMaxWp()) { CastToNPC()->Depop(true); //depop and restart spawn timer if (found_spawn) found_spawn->SetNPCPointerNull(); } - else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { + else if (wandertype == GridOneWayDepop && cur_wp == CastToNPC()->GetMaxWp()) { CastToNPC()->Depop(false);//depop without spawn timer if (found_spawn) found_spawn->SetNPCPointerNull(); diff --git a/zone/npc.cpp b/zone/npc.cpp index 8b4996177..ed5f37bb5 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -705,12 +705,6 @@ bool NPC::Process() SpellProcess(); if (mob_scan_close.Check()) { - LogAIScanClose( - "is_moving [{}] npc [{}] timer [{}]", - moving ? "true" : "false", - GetCleanName(), - mob_scan_close.GetDuration() - ); entity_list.ScanCloseMobs(close_mobs, this); diff --git a/zone/npc.h b/zone/npc.h index 84ab8b4ef..d0ecdf1ca 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -303,7 +303,7 @@ public: int GetMaxWp() const { return max_wp; } void DisplayWaypointInfo(Client *to); void CalculateNewWaypoint(); - void AssignWaypoints(int32 grid); + void AssignWaypoints(int32 grid, int start_wp = 0); void SetWaypointPause(); void UpdateWaypoint(int wp_index); @@ -312,7 +312,8 @@ public: void ResumeWandering(); void PauseWandering(int pausetime); void MoveTo(const glm::vec4& position, bool saveguardspot); - void GetClosestWaypoint(std::list &wp_list, int count, const glm::vec3& location); + void GetClosestWaypoints(std::list &wp_list, int count, const glm::vec3& location); + int GetClosestWaypoint(const glm::vec3& location); uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const; // returns item id int32 GetEquipmentMaterial(uint8 material_slot) const; diff --git a/zone/pets.cpp b/zone/pets.cpp index 438509b77..b4897940a 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -199,7 +199,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, } #ifdef BOTS else if (this->IsBot()) - act_power = CastToBot()->GetBotFocusEffect(Bot::BotfocusPetPower, spell_id); + act_power = CastToBot()->GetBotFocusEffect(focusPetPower, spell_id); #endif } else if (petpower > 0) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 69e55a57f..bea136e28 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1910,6 +1910,21 @@ bool QuestManager::summonallplayercorpses(uint32 char_id, const glm::vec4& posit return true; } +int QuestManager::getplayercorpsecount(uint32 char_id) { + if (char_id > 0) { + return database.CountCharacterCorpses(char_id); + } + return 0; + +} + +int QuestManager::getplayercorpsecountbyzoneid(uint32 char_id, uint32 zone_id) { + if (char_id > 0 && zone_id > 0) { + return database.CountCharacterCorpsesByZoneID(char_id, zone_id); + } + return 0; +} + uint32 QuestManager::getplayerburiedcorpsecount(uint32 char_id) { uint32 Result = 0; diff --git a/zone/questmgr.h b/zone/questmgr.h index 1b9ea9c4d..5ad551c43 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -172,6 +172,8 @@ public: bool summonburiedplayercorpse(uint32 char_id, const glm::vec4& position); bool summonallplayercorpses(uint32 char_id, const glm::vec4& position); uint32 getplayerburiedcorpsecount(uint32 char_id); + int getplayercorpsecount(uint32 char_id); + int getplayercorpsecountbyzoneid(uint32 char_id, uint32 zone_id); bool buryplayercorpse(uint32 char_id); void forcedooropen(uint32 doorid, bool altmode); void forcedoorclose(uint32 doorid, bool altmode); diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index e6d265813..958500550 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -233,6 +233,20 @@ bool Spawn2::Process() { } currentnpcid = npcid; + + glm::vec4 loc(x, y, z, heading); + int starting_wp = 0; + if (spawn_group->wp_spawns && grid_ > 0) + { + glm::vec4 wploc; + starting_wp = database.GetRandomWaypointLocFromGrid(wploc, zone->GetZoneID(), grid_); + if (wploc.x != 0.0f || wploc.y != 0.0f || wploc.z != 0.0f) + { + loc = wploc; + Log(Logs::General, Logs::Spawns, "spawning at random waypoint #%i loc: (%.3f, %.3f, %.3f).", starting_wp , loc.x, loc.y, loc.z); + } + } + NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), GravityBehavior::Water); npc->mod_prespawn(this); @@ -275,7 +289,7 @@ bool Spawn2::Process() { z ); - LoadGrid(); + LoadGrid(starting_wp); } else { LogSpawns("Spawn2 [{}]: Group [{}] spawned [{}] ([{}]) at ([{}], [{}], [{}]). Grid loading delayed", @@ -302,7 +316,7 @@ void Spawn2::Disable() enabled = false; } -void Spawn2::LoadGrid() { +void Spawn2::LoadGrid(int start_wp) { if (!npcthis) return; if (grid_ < 1) @@ -311,8 +325,8 @@ void Spawn2::LoadGrid() { return; //dont set an NPC's grid until its loaded for them. npcthis->SetGrid(grid_); - npcthis->AssignWaypoints(grid_); - LogSpawns("Spawn2 [{}]: Loading grid [{}] for [{}]", spawn2_id, grid_, npcthis->GetName()); + npcthis->AssignWaypoints(grid_, start_wp); + LogSpawns("Spawn2 [{}]: Loading grid [{}] for [{}]; starting wp is [{}]", spawn2_id, grid_, npcthis->GetName(), start_wp); } /* diff --git a/zone/spawn2.h b/zone/spawn2.h index a626b7084..bf6530876 100644 --- a/zone/spawn2.h +++ b/zone/spawn2.h @@ -36,7 +36,7 @@ public: uint16 cond_id = SC_AlwaysEnabled, int16 min_value = 0, bool in_enabled = true, EmuAppearance anim = eaStanding); ~Spawn2(); - void LoadGrid(); + void LoadGrid(int start_wp = 0); void Enable() { enabled = true; } void Disable(); bool Enabled() { return enabled; } diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp index cc3735386..6a7817bce 100644 --- a/zone/spawngroup.cpp +++ b/zone/spawngroup.cpp @@ -48,7 +48,8 @@ SpawnGroup::SpawnGroup( int delay_in, int despawn_in, uint32 despawn_timer_in, - int min_delay_in + int min_delay_in, + bool wp_spawns_in ) { id = in_id; @@ -63,6 +64,7 @@ SpawnGroup::SpawnGroup( delay = delay_in; despawn = despawn_in; despawn_timer = despawn_timer_in; + wp_spawns = wp_spawns_in; } uint32 SpawnGroup::GetNPCType(uint16 in_filter) @@ -198,7 +200,8 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, - spawngroup.mindelay + spawngroup.mindelay, + spawngroup.wp_spawns FROM spawn2, spawngroup @@ -229,7 +232,8 @@ bool ZoneDatabase::LoadSpawnGroups(const char *zone_name, uint16 version, SpawnG atoi(row[8]), atoi(row[9]), atoi(row[10]), - atoi(row[11]) + atoi(row[11]), + atoi(row[12]) ); spawn_group_list->AddSpawnGroup(new_spawn_group); @@ -305,7 +309,8 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, - spawngroup.mindelay + spawngroup.mindelay, + spawngroup.wp_spawns FROM spawngroup WHERE @@ -332,7 +337,8 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawn_group_id, SpawnGroupList *spawn atoi(row[8]), atoi(row[9]), atoi(row[10]), - atoi(row[11]) + atoi(row[11]), + atoi(row[12]) ); spawn_group_list->AddSpawnGroup(new_spawn_group); diff --git a/zone/spawngroup.h b/zone/spawngroup.h index 2a4c1af6c..a1068cea0 100644 --- a/zone/spawngroup.h +++ b/zone/spawngroup.h @@ -49,13 +49,15 @@ public: int delay_in, int despawn_in, uint32 despawn_timer_in, - int min_delay_in + int min_delay_in, + bool wp_spawns_in ); ~SpawnGroup(); uint32 GetNPCType(uint16 condition_value_filter=1); void AddSpawnEntry(SpawnEntry *newEntry); uint32 id; + bool wp_spawns; // if true, spawn NPCs at a random waypoint location (if spawnpoint has a grid) instead of the spawnpoint's loc float roamdist; float roambox[4]; int min_delay; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 0f29c38ce..63a4a113f 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -286,6 +286,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove dmg = -dmg; } + // hack fix for client health not reflecting server value + last_hp = 0; + //do any AAs apply to these spells? if(dmg < 0) { if (!PassCastRestriction(false, spells[spell_id].base2[i], true)) @@ -1820,6 +1823,58 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove break; } + case SE_SummonCorpseZone: + { + if (IsClient()) { + Client* client_target = this->CastToClient(); + if (client_target->IsGrouped()) { + Group* group = client_target->GetGroup(); + if (!group->IsGroupMember(caster)) { + if (caster != this) { + caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); + break; + } + } + } else if (caster) { + if (caster->IsRaidGrouped()) { + Raid *raid = caster->GetRaid(); + uint32 group_id = raid->GetGroup(caster->GetName()); + if (group_id > 0 && group_id < MAX_RAID_GROUPS) { + if (raid->GetGroup(client_target->GetName()) != group_id) { + caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); + break; + } + } + } else { + if (caster != this) { + caster->MessageString(Chat::Red, SUMMON_ONLY_GROUP_CORPSE); + break; + } + } + } + + if (client_target) { + if (database.CountCharacterCorpses(client_target->CharacterID()) == 0) { + if (caster == this) { + Message(Chat::Yellow, "You have no corpses to summon."); + } else { + caster->Message(Chat::Yellow, "%s has no corpses to summon.", client_target->GetCleanName()); + } + } else { + if (caster == this) { + Message(Chat::Spells, "Summoning your corpses."); + } else { + caster->MessageString(Chat::Spells, SUMMONING_CORPSE_ZONE, client_target->GetCleanName()); + } + client_target->SummonAllCorpses(client_target->GetPosition()); + } + } else { + MessageString(Chat::Spells, TARGET_NOT_FOUND); + LogError("[{}] attempted to cast spell id [{}] with spell effect SE_SummonCorpseZone, but could not cast target into a Client object", GetCleanName(), spell_id); + } + } + break; + } case SE_AddMeleeProc: case SE_WeaponProc: { diff --git a/zone/spells.cpp b/zone/spells.cpp index afeac90e9..a1e926a1c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3455,9 +3455,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // 1 = PCs, 2 = NPCs if (spells[spell_id].pcnpc_only_flag && spells[spell_id].targettype != ST_AETargetHateList && spells[spell_id].targettype != ST_HateList) { - if (spells[spell_id].pcnpc_only_flag == 1 && !spelltar->IsClient() && !spelltar->IsMerc()) + if (spells[spell_id].pcnpc_only_flag == 1 && !spelltar->IsClient() && !spelltar->IsMerc() && !spelltar->IsBot()) return false; - else if (spells[spell_id].pcnpc_only_flag == 2 && (spelltar->IsClient() || spelltar->IsMerc())) + else if (spells[spell_id].pcnpc_only_flag == 2 && (spelltar->IsClient() || spelltar->IsMerc() || spelltar->IsBot())) return false; } diff --git a/zone/string_ids.h b/zone/string_ids.h index acab3df22..25321b3c9 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -65,6 +65,7 @@ #define SILENCED_STRING 207 //You *CANNOT* cast spells, you have been silenced! #define CANNOT_AFFECT_PC 210 //That spell can not affect this target PC. #define SPELL_NEED_TAR 214 //You must first select a target for this spell! +#define SUMMON_ONLY_GROUP_CORPSE 215 //You must first target a living group member whose corpse you wish to summon. #define ONLY_ON_CORPSES 221 //This spell only works on corpses. #define CANT_DRAIN_SELF 224 //You can't drain yourself! #define CORPSE_NOT_VALID 230 //This corpse is not valid. @@ -169,6 +170,7 @@ #define PVP_ON 552 //You are now player kill and follow the ways of Discord. #define GENERIC_STRINGID_SAY 554 //%1 says '%T2' #define CANNOT_WAKE 555 //%1 tells you, 'I am unable to wake %2, master.' +#define SUMMONING_CORPSE_ZONE 596 //Summoning %1's corpse(s). #define PET_HOLD_SET_ON 698 //The pet hold mode has been set to on. #define PET_HOLD_SET_OFF 699 //The pet hold mode has been set to off. #define PET_FOCUS_SET_ON 700 //The pet focus mode has been set to on. @@ -415,6 +417,7 @@ #define TARGET_PLAYER_FOR_GUILD_STATUS 12260 #define GROUP_INVITEE_NOT_FOUND 12268 //You must target a player or use /invite to invite someone to your group. #define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself. +#define ALREADY_IN_PARTY 12272 //That person is already in your party. #define NO_LONGER_HIDDEN 12337 //You are no longer hidden. #define STOP_SNEAKING 12338 //You stop sneaking #define NOT_IN_CONTROL 12368 //You do not have control of yourself right now. diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 10e4a11f3..65f4fd099 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -159,7 +159,7 @@ void NPC::PauseWandering(int pausetime) if (GetGrid() != 0) { moving = false; DistractedFromGrid = true; - LogPathing("Paused Wandering requested. Grid [{}]. Resuming in [{}] ms (0=not until told)", GetGrid(), pausetime); + LogPathing("Paused Wandering requested. Grid [{}]. Resuming in [{}] seconds (0=not until told)", GetGrid(), pausetime); StopNavigation(); if (pausetime < 1) { // negative grid number stops him dead in his tracks until ResumeWandering() SetGrid(0 - GetGrid()); @@ -244,14 +244,14 @@ void NPC::CalculateNewWaypoint() int old_wp = cur_wp; bool reached_end = false; bool reached_beginning = false; - if (cur_wp == max_wp) + if (cur_wp == max_wp - 1) //cur_wp starts at 0, max_wp starts at 1. reached_end = true; if (cur_wp == 0) reached_beginning = true; switch (wandertype) { - case 0: //circle + case GridCircular: { if (reached_end) cur_wp = 0; @@ -259,10 +259,10 @@ void NPC::CalculateNewWaypoint() cur_wp = cur_wp + 1; break; } - case 1: //10 closest + case GridRandom10: { std::list closest; - GetClosestWaypoint(closest, 10, glm::vec3(GetPosition())); + GetClosestWaypoints(closest, 10, glm::vec3(GetPosition())); auto iter = closest.begin(); if (closest.size() != 0) { @@ -273,30 +273,64 @@ void NPC::CalculateNewWaypoint() break; } - case 2: //random + case GridRandom: + case GridCenterPoint: { - cur_wp = zone->random.Int(0, Waypoints.size() - 1); - if (cur_wp == old_wp) + if (wandertype == GridCenterPoint && !reached_beginning) { - if (cur_wp == (Waypoints.size() - 1)) + cur_wp = 0; + } + else + { + cur_wp = zone->random.Int(0, Waypoints.size() - 1); + if (cur_wp == old_wp || (wandertype == GridCenterPoint && cur_wp == 0)) { - if (cur_wp > 0) + if (cur_wp == (Waypoints.size() - 1)) { - cur_wp--; + if (cur_wp > 0) + { + cur_wp--; + } } - } - else if (cur_wp == 0) - { - if ((Waypoints.size() - 1) > 0) + else if (cur_wp == 0) { - cur_wp++; + if ((Waypoints.size() - 1) > 0) + { + cur_wp++; + } } } } break; } - case 3: //patrol + case GridRandomCenterPoint: + { + bool on_center = Waypoints[cur_wp].centerpoint; + std::vector random_waypoints; + for (auto &w : Waypoints) + { + wplist wpl = w; + if (wpl.index != cur_wp && + ((on_center && !wpl.centerpoint) || (!on_center && wpl.centerpoint))) + { + random_waypoints.push_back(w); + } + } + + if (random_waypoints.size() == 0) + { + cur_wp = 0; + } + else + { + int windex = zone->random.Roll0(random_waypoints.size()); + cur_wp = random_waypoints[windex].index; + } + + break; + } + case GridPatrol: { if (reached_end) patrol = 1; @@ -309,16 +343,16 @@ void NPC::CalculateNewWaypoint() break; } - case 4: //goto the end and depop with spawn timer - case 6: //goto the end and depop without spawn timer + case GridOneWayRepop: + case GridOneWayDepop: { cur_wp = cur_wp + 1; break; } - case 5: //pick random closest 5 and pick one that's in sight + case GridRand5LoS: { std::list closest; - GetClosestWaypoint(closest, 5, glm::vec3(GetPosition())); + GetClosestWaypoints(closest, 5, glm::vec3(GetPosition())); auto iter = closest.begin(); while (iter != closest.end()) @@ -341,6 +375,25 @@ void NPC::CalculateNewWaypoint() } break; } + case GridRandomPath: // randomly select a waypoint but follow path to it instead of walk directly to it ignoring walls + { + if (Waypoints.size() == 0) + { + cur_wp = 0; + } + else + { + if (cur_wp == patrol) // reutilizing patrol member instead of making new member for this wander type; here we use it to save a random waypoint + { + while (patrol == cur_wp) + patrol = zone->random.Int(0, Waypoints.size() - 1); + } + if (patrol > cur_wp) + cur_wp = cur_wp + 1; + else + cur_wp = cur_wp - 1; + } + } } // Preserve waypoint setting for quest controlled NPCs @@ -357,7 +410,30 @@ bool wp_distance_pred(const wp_distance& left, const wp_distance& right) return left.dist < right.dist; } -void NPC::GetClosestWaypoint(std::list &wp_list, int count, const glm::vec3& location) +int NPC::GetClosestWaypoint(const glm::vec3& location) +{ + if (Waypoints.size() <= 1) + return 0; + + int closest = 0; + float closestDist = 9999999.0f; + float dist; + + for (int i = 0; i < Waypoints.size(); ++i) + { + dist = DistanceSquared(location, glm::vec3(Waypoints[i].x, Waypoints[i].y, Waypoints[i].z)); + + if (dist < closestDist) + { + closestDist = dist; + closest = i; + } + } + return closest; +} + +// fills wp_list with the closest count number of waypoints +void NPC::GetClosestWaypoints(std::list &wp_list, int count, const glm::vec3& location) { wp_list.clear(); if (Waypoints.size() <= count) @@ -485,7 +561,7 @@ void Mob::StopNavigation() { mMovementManager->StopNavigation(this); } -void NPC::AssignWaypoints(int32 grid) +void NPC::AssignWaypoints(int32 grid, int start_wp) { if (grid == 0) return; // grid ID 0 not supported @@ -518,7 +594,7 @@ void NPC::AssignWaypoints(int32 grid) SetGrid(grid); // Assign grid number // Retrieve all waypoints for this grid - query = StringFormat("SELECT `x`,`y`,`z`,`pause`,`heading` " + query = StringFormat("SELECT `x`,`y`,`z`,`pause`,`heading`, `centerpoint` " "FROM grid_entries WHERE `gridid` = %i AND `zoneid` = %i " "ORDER BY `number`", grid, zone->GetZoneID()); results = database.QueryDatabase(query); @@ -539,14 +615,22 @@ void NPC::AssignWaypoints(int32 grid) newwp.pause = atoi(row[3]); newwp.heading = atof(row[4]); + newwp.centerpoint = atobool(row[5]); Waypoints.push_back(newwp); } - UpdateWaypoint(0); + cur_wp = start_wp; + UpdateWaypoint(start_wp); SetWaypointPause(); - if (wandertype == 1 || wandertype == 2 || wandertype == 5) + if (wandertype == GridRandomPath) { + cur_wp = GetClosestWaypoint(glm::vec3(GetPosition())); + patrol = cur_wp; + } + + if (wandertype == GridRandom10 || wandertype == GridRandom || wandertype == GridRand5LoS) CalculateNewWaypoint(); + } void Mob::SendTo(float new_x, float new_y, float new_z) { @@ -687,7 +771,7 @@ void Mob::FixZ(int32 z_find_offset /*= 5*/, bool fix_client_z /*= false*/) { float Mob::GetZOffset() const { float offset = 3.125f; - switch (race) { + switch (GetModel()) { case RACE_BASILISK_436: offset = 0.577f; break; @@ -1058,6 +1142,37 @@ int ZoneDatabase::GetHighestWaypoint(uint32 zoneid, uint32 gridid) { return atoi(row[0]); } +int ZoneDatabase::GetRandomWaypointLocFromGrid(glm::vec4 &loc, uint16 zoneid, int grid) +{ + loc.x = loc.y = loc.z = loc.w = 0.0f; + + std::string query = StringFormat("SELECT `x`,`y`,`z`,`heading` " + "FROM grid_entries WHERE `gridid` = %i AND `zoneid` = %u ORDER BY `number`", grid, zone->GetZoneID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + Log(Logs::General, Logs::Error, "MySQL Error while trying get random waypoint loc from grid %i in zoneid %u; %s", grid, zoneid, results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() > 0) + { + int roll = zone->random.Int(0, results.RowCount() - 1); + int i = 0; + auto row = results.begin(); + while (i < roll) + { + row++; + i++; + } + loc.x = atof(row[0]); + loc.y = atof(row[1]); + loc.z = atof(row[2]); + loc.w = atof(row[3]); + return i; + } + return 0; +} + void NPC::SaveGuardSpotCharm() { m_GuardPointSaved = m_GuardPoint; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index dc2e29cad..d5346877d 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -4707,6 +4707,47 @@ bool ZoneDatabase::SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zone_id return (CorpseCount > 0); } +int ZoneDatabase::CountCharacterCorpses(uint32 char_id) { + std::string query = fmt::format( + SQL( + SELECT + COUNT(*) + FROM + character_corpses + WHERE + charid = '{}' + ), + char_id + ); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + return atoi(row[0]); + } + return 0; +} + +int ZoneDatabase::CountCharacterCorpsesByZoneID(uint32 char_id, uint32 zone_id) { + std::string query = fmt::format( + SQL( + SELECT + COUNT(*) + FROM + character_corpses + WHERE + charid = '{}' + AND + zone_id = '{}' + ), + char_id, + zone_id + ); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + return atoi(row[0]); + } + return 0; +} + bool ZoneDatabase::UnburyCharacterCorpse(uint32 db_id, uint32 new_zone_id, uint16 new_instance_id, const glm::vec4& position) { std::string query = StringFormat("UPDATE `character_corpses` " "SET `is_buried` = 0, `zone_id` = %u, `instance_id` = %u, " diff --git a/zone/zonedb.h b/zone/zonedb.h index e93a40713..d5c02c693 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -47,6 +47,7 @@ struct wplist { float z; int pause; float heading; + bool centerpoint; }; #pragma pack(1) @@ -356,6 +357,8 @@ public: bool DeleteCharacterCorpse(uint32 dbid); bool SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, const glm::vec4& position); bool SummonAllGraveyardCorpses(uint32 cur_zoneid, uint32 dest_zoneid, uint16 dest_instanceid, const glm::vec4& position); + int CountCharacterCorpses(uint32 char_id); + int CountCharacterCorpsesByZoneID(uint32 char_id, uint32 zone_id); bool UnburyCharacterCorpse(uint32 dbid, uint32 new_zoneid, uint16 dest_instanceid, const glm::vec4& position); bool LoadCharacterCorpses(uint32 iZoneID, uint16 iInstanceID); bool DeleteGraveyard(uint32 zone_id, uint32 graveyard_id); @@ -432,6 +435,7 @@ public: void AssignGrid(Client *client, int grid, int spawn2id); int GetHighestGrid(uint32 zoneid); int GetHighestWaypoint(uint32 zoneid, uint32 gridid); + int GetRandomWaypointLocFromGrid(glm::vec4 &loc, uint16 zoneid, int grid); /* NPCs */ diff --git a/zone/zonedump.h b/zone/zonedump.h index 302d4ee25..6010c9480 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -44,7 +44,7 @@ struct NPCType uint16 race; uint8 class_; uint8 bodytype; // added for targettype support - uint8 deity; //not loaded from DB + uint32 deity; //not loaded from DB uint8 level; uint32 npc_id; uint8 texture;