diff --git a/common/faction.h b/common/faction.h index 04b40eb9c..a4aac84a2 100755 --- a/common/faction.h +++ b/common/faction.h @@ -24,17 +24,15 @@ #include enum FACTION_VALUE { - FACTION_ALLY = 1, - FACTION_WARMLY = 2, - FACTION_KINDLY = 3, - FACTION_AMIABLY = 4, - - FACTION_INDIFFERENTLY = 5, - + FACTION_ALLY = 1, + FACTION_WARMLY = 2, + FACTION_KINDLY = 3, + FACTION_AMIABLY = 4, + FACTION_INDIFFERENTLY = 5, FACTION_APPREHENSIVELY = 6, - FACTION_DUBIOUSLY = 7, - FACTION_THREATENINGLY = 8, - FACTION_SCOWLS = 9 + FACTION_DUBIOUSLY = 7, + FACTION_THREATENINGLY = 8, + FACTION_SCOWLS = 9 }; struct NPCFactionList { @@ -75,23 +73,6 @@ struct NPCFaction uint8 temp; }; -// Faction Associations give a much more live like faction system -// Basically the primary faction and magnitude of a faction hit will generate the rest of them - -// Largest faction I could find quickly was Lord Inquisitor Seru with 9 total hits (8 associations) so 8 + 2 for max for now -#define MAX_FACTION_ASSOC 10 - -// this is the ID of a faction association and it's multiplier -struct FactionAssociationHit { - int id; - float multiplier; -}; - -struct FactionAssociations { - // maybe there should be more data here, fine for now - FactionAssociationHit hits[MAX_FACTION_ASSOC]; -}; - const char *FactionValueToString(FACTION_VALUE faction_value); FACTION_VALUE CalculateFaction(FactionMods* fm, int32 tmpCharacter_value); #endif diff --git a/common/servertalk.h b/common/servertalk.h index 1fafb7efa..6a2693217 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -251,6 +251,7 @@ #define ServerOP_ReloadDzTemplates 0x4123 #define ServerOP_ReloadZoneData 0x4124 #define ServerOP_ReloadDataBucketsCache 0x4125 +#define ServerOP_ReloadFactions 0x4126 #define ServerOP_CZDialogueWindow 0x4500 #define ServerOP_CZLDoNUpdate 0x4501 diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 84b68ebfa..d0c8cc084 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1371,186 +1371,6 @@ std::string SharedDatabase::GetBook(const char *txtfile, int16 *language) return txtout; } -void SharedDatabase::GetFactionListInfo(uint32 &list_count, uint32 &max_lists) { - list_count = 0; - max_lists = 0; - - const std::string query = "SELECT COUNT(*), MAX(id) FROM npc_faction"; - auto results = QueryDatabase(query); - if (!results.Success()) { - return; - } - - if (results.RowCount() == 0) - return; - - auto& row = results.begin(); - - list_count = Strings::ToUnsignedInt(row[0]); - max_lists = Strings::ToUnsignedInt(row[1] ? row[1] : "0"); -} - -const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) const -{ - if(!faction_hash) { - return nullptr; - } - - if(faction_hash->exists(id)) { - return &(faction_hash->at(id)); - } - - return nullptr; -} - -void SharedDatabase::LoadNPCFactionLists(void *data, uint32 size, uint32 list_count, uint32 max_lists) { - EQ::FixedMemoryHashSet hash(static_cast(data), size, list_count, max_lists); - NPCFactionList faction; - - const std::string query = "SELECT npc_faction.id, npc_faction.primaryfaction, npc_faction.ignore_primary_assist, " - "npc_faction_entries.faction_id, npc_faction_entries.value, npc_faction_entries.npc_value, " - "npc_faction_entries.temp FROM npc_faction LEFT JOIN npc_faction_entries " - "ON npc_faction.id = npc_faction_entries.npc_faction_id ORDER BY npc_faction.id;"; - auto results = QueryDatabase(query); - if (!results.Success()) { - return; - } - - uint32 current_id = 0; - uint32 current_entry = 0; - - for(auto& row = results.begin(); row != results.end(); ++row) { - const uint32 id = Strings::ToUnsignedInt(row[0]); - if(id != current_id) { - if(current_id != 0) { - hash.insert(current_id, faction); - } - - memset(&faction, 0, sizeof(faction)); - current_entry = 0; - current_id = id; - faction.id = id; - faction.primaryfaction = Strings::ToUnsignedInt(row[1]); - faction.assistprimaryfaction = (Strings::ToInt(row[2]) == 0); - } - - if(!row[3]) - continue; - - if(current_entry >= MAX_NPC_FACTIONS) - continue; - - faction.factionid[current_entry] = Strings::ToUnsignedInt(row[3]); - faction.factionvalue[current_entry] = Strings::ToInt(row[4]); - faction.factionnpcvalue[current_entry] = static_cast(Strings::ToInt(row[5])); - faction.factiontemp[current_entry] = static_cast(Strings::ToUnsignedInt(row[6])); - ++current_entry; - } - - if(current_id != 0) - hash.insert(current_id, faction); - -} - -bool SharedDatabase::LoadNPCFactionLists(const std::string &prefix) { - faction_mmf.reset(nullptr); - faction_hash.reset(nullptr); - - try { - const auto Config = EQEmuConfig::get(); - EQ::IPCMutex mutex("faction"); - mutex.Lock(); - std::string file_name = fmt::format("{}/{}{}", path.GetSharedMemoryPath(), prefix, std::string("faction")); - LogInfo("Loading [{}]", file_name); - faction_mmf = std::make_unique(file_name); - faction_hash = std::make_unique>(static_cast(faction_mmf->Get()), faction_mmf->Size()); - mutex.Unlock(); - - LogInfo("Loaded faction lists via shared memory"); - } catch(std::exception& ex) { - LogError("Error Loading npc factions: {}", ex.what()); - return false; - } - - return true; -} - -void SharedDatabase::GetFactionAssociationInfo(uint32 &list_count, uint32 &max_lists) -{ - list_count = static_cast(FactionAssociationRepository::Count(*this)); - max_lists = static_cast(FactionAssociationRepository::GetMaxId(*this)); -} - -const FactionAssociations *SharedDatabase::GetFactionAssociationHit(int id) -{ - if (!faction_associations_hash) { - return nullptr; - } - - if (faction_associations_hash->exists(id)) { - return &(faction_associations_hash->at(id)); - } - - return nullptr; -} - -void SharedDatabase::LoadFactionAssociation(void *data, uint32 size, uint32 list_count, uint32 max_lists) -{ - EQ::FixedMemoryHashSet hash(reinterpret_cast(data), size, list_count, max_lists); - FactionAssociations faction{}; - - auto results = FactionAssociationRepository::All(*this); - for (auto &row : results) { - faction.hits[0].id = row.id_1; - faction.hits[0].multiplier = row.mod_1; - faction.hits[1].id = row.id_2; - faction.hits[1].multiplier = row.mod_2; - faction.hits[2].id = row.id_3; - faction.hits[2].multiplier = row.mod_3; - faction.hits[3].id = row.id_4; - faction.hits[3].multiplier = row.mod_4; - faction.hits[4].id = row.id_5; - faction.hits[4].multiplier = row.mod_5; - faction.hits[5].id = row.id_6; - faction.hits[5].multiplier = row.mod_6; - faction.hits[6].id = row.id_7; - faction.hits[6].multiplier = row.mod_7; - faction.hits[7].id = row.id_8; - faction.hits[7].multiplier = row.mod_8; - faction.hits[8].id = row.id_9; - faction.hits[8].multiplier = row.mod_9; - faction.hits[9].id = row.id_10; - faction.hits[9].multiplier = row.mod_10; - - hash.insert(row.id, faction); - } -} - -bool SharedDatabase::LoadFactionAssociation(const std::string &prefix) -{ - faction_associations_mmf.reset(nullptr); - faction_associations_hash.reset(nullptr); - - try { - auto Config = EQEmuConfig::get(); - EQ::IPCMutex mutex("factionassociations"); - mutex.Lock(); - std::string file_name = fmt::format("{}/{}{}", path.GetSharedMemoryPath(), prefix, std::string("factionassociations")); - faction_associations_mmf = std::unique_ptr(new EQ::MemoryMappedFile(file_name)); - faction_associations_hash = std::unique_ptr>( - new EQ::FixedMemoryHashSet(reinterpret_cast(faction_associations_mmf->Get()), - faction_associations_mmf->Size())); - mutex.Unlock(); - - LogInfo("Loaded faction associations via shared memory"); - } catch (std::exception &ex) { - LogError("Error Loading faction associations: {}", ex.what()); - return false; - } - - return true; -} - // Create appropriate EQ::ItemInstance class EQ::ItemInstance* SharedDatabase::CreateItem( uint32 item_id, diff --git a/common/shareddb.h b/common/shareddb.h index eec5aab2b..455f8288a 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -164,22 +164,6 @@ public: uint32 GetSharedItemsCount() { return m_shared_items_count; } uint32 GetItemsCount(); - /** - * faction - */ - void GetFactionListInfo(uint32 &list_count, uint32 &max_lists); - const NPCFactionList *GetNPCFactionEntry(uint32 id) const; - void LoadNPCFactionLists(void *data, uint32 size, uint32 list_count, uint32 max_lists); - bool LoadNPCFactionLists(const std::string &prefix); - - /** - * faction associations - */ - void GetFactionAssociationInfo(uint32 &list_count, uint32 &max_lists); - const FactionAssociations *GetFactionAssociationHit(int id); - void LoadFactionAssociation(void *data, uint32 size, uint32 list_count, uint32 max_lists); - bool LoadFactionAssociation(const std::string &prefix); - /** * loot */ diff --git a/shared_memory/CMakeLists.txt b/shared_memory/CMakeLists.txt index 86fa47d6b..2332e40c6 100644 --- a/shared_memory/CMakeLists.txt +++ b/shared_memory/CMakeLists.txt @@ -2,21 +2,17 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.12) SET(shared_memory_sources base_data.cpp - faction_association.cpp items.cpp loot.cpp main.cpp - npc_faction.cpp spells.cpp skill_caps.cpp ) SET(shared_memory_headers base_data.h - faction_association.h items.h loot.h - npc_faction.h spells.h skill_caps.h ) diff --git a/shared_memory/faction_association.cpp b/shared_memory/faction_association.cpp deleted file mode 100644 index c0ee0a9e9..000000000 --- a/shared_memory/faction_association.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2022 EQEMu Development Team (http://eqemulator.net) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "faction_association.h" -#include "../common/global_define.h" -#include "../common/shareddb.h" -#include "../common/ipc_mutex.h" -#include "../common/memory_mapped_file.h" -#include "../common/eqemu_exception.h" -#include "../common/faction.h" - -void LoadFactionAssociation(SharedDatabase *database, const std::string &prefix) { - EQ::IPCMutex mutex("factionassociations"); - mutex.Lock(); - - uint32 lists = 0; - uint32 max_list = 0; - database->GetFactionAssociationInfo(lists, max_list); - - uint32 size = static_cast(EQ::FixedMemoryHashSet::estimated_size(lists, max_list)); - - auto Config = EQEmuConfig::get(); - std::string file_name = Config->SharedMemDir + prefix + std::string("factionassociations"); - EQ::MemoryMappedFile mmf(file_name, size); - mmf.ZeroFile(); - - void *ptr = mmf.Get(); - database->LoadFactionAssociation(ptr, size, lists, max_list); - mutex.Unlock(); -} diff --git a/shared_memory/faction_association.h b/shared_memory/faction_association.h deleted file mode 100644 index 54d850a08..000000000 --- a/shared_memory/faction_association.h +++ /dev/null @@ -1,28 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2022 EQEMu Development Team (http://eqemulator.net) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef __EQEMU_SHARED_MEMORY_FACTION_ASSOCIATION_H -#define __EQEMU_SHARED_MEMORY_FACTION_ASSOCIATION_H - -#include -#include "../common/eqemu_config.h" - -class SharedDatabase; -void LoadFactionAssociation(SharedDatabase *database, const std::string &prefix); - -#endif diff --git a/shared_memory/main.cpp b/shared_memory/main.cpp index b5473d43f..acbed503a 100644 --- a/shared_memory/main.cpp +++ b/shared_memory/main.cpp @@ -27,9 +27,7 @@ #include "../common/rulesys.h" #include "../common/eqemu_exception.h" #include "../common/strings.h" -#include "faction_association.h" #include "items.h" -#include "npc_faction.h" #include "loot.h" #include "skill_caps.h" #include "spells.h" @@ -185,8 +183,6 @@ int main(int argc, char **argv) bool load_all = true; bool load_items = false; - bool load_factions = false; - bool load_faction_assoc = false; bool load_loot = false; bool load_skill_caps = false; bool load_spells = false; @@ -209,13 +205,6 @@ int main(int argc, char **argv) } break; - case 'f': - if (strcasecmp("factions", argv[i]) == 0) { - load_factions = true; - load_all = false; - } - break; - case 'l': if (strcasecmp("loot", argv[i]) == 0) { load_loot = true; @@ -232,10 +221,6 @@ int main(int argc, char **argv) load_spells = true; load_all = false; } - else if (strcasecmp("faction_assoc", argv[i]) == 0) { - load_faction_assoc = true; - load_all = false; - } break; case '-': { auto split = Strings::Split(argv[i], '='); @@ -267,15 +252,6 @@ int main(int argc, char **argv) } } - if (load_all || load_factions) { - try { - LoadFactions(&content_db, hotfix_name); - } catch (std::exception &ex) { - LogError("{}", ex.what()); - return 1; - } - } - if (load_all || load_loot) { LogInfo("Loading loot"); try { @@ -306,16 +282,6 @@ int main(int argc, char **argv) } } - if (load_all || load_faction_assoc) { - LogInfo("Loading faction associations"); - try { - LoadFactionAssociation(&content_db, hotfix_name); - } catch(std::exception &ex) { - LogError("{}", ex.what()); - return 1; - } - } - if (load_all || load_bd) { LogInfo("Loading base data"); try { diff --git a/shared_memory/npc_faction.cpp b/shared_memory/npc_faction.cpp deleted file mode 100644 index fb2d20ac4..000000000 --- a/shared_memory/npc_faction.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "npc_faction.h" -#include "../common/global_define.h" -#include "../common/shareddb.h" -#include "../common/ipc_mutex.h" -#include "../common/memory_mapped_file.h" -#include "../common/eqemu_exception.h" -#include "../common/faction.h" - -void LoadFactions(SharedDatabase *database, const std::string &prefix) { - EQ::IPCMutex mutex("faction"); - mutex.Lock(); - - uint32 lists = 0; - uint32 max_list = 0; - database->GetFactionListInfo(lists, max_list); - - uint32 size = static_cast(EQ::FixedMemoryHashSet::estimated_size(lists, max_list)); - - auto Config = EQEmuConfig::get(); - std::string file_name = Config->SharedMemDir + prefix + std::string("faction"); - EQ::MemoryMappedFile mmf(file_name, size); - mmf.ZeroFile(); - - void *ptr = mmf.Get(); - database->LoadNPCFactionLists(ptr, size, lists, max_list); - mutex.Unlock(); -} diff --git a/shared_memory/npc_faction.h b/shared_memory/npc_faction.h deleted file mode 100644 index 0b8a047f0..000000000 --- a/shared_memory/npc_faction.h +++ /dev/null @@ -1,28 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef __EQEMU_SHARED_MEMORY_NPC_FACTION_H -#define __EQEMU_SHARED_MEMORY_NPC_FACTION_H - -#include -#include "../common/eqemu_config.h" - -class SharedDatabase; -void LoadFactions(SharedDatabase *database, const std::string &prefix); - -#endif diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 4dc1e1609..1799101d0 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1389,6 +1389,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_ReloadCommands: case ServerOP_ReloadDoors: case ServerOP_ReloadDataBucketsCache: + case ServerOP_ReloadFactions: case ServerOP_ReloadGroundSpawns: case ServerOP_ReloadLevelEXPMods: case ServerOP_ReloadMerchants: diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 84a3258a6..2c4891c61 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -161,6 +161,7 @@ SET(zone_sources zone_config.cpp zonedb.cpp zone_event_scheduler.cpp + zone_npc_factions.cpp zone_reload.cpp zoning.cpp ) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 544b209d2..4d5836951 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -285,24 +285,24 @@ void NPC::DescribeAggro(Client *to_who, Mob *mob, bool verbose) { auto faction_name = content_db.GetFactionName(mob_faction_id); bool has_entry = false; for (auto faction : faction_list) { - if (static_cast(faction->factionID) == mob_faction_id) { + if (static_cast(faction.faction_id) == mob_faction_id) { to_who->Message( Chat::White, fmt::format( "{} has {} standing with Faction {} ({}) with their Faction Level of {}", to_who->GetTargetDescription(mob), ( - faction->npc_value != 0 ? + faction.npc_value != 0 ? ( - faction->npc_value > 0 ? + faction.npc_value > 0 ? "positive" : "negative" ) : "neutral" ), faction_name, - faction->factionID, - faction->npc_value + faction.faction_id, + faction.npc_value ).c_str() ); has_entry = true; diff --git a/zone/client.cpp b/zone/client.cpp index 51fae8854..9991f15c0 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7385,39 +7385,42 @@ FACTION_VALUE Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_ra } //Sets the characters faction standing with the specified NPC. -void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity, bool quest) +void Client::SetFactionLevel( + uint32 character_id, + uint32 npc_faction_id, + uint8 class_id, + uint8 race_id, + uint8 deity_id, + bool is_quest +) { - int32 faction_id[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - int32 npc_value[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - uint8 temp[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - int32 current_value; + auto l = zone->GetNPCFactionEntries(npc_faction_id); - // Get the npc faction list - if (!content_db.GetNPCFactionList(npc_id, faction_id, npc_value, temp)) { + if (l.empty()) { return; } - for (int i = 0; i < MAX_NPC_FACTIONS; i++) { - int32 faction_before_hit; - FactionMods fm; - int32 this_faction_max; - int32 this_faction_min; + int current_value; - if (faction_id[i] <= 0) + for (auto& e : l) { + if (e.faction_id <= 0 || e.value == 0) { continue; + } - // Find out starting faction for this faction - // It needs to be used to adj max and min personal - // The range is still the same, 1200-3000(4200), but adjusted for base - content_db.GetFactionData(&fm, GetClass(), GetFactionRace(), GetDeity(), faction_id[i]); + int faction_before; + int faction_minimum; + int faction_maximum; - if (quest) - { - //The ole switcheroo - if (npc_value[i] > 0) - npc_value[i] = -std::abs(npc_value[i]); - else if (npc_value[i] < 0) - npc_value[i] = std::abs(npc_value[i]); + FactionMods faction_modifiers; + + content_db.GetFactionData(&faction_modifiers, class_id, race_id, deity_id, e.faction_id); + + if (is_quest) { + if (e.npc_value > 0) { + e.npc_value = -std::abs(e.npc_value); + } else if (e.npc_value < 0) { + e.npc_value = std::abs(e.npc_value); + } } // Adjust the amount you can go up or down so the resulting range @@ -7425,23 +7428,36 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui // // Adjust these values for cases where starting faction is below // min or above max by not allowing any earn in those directions. - this_faction_min = fm.min - fm.base; - this_faction_min = std::min(0, this_faction_min); - this_faction_max = fm.max - fm.base; - this_faction_max = std::max(0, this_faction_max); + faction_minimum = faction_modifiers.min - faction_modifiers.base; + faction_minimum = std::min(0, faction_minimum); + + faction_maximum = faction_modifiers.max - faction_modifiers.base; + faction_maximum = std::max(0, faction_maximum); // Get the characters current value with that faction - current_value = GetCharacterFactionLevel(faction_id[i]); - faction_before_hit = current_value; + current_value = GetCharacterFactionLevel(e.faction_id); + faction_before = current_value; - UpdatePersonalFaction(char_id, npc_value[i], faction_id[i], ¤t_value, temp[i], this_faction_min, this_faction_max); + UpdatePersonalFaction( + character_id, + e.value, + e.faction_id, + ¤t_value, + e.temp, + faction_minimum, + faction_maximum + ); - //Message(Chat::Lime, "Min(%d) Max(%d) Before(%d), After(%d)\n", this_faction_min, this_faction_max, faction_before_hit, current_value); - - SendFactionMessage(npc_value[i], faction_id[i], faction_before_hit, current_value, temp[i], this_faction_min, this_faction_max); + SendFactionMessage( + e.value, + e.faction_id, + faction_before, + current_value, + e.temp, + faction_minimum, + faction_maximum + ); } - - return; } void Client::SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp) @@ -8213,33 +8229,92 @@ void Client::CashReward(uint32 copper, uint32 silver, uint32 gold, uint32 platin QueuePacket(outapp.get()); } -void Client::RewardFaction(int id, int amount) +void Client::RewardFaction(int faction_id, int amount) { - // first we hit the primary faction, even without any associations - SetFactionLevel2(CharacterID(), id, GetClass(), GetBaseRace(), GetDeity(), amount, false); + SetFactionLevel2(CharacterID(), faction_id, GetClass(), GetBaseRace(), GetDeity(), amount, false); - auto faction_assoc = content_db.GetFactionAssociationHit(id); - // We could log here, but since it's actually expected for some not to have entries, it would be noisy. - if (!faction_assoc) { + auto f = zone->GetFactionAssociation(faction_id); + if (!f) { return; } - // now hit them in order - for (int i = 0; i < MAX_FACTION_ASSOC; ++i) { - if (faction_assoc->hits[i].id <= 0) // we don't allow later entries - break; - if (faction_assoc->hits[i].multiplier == 0.0f) { - LogFaction("Bad association multiplier for ID {} entry {}", id, i + 1); - continue; + std::vector faction_ids = { + f->id_1, + f->id_2, + f->id_3, + f->id_4, + f->id_5, + f->id_6, + f->id_7, + f->id_8, + f->id_9, + f->id_10 + }; + + std::vector faction_modifiers = { + f->mod_1, + f->mod_2, + f->mod_3, + f->mod_4, + f->mod_5, + f->mod_6, + f->mod_7, + f->mod_8, + f->mod_9, + f->mod_10 + }; + + std::vector temporary_values = { + static_cast(faction_modifiers[0] * amount), + static_cast(faction_modifiers[1] * amount), + static_cast(faction_modifiers[2] * amount), + static_cast(faction_modifiers[3] * amount), + static_cast(faction_modifiers[4] * amount), + static_cast(faction_modifiers[5] * amount), + static_cast(faction_modifiers[6] * amount), + static_cast(faction_modifiers[7] * amount), + static_cast(faction_modifiers[8] * amount), + static_cast(faction_modifiers[9] * amount) + }; + + std::vector signs = { + temporary_values[0] < 0.0f ? -1 : 1, + temporary_values[1] < 0.0f ? -1 : 1, + temporary_values[2] < 0.0f ? -1 : 1, + temporary_values[3] < 0.0f ? -1 : 1, + temporary_values[4] < 0.0f ? -1 : 1, + temporary_values[5] < 0.0f ? -1 : 1, + temporary_values[6] < 0.0f ? -1 : 1, + temporary_values[7] < 0.0f ? -1 : 1, + temporary_values[8] < 0.0f ? -1 : 1, + temporary_values[9] < 0.0f ? -1 : 1 + }; + + std::vector new_values = { + std::max(1, static_cast(std::abs(temporary_values[0]))) * signs[0], + std::max(1, static_cast(std::abs(temporary_values[1]))) * signs[1], + std::max(1, static_cast(std::abs(temporary_values[2]))) * signs[2], + std::max(1, static_cast(std::abs(temporary_values[3]))) * signs[3], + std::max(1, static_cast(std::abs(temporary_values[4]))) * signs[4], + std::max(1, static_cast(std::abs(temporary_values[5]))) * signs[5], + std::max(1, static_cast(std::abs(temporary_values[6]))) * signs[6], + std::max(1, static_cast(std::abs(temporary_values[7]))) * signs[7], + std::max(1, static_cast(std::abs(temporary_values[8]))) * signs[8], + std::max(1, static_cast(std::abs(temporary_values[9]))) * signs[9] + }; + + for (uint16 slot_id = 0; slot_id < faction_ids.size(); slot_id++) { + if (faction_ids[slot_id] > 0) { + SetFactionLevel2( + CharacterID(), + faction_ids[slot_id], + GetClass(), + GetBaseRace(), + GetDeity(), + new_values[slot_id], + false + ); } - - // value is truncated and min clamped to 1 (or -1) - float temp = faction_assoc->hits[i].multiplier * amount; - int sign = temp < 0.0f ? -1 : 1; - int32 new_amount = std::max(1, static_cast(std::abs(temp))) * sign; - - SetFactionLevel2(CharacterID(), faction_assoc->hits[i].id, GetClass(), GetBaseRace(), GetDeity(), - new_amount, false); } } @@ -9019,6 +9094,7 @@ void Client::ShowDevToolsMenu() menu_reload_three += Saylink::Silent("#reload data_buckets_cache", "Databuckets"); menu_reload_three += " | " + Saylink::Silent("#reload doors", "Doors"); + menu_reload_three += " | " + Saylink::Silent("#reload factions", "Factions"); menu_reload_three += " | " + Saylink::Silent("#reload ground_spawns", "Ground Spawns"); menu_reload_four += Saylink::Silent("#reload logs", "Level Based Experience Modifiers"); @@ -10993,7 +11069,24 @@ void Client::SendReloadCommandMessages() { ); auto dztemplates_link = Saylink::Silent("#reload dztemplates"); - Message(Chat::White, fmt::format("Usage: {} - Reloads Dynamic Zone Templates globally", dztemplates_link).c_str()); + + Message( + Chat::White, + fmt::format( + "Usage: {} - Reloads Dynamic Zone Templates globally", + dztemplates_link + ).c_str() + ); + + auto factions_link = Saylink::Silent("#reload factions"); + + Message( + Chat::White, + fmt::format( + "Usage: {} - Reloads Factions globally", + factions_link + ).c_str() + ); auto ground_spawns_link = Saylink::Silent("#reload ground_spawns"); diff --git a/zone/client.h b/zone/client.h index 921952c84..9ee58c2d2 100644 --- a/zone/client.h +++ b/zone/client.h @@ -699,7 +699,7 @@ public: void SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 faction_before_hit, int32 totalvalue, uint8 temp, int32 this_faction_min, int32 this_faction_max); void UpdatePersonalFaction(int32 char_id, int32 npc_value, int32 faction_id, int32 *current_value, int32 temp, int32 this_faction_min, int32 this_faction_max); - void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity, bool quest = false); + void SetFactionLevel(uint32 char_id, uint32 npc_faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, bool quest = false); void SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp); int32 GetRawItemAC(); diff --git a/zone/gm_commands/npcedit.cpp b/zone/gm_commands/npcedit.cpp index d51c762b5..c39065191 100755 --- a/zone/gm_commands/npcedit.cpp +++ b/zone/gm_commands/npcedit.cpp @@ -340,9 +340,9 @@ void command_npcedit(Client *c, const Seperator *sep) } else if (!strcasecmp(sep->arg[1], "faction")) { if (sep->IsNumber(2)) { auto npc_faction_id = Strings::ToInt(sep->arg[2]); - const NPCFactionList* cf = content_db.GetNPCFactionEntry(npc_faction_id); - if (cf) { - auto faction_id = cf->primaryfaction; + const auto f = zone->GetNPCFaction(npc_faction_id); + if (f) { + auto faction_id = f->primaryfaction; auto faction_name = content_db.GetFactionName(faction_id); n.npc_faction_id = npc_faction_id; diff --git a/zone/gm_commands/reload.cpp b/zone/gm_commands/reload.cpp index 49c614255..b07f9a9da 100644 --- a/zone/gm_commands/reload.cpp +++ b/zone/gm_commands/reload.cpp @@ -21,6 +21,7 @@ void command_reload(Client *c, const Seperator *sep) bool is_data_buckets = !strcasecmp(sep->arg[1], "data_buckets_cache"); bool is_doors = !strcasecmp(sep->arg[1], "doors"); bool is_dztemplates = !strcasecmp(sep->arg[1], "dztemplates"); + bool is_factions = !strcasecmp(sep->arg[1], "factions"); bool is_ground_spawns = !strcasecmp(sep->arg[1], "ground_spawns"); bool is_level_mods = !strcasecmp(sep->arg[1], "level_mods"); bool is_logs = !strcasecmp(sep->arg[1], "logs") || is_logs_reload_alias; @@ -50,6 +51,7 @@ void command_reload(Client *c, const Seperator *sep) !is_data_buckets && !is_doors && !is_dztemplates && + !is_factions && !is_ground_spawns && !is_level_mods && !is_logs && @@ -100,6 +102,9 @@ void command_reload(Client *c, const Seperator *sep) } else if (is_dztemplates) { c->Message(Chat::White, "Attempting to reload Dynamic Zone Templates globally."); pack = new ServerPacket(ServerOP_ReloadDzTemplates, 0); + } else if (is_factions) { + c->Message(Chat::White, "Attempting to reload Factions globally."); + pack = new ServerPacket(ServerOP_ReloadFactions, 0); } else if (is_ground_spawns) { c->Message(Chat::White, "Attempting to reload Ground Spawns globally."); pack = new ServerPacket(ServerOP_ReloadGroundSpawns, 0); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index f0bcbffc8..e9358ad69 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -390,9 +390,9 @@ int Lua_Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 race, uint return static_cast(self->GetFactionLevel(char_id, npc_id, race, class_, deity, faction, npc)); } -void Lua_Client::SetFactionLevel(uint32 char_id, uint32 npc_id, int char_class, int char_race, int char_deity) { +void Lua_Client::SetFactionLevel(uint32 char_id, uint32 npc_faction_id, int char_class, int char_race, int char_deity) { Lua_Safe_Call_Void(); - self->SetFactionLevel(char_id, npc_id, char_class, char_race, char_deity); + self->SetFactionLevel(char_id, npc_faction_id, char_class, char_race, char_deity); } void Lua_Client::SetFactionLevel2(uint32 char_id, int faction_id, int char_class, int char_race, int char_deity, int value, int temp) { diff --git a/zone/lua_client.h b/zone/lua_client.h index 2696cea65..742b6650e 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -145,7 +145,7 @@ public: bool TeleportRaidToPlayerByName(std::string player_name); void ChangeLastName(std::string last_name); int GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 race, uint32 class_, uint32 deity, uint32 faction, Lua_NPC npc); - void SetFactionLevel(uint32 char_id, uint32 npc_id, int char_class, int char_race, int char_deity); + void SetFactionLevel(uint32 char_id, uint32 npc_faction_id, int char_class, int char_race, int char_deity); void SetFactionLevel2(uint32 char_id, int faction_id, int char_class, int char_race, int char_deity, int value, int temp); void RewardFaction(int id, int amount); int GetRawItemAC(); diff --git a/zone/main.cpp b/zone/main.cpp index 21353d4c3..13dbce7da 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -372,14 +372,6 @@ int main(int argc, char **argv) LogError("Failed. But ignoring error and going on.."); } - if (!content_db.LoadNPCFactionLists(hotfix_name)) { - LogError("Loading npcs faction lists failed!"); - return 1; - } - if (!content_db.LoadFactionAssociation(hotfix_name)) { - LogError("Loading faction association hits failed!"); - return 1; - } if (!database.LoadLoot(hotfix_name)) { LogError("Loading loot failed!"); return 1; diff --git a/zone/mob.cpp b/zone/mob.cpp index 3b0cbb2fa..34c5a7105 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2775,14 +2775,13 @@ void Mob::ShowStats(Client* c) // Faction if (t->GetNPCFactionID()) { - auto faction_id = t->GetPrimaryFaction(); - auto faction_name = content_db.GetFactionName(faction_id); + const std::string& faction_name = content_db.GetFactionName(t->GetPrimaryFaction()); c->Message( Chat::White, fmt::format( "Faction: {} ({})", faction_name, - faction_id + t->GetPrimaryFaction() ).c_str() ); } diff --git a/zone/npc.cpp b/zone/npc.cpp index 36716d9f5..1eead5772 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -512,34 +512,19 @@ NPC::~NPC() { AI_Stop(); - if(proximity != nullptr) { + if (proximity) { entity_list.RemoveProximity(GetID()); safe_delete(proximity); } safe_delete(NPCTypedata_ours); - { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - safe_delete(item); - } - itemlist.clear(); + for (auto* e : itemlist) { + safe_delete(e); } - { - std::list::iterator cur,end; - cur = faction_list.begin(); - end = faction_list.end(); - for(; cur != end; ++cur) { - struct NPCFaction* fac = *cur; - safe_delete(fac); - } + itemlist.clear(); faction_list.clear(); - } safe_delete(reface_timer); safe_delete(swarmInfoPtr); @@ -3204,19 +3189,17 @@ FACTION_VALUE NPC::GetReverseFactionCon(Mob* iOther) { //Look through our faction list and return a faction con based //on the npc_value for the other person's primary faction in our list. -FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) { - std::list::iterator cur,end; - cur = faction_list.begin(); - end = faction_list.end(); - for(; cur != end; ++cur) { - struct NPCFaction* fac = *cur; - if ((int32)fac->factionID == other_faction) { - if (fac->npc_value > 0) +FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) +{ + for (const auto& e : faction_list) { + if (e.faction_id == other_faction) { + if (e.npc_value > 0) { return FACTION_ALLY; - else if (fac->npc_value < 0) + } else if (e.npc_value < 0) { return FACTION_SCOWLS; - else + } else { return FACTION_INDIFFERENTLY; + } } } @@ -3225,14 +3208,16 @@ FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) { // where an npc is on a faction but has no hits (hence no entry in // npc_faction_entries). - if (GetPrimaryFaction() == other_faction) + if (GetPrimaryFaction() == other_faction) { return FACTION_ALLY; - else + } else { return FACTION_INDIFFERENTLY; + } } -bool NPC::IsFactionListAlly(uint32 other_faction) { - return(CheckNPCFactionAlly(other_faction) == FACTION_ALLY); +bool NPC::IsFactionListAlly(uint32 other_faction) +{ + return CheckNPCFactionAlly(other_faction) == FACTION_ALLY; } int NPC::GetScore() @@ -3673,9 +3658,9 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) */ if (mob->GetLevel() >= 50 || mob->AlwaysAggro() || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY) { if (mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) { - const NPCFactionList *cf = content_db.GetNPCFactionEntry(mob->CastToNPC()->GetNPCFactionID()); - if (cf) { - if (cf->assistprimaryfaction == 0) { + const auto f = zone->GetNPCFaction(mob->CastToNPC()->GetNPCFactionID()); + if (f) { + if (f->ignore_primary_assist) { continue; //Same faction and ignore primary assist } } diff --git a/zone/npc.h b/zone/npc.h index fcacc8fbc..3bc250ab5 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -26,6 +26,7 @@ #include "../common/zone_store.h" #include "zonedump.h" #include "../common/loottable.h" +#include "../common/repositories/npc_faction_entries_repository.h" #include #include @@ -296,7 +297,7 @@ public: void SetNPCFactionID(int32 in) { npc_faction_id = in; - content_db.GetFactionIdsForNPC(npc_faction_id, &faction_list, &primary_faction); + content_db.GetFactionIDsForNPC(npc_faction_id, &faction_list, &primary_faction); } glm::vec4 m_SpawnPoint; @@ -564,7 +565,6 @@ protected: friend class EntityList; friend class Aura; - std::list faction_list; uint32 copper; uint32 silver; uint32 gold; @@ -573,6 +573,8 @@ protected: uint32 spawn_group_id; uint16 wp_m; + std::list faction_list; + int32 npc_faction_id; int32 primary_faction; int32 faction_amount; diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 38629295d..762619f81 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -344,9 +344,9 @@ int Perl_Client_GetFactionLevel(Client* self, uint32 char_id, uint32 npc_id, uin return self->GetFactionLevel(char_id, npc_id, race_id, class_id, deity_id, faction_id, tnpc); } -void Perl_Client_SetFactionLevel(Client* self, uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity) // @categories Faction +void Perl_Client_SetFactionLevel(Client* self, uint32 char_id, uint32 npc_faction_id, uint8 char_class, uint8 char_race, uint8 char_deity) // @categories Faction { - self->SetFactionLevel(char_id, npc_id, char_class, char_race, char_deity); + self->SetFactionLevel(char_id, npc_faction_id, char_class, char_race, char_deity); } void Perl_Client_SetFactionLevel2(Client* self, uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value) // @categories Faction diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index ee3d9541b..0f7c92286 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1440,6 +1440,7 @@ void QuestManager::rewardfaction(int faction_id, int faction_value) { QuestManagerCurrentQuestVars(); if (initiator) { if (faction_id != 0 && faction_value != 0) { + zone->LoadFactionAssociation(faction_id); initiator->RewardFaction(faction_id, faction_value); } } diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index 8485352fd..7d3878730 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -1057,6 +1057,7 @@ void ClientTaskState::RewardTask(Client *c, const TaskInformation *ti, ClientTas // just use normal NPC faction ID stuff if (ti->faction_reward && ti->faction_amount == 0) { + zone->LoadNPCFaction(ti->faction_reward); c->SetFactionLevel( c->CharacterID(), ti->faction_reward, @@ -1065,6 +1066,8 @@ void ClientTaskState::RewardTask(Client *c, const TaskInformation *ti, ClientTas c->GetDeity() ); } else if (ti->faction_reward != 0 && ti->faction_amount != 0) { + // faction_reward is a faction ID + zone->LoadFactionAssociation(ti->faction_reward); c->RewardFaction( ti->faction_reward, ti->faction_amount diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index a66b01bad..22a2513b8 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1989,6 +1989,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } break; } + case ServerOP_ReloadFactions: + { + if (zone && zone->IsLoaded()) { + zone->SendReloadMessage("Factions"); + content_db.LoadFactionData(); + zone->ReloadNPCFactions(); + zone->ReloadFactionAssociations(); + } + + break; + } case ServerOP_ReloadLevelEXPMods: { if (zone && zone->IsLoaded()) { @@ -3497,16 +3508,6 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) LogError("Loading items failed!"); } - LogInfo("Loading npc faction lists"); - if (!content_db.LoadNPCFactionLists(hotfix_name)) { - LogError("Loading npcs faction lists failed!"); - } - - LogInfo("Loading faction association hits"); - if (!content_db.LoadFactionAssociation(hotfix_name)) { - LogError("Loading faction association hits failed!"); - } - LogInfo("Loading loot tables"); if (!content_db.LoadLoot(hotfix_name)) { LogError("Loading loot failed!"); diff --git a/zone/zone.h b/zone/zone.h index 3136d00eb..dec231648 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -38,6 +38,9 @@ #include "queryserv.h" #include "../common/discord/discord.h" #include "../common/repositories/dynamic_zone_templates_repository.h" +#include "../common/repositories/npc_faction_repository.h" +#include "../common/repositories/npc_faction_entries_repository.h" +#include "../common/repositories/faction_association_repository.h" struct EXPModifier { @@ -407,6 +410,21 @@ public: void ReloadContentFlags(); + void LoadNPCFaction(const uint32 npc_faction_id); + void LoadNPCFactions(const std::vector& npc_faction_ids); + void ClearNPCFactions(); + void ReloadNPCFactions(); + NpcFactionRepository::NpcFaction* GetNPCFaction(const uint32 npc_faction_id); + std::vector GetNPCFactionEntries(const uint32 npc_faction_id) const; + + void LoadNPCFactionAssociation(const uint32 npc_faction_id); + void LoadNPCFactionAssociations(const std::vector& npc_faction_ids); + void LoadFactionAssociation(const uint32 faction_id); + void LoadFactionAssociations(const std::vector& faction_ids); + void ClearFactionAssociations(); + void ReloadFactionAssociations(); + FactionAssociationRepository::FactionAssociation* GetFactionAssociation(const uint32 faction_id); + private: bool allow_mercs; bool can_bind; @@ -457,6 +475,10 @@ private: Timer qglobal_purge_timer; ZoneSpellsBlocked *blocked_spells; + // Factions + std::vector m_npc_factions = { }; + std::vector m_npc_faction_entries = { }; + std::vector m_faction_associations = { }; }; #endif diff --git a/zone/zone_npc_factions.cpp b/zone/zone_npc_factions.cpp new file mode 100644 index 000000000..e0d426e42 --- /dev/null +++ b/zone/zone_npc_factions.cpp @@ -0,0 +1,277 @@ +#include +#include "zone.h" +#include "../common/repositories/npc_faction_repository.h" +#include "../common/repositories/npc_faction_entries_repository.h" + +void Zone::LoadNPCFactions(const std::vector &npc_faction_ids) +{ + LogFaction( + "Load for Faction IDs [{}]", + Strings::Join(npc_faction_ids, ", ") + ); + + if (npc_faction_ids.empty()) { + LogFactionDetail("No NPC factions to load."); + return; + } + + // Narrow the list sent in, to new npc_faction_ids that are being loaded + // as the result of a new spawn. Ignore those already loaded. + + std::vector new_npc_faction_ids = { }; + + for (const auto& e : npc_faction_ids) { + bool found = false; + + for (const auto& nf : m_npc_factions) { + found = (nf.id == e); + if (found) { + LogFaction("Already loaded npc_faction [{}]", nf.id); + break; + } + } + + // This one is new + if (!found) { + new_npc_faction_ids.emplace_back(e); + } + } + + if (new_npc_faction_ids.empty()) { + LogFactionDetail("No New NPC factions to load."); + return; + } + + auto npc_factions = NpcFactionRepository::GetWhere( + content_db, + fmt::format( + "`id` IN ({})", + Strings::Join(new_npc_faction_ids, ", ") + ) + ); + + auto npc_faction_entries = NpcFactionEntriesRepository::GetWhere( + content_db, + fmt::format( + "`npc_faction_id` IN ({})", + Strings::Join(new_npc_faction_ids, ", ") + ) + ); + + for (const auto& e : npc_factions) { + m_npc_factions.emplace_back(e); + + for (const auto& f : npc_faction_entries) { + m_npc_faction_entries.emplace_back(f); + } + } + + if (npc_factions.size() > 1) { + LogFaction("Loaded [{}] Factions", npc_factions.size()); + } +} + +void Zone::LoadNPCFaction(const uint32 npc_faction_id) +{ + if (!npc_faction_id) { + return; + } + + LogFaction("LoadNPCFaction for [{}]", npc_faction_id); + LoadNPCFactions({ npc_faction_id }); +} + +void Zone::ClearNPCFactions() +{ + m_npc_factions.clear(); + m_npc_faction_entries.clear(); +} + +void Zone::ReloadNPCFactions() +{ + LogFaction("Reloading NPC Factions"); + + ClearNPCFactions(); + + std::vector npc_faction_ids = { }; + + for (const auto& n : entity_list.GetNPCList()) { + if (n.second->GetNPCFactionID() != 0) { + if ( + std::find( + npc_faction_ids.begin(), + npc_faction_ids.end(), + n.second->GetNPCFactionID() + ) == npc_faction_ids.end() + ) { + npc_faction_ids.emplace_back(n.second->GetNPCFactionID()); + } + } + } + + LoadNPCFactions(npc_faction_ids); +} + +NpcFactionRepository::NpcFaction* Zone::GetNPCFaction(const uint32 npc_faction_id) +{ + for (auto& e : m_npc_factions) { + if (e.id == npc_faction_id) { + return &e; + } + } + + return nullptr; +} + +std::vector Zone::GetNPCFactionEntries(const uint32 npc_faction_id) const +{ + std::vector npc_faction_entries = { }; + + std::vector faction_ids; + + for (auto e : m_npc_faction_entries) { + if ( + e.npc_faction_id == npc_faction_id && + std::find( + faction_ids.begin(), + faction_ids.end(), + e.faction_id + ) == faction_ids.end() + ) { + faction_ids.emplace_back(e.faction_id); + npc_faction_entries.emplace_back(e); + } + } + + return npc_faction_entries; +} + +void Zone::LoadNPCFactionAssociations(const std::vector& npc_faction_ids) +{ + LogFaction( + "Load Associations for NPC Faction IDs [{}]", + Strings::Join(npc_faction_ids, ", ") + ); + + if (npc_faction_ids.empty()) { + LogFactionDetail("No Faction Associations to load."); + return; + } + + std::vector faction_ids = { }; + + for (const auto& e : npc_faction_ids) { + for (const auto& f : m_npc_factions) { + bool found = false; + if (e == f.id && f.primaryfaction > 0) { + for (const auto& a : m_faction_associations) { + if (a.id == f.primaryfaction) { + found = true; + LogFaction("Association [{}] already loaded", a.id); + break; + } + } + + if (!found) { + faction_ids.emplace_back(f.primaryfaction); + } + } + } + } + + if (faction_ids.empty()) { + LogFactionDetail("No New Faction Associations to load."); + } + else { + LoadFactionAssociations(faction_ids); + } +} + +void Zone::LoadNPCFactionAssociation(const uint32 npc_faction_id) +{ + if (!npc_faction_id) { + return; + } + + LoadNPCFactionAssociations({ npc_faction_id }); +} + +void Zone::LoadFactionAssociations(const std::vector& faction_ids) +{ + LogFaction( + "These are the primary faction IDs [{}]", + Strings::Join(faction_ids, ", ") + ); + + const auto& faction_associations = FactionAssociationRepository::GetWhere( + content_db, + fmt::format( + "`id` IN ({})", + Strings::Join(faction_ids, ", ") + ) + ); + + if (faction_associations.empty()) { + LogFaction( + "No Faction Association Entries to load for Faction IDs [{}]", + Strings::Join(faction_ids, ", ") + ); + return; + } + + for (const auto& e : faction_associations) { + m_faction_associations.emplace_back(e); + } + + LogFaction("Loaded [{}] Faction Associations.", faction_associations.size()); +} + +void Zone::LoadFactionAssociation(const uint32 faction_id) +{ + if (!faction_id) { + return; + } + + LoadFactionAssociations({ faction_id }); +} + + +void Zone::ClearFactionAssociations() +{ + m_faction_associations.clear(); +} + +void Zone::ReloadFactionAssociations() +{ + ClearFactionAssociations(); + + std::vector npc_faction_ids = { }; + + for (const auto& n : entity_list.GetNPCList()) { + if (n.second->GetNPCFactionID() != 0) { + if ( + std::find( + npc_faction_ids.begin(), + npc_faction_ids.end(), + n.second->GetNPCFactionID() + ) == npc_faction_ids.end() + ) { + npc_faction_ids.emplace_back(n.second->GetNPCFactionID()); + } + } + } + + LogFaction("Reloading Faction Associations"); + LoadNPCFactionAssociations(npc_faction_ids); +} + +FactionAssociationRepository::FactionAssociation* Zone::GetFactionAssociation(const uint32 faction_id) +{ + for (auto& e : m_faction_associations) { + if (e.id == faction_id) { + return &e; + } + } + + return nullptr; +} diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 5bfae6980..50755459e 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1746,6 +1746,7 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load } std::vector npc_ids; + std::vector npc_faction_ids; for (NpcTypesRepository::NpcTypes &n : NpcTypesRepository::GetWhere((Database &) content_db, filter)) { NPCType *t; @@ -1798,7 +1799,6 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load t->special_abilities[0] = '\0'; } - t->npc_spells_id = n.npc_spells_id; t->npc_spells_effects_id = n.npc_spells_effects_id; t->d_melee_texture1 = n.d_melee_texture1; @@ -1845,6 +1845,18 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load t->drakkin_tattoo = n.drakkin_tattoo; t->drakkin_details = n.drakkin_details; + if (t->npc_faction_id > 0) { + if ( + std::find( + npc_faction_ids.begin(), + npc_faction_ids.end(), + t->npc_faction_id + ) == npc_faction_ids.end() + ) { + npc_faction_ids.emplace_back(t->npc_faction_id); + } + } + // armor tint uint32 armor_tint_id = n.armortint_id; t->armor_tint.Head.Color = (n.armortint_red & 0xFF) << 16; @@ -1967,6 +1979,11 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load DataBucket::BulkLoadEntities(DataBucketLoadType::NPC, npc_ids); + if (!npc_faction_ids.empty()) { + zone->LoadNPCFactions(npc_faction_ids); + zone->LoadNPCFactionAssociations(npc_faction_ids); + } + return npc; } @@ -3466,30 +3483,6 @@ std::string ZoneDatabase::GetFactionName(int32 faction_id) return faction_name; } -//o-------------------------------------------------------------- -//| Name: GetNPCFactionList; Dec. 16, 2001 -//o-------------------------------------------------------------- -//| Purpose: Gets a list of faction_id's and values bound to the npc_id. Returns false on failure. -//o-------------------------------------------------------------- -bool ZoneDatabase::GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction) { - if (npcfaction_id <= 0) { - if (primary_faction) - *primary_faction = npcfaction_id; - return true; - } - const NPCFactionList* nfl = GetNPCFactionEntry(npcfaction_id); - if (!nfl) - return false; - if (primary_faction) - *primary_faction = nfl->primaryfaction; - for (int i=0; ifactionid[i]; - value[i] = nfl->factionvalue[i]; - temp[i] = nfl->factiontemp[i]; - } - return true; -} - //o-------------------------------------------------------------- //| Name: SetCharacterFactionLevel; Dec. 20, 2001 //o-------------------------------------------------------------- @@ -3634,46 +3627,40 @@ bool ZoneDatabase::LoadFactionData() return true; } -bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction) { - if (nfl_id <= 0) { - std::list::iterator cur,end; - cur = faction_list->begin(); - end = faction_list->end(); - for(; cur != end; ++cur) { - struct NPCFaction* tmp = *cur; - safe_delete(tmp); +bool ZoneDatabase::GetFactionIDsForNPC( + uint32 npc_faction_id, + std::list *faction_list, + int32* primary_faction +) +{ + if (npc_faction_id <= 0) { + faction_list->clear(); + + if (primary_faction) { + *primary_faction = npc_faction_id; } - faction_list->clear(); - if (primary_faction) - *primary_faction = nfl_id; return true; } - const NPCFactionList* nfl = GetNPCFactionEntry(nfl_id); - if (!nfl) - return false; - if (primary_faction) - *primary_faction = nfl->primaryfaction; - std::list::iterator cur,end; - cur = faction_list->begin(); - end = faction_list->end(); - for(; cur != end; ++cur) { - struct NPCFaction* tmp = *cur; - safe_delete(tmp); + const auto& npcf = zone->GetNPCFaction(npc_faction_id); + if (!npcf) { + LogError("No NPC faction entry for [{}]", npc_faction_id); + return false; } + + const auto& l = zone->GetNPCFactionEntries(npc_faction_id); + + if (primary_faction) { + *primary_faction = npcf->primaryfaction; + } + faction_list->clear(); - for (int i=0; ifactionid[i]) { - pFac = new struct NPCFaction; - pFac->factionID = nfl->factionid[i]; - pFac->value_mod = nfl->factionvalue[i]; - pFac->npc_value = nfl->factionnpcvalue[i]; - pFac->temp = nfl->factiontemp[i]; - faction_list->push_back(pFac); - } + + for (const auto& e: l) { + faction_list->emplace_back(e); } + return true; } diff --git a/zone/zonedb.h b/zone/zonedb.h index edebe47bc..03711a6cd 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -12,6 +12,7 @@ #include "event_codes.h" #include "../common/repositories/doors_repository.h" #include "../common/races.h" +#include "../common/repositories/npc_faction_entries_repository.h" #include "bot_database.h" @@ -505,11 +506,10 @@ public: uint32 UpdateCharacterCorpseConsent(uint32 character_id, uint32 guild_consent_id); /* Faction */ - bool GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction = 0); bool GetFactionData(FactionMods* fd, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id); //needed for factions Dec, 16 2001 bool GetFactionName(int faction_id, char* name, uint32 buflen); // needed for factions Dec, 16 2001 std::string GetFactionName(int faction_id); - bool GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction = 0); // improve faction handling + bool GetFactionIDsForNPC(uint32 npc_faction_id, std::list* faction_list, int32* primary_faction = 0); // improve faction handling bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // needed for factions Dec, 16 2001 bool LoadFactionData(); inline uint32 GetMaxFaction() { return max_faction; }