[Factions] Remove from shared memory and simplify (#3999)

* [Factions] Remove from shared memory and simplify

- Removes factions from shared memory and moves to zone based storage of repositories and changes the NPC `faction_list` to also use repositories.
- This affects NPC Factions and Faction Associations.

* Bug fixes.

* Update client.cpp

* Update client.cpp

* Update client.cpp

* Cleanup

* Update client.cpp

* Update client.cpp

* Update client.cpp

* Final push

* Update CMakeLists.txt

* Consolidate reloading.

* [Cleanup] PR # 3999 (#4039)

* [Fixes for PR # 3999

* [Reload actual in game factions, not just the umbrella data.

* syntax

* Fix typo

* Foix bug where primary_faction not filled in when no hits

* Fix typos

* Fix splash factions for kills.

* Fix typo

* Fix more variable names to be accurate

* Fix Loads to load new ones as they come in.

* Load npc_factions without primary (tasks) and support old task faction

* Rename to make way for new LoadFactionAssocition (by faction_id)

* Fix some review comments

* Add code to load factions for splash tasks and quests.

* Fix issue with sign and RewardFaction, fix Log Message

---------

Co-authored-by: Paul Coene <noudess@gmail.com>
This commit is contained in:
Alex King 2024-02-04 10:38:38 -05:00 committed by GitHub
parent 029581772d
commit 297e358c02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 568 additions and 597 deletions

View File

@ -24,17 +24,15 @@
#include <string>
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

View File

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

View File

@ -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<NPCFactionList> hash(static_cast<uint8*>(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<int8>(Strings::ToInt(row[5]));
faction.factiontemp[current_entry] = static_cast<uint8>(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<EQ::MemoryMappedFile>(file_name);
faction_hash = std::make_unique<EQ::FixedMemoryHashSet<NPCFactionList>>(static_cast<uint8*>(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<uint32>(FactionAssociationRepository::Count(*this));
max_lists = static_cast<uint32>(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<FactionAssociations> hash(reinterpret_cast<uint8 *>(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<EQ::MemoryMappedFile>(new EQ::MemoryMappedFile(file_name));
faction_associations_hash = std::unique_ptr<EQ::FixedMemoryHashSet<FactionAssociations>>(
new EQ::FixedMemoryHashSet<FactionAssociations>(reinterpret_cast<uint8 *>(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,

View File

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

View File

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

View File

@ -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<uint32>(EQ::FixedMemoryHashSet<FactionAssociations>::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();
}

View File

@ -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 <string>
#include "../common/eqemu_config.h"
class SharedDatabase;
void LoadFactionAssociation(SharedDatabase *database, const std::string &prefix);
#endif

View File

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

View File

@ -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<uint32>(EQ::FixedMemoryHashSet<NPCFactionList>::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();
}

View File

@ -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 <string>
#include "../common/eqemu_config.h"
class SharedDatabase;
void LoadFactions(SharedDatabase *database, const std::string &prefix);
#endif

View File

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

View File

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

View File

@ -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<int>(faction->factionID) == mob_faction_id) {
if (static_cast<int>(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;

View File

@ -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], &current_value, temp[i], this_faction_min, this_faction_max);
UpdatePersonalFaction(
character_id,
e.value,
e.faction_id,
&current_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<int> 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<float> 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<float> temporary_values = {
static_cast<float>(faction_modifiers[0] * amount),
static_cast<float>(faction_modifiers[1] * amount),
static_cast<float>(faction_modifiers[2] * amount),
static_cast<float>(faction_modifiers[3] * amount),
static_cast<float>(faction_modifiers[4] * amount),
static_cast<float>(faction_modifiers[5] * amount),
static_cast<float>(faction_modifiers[6] * amount),
static_cast<float>(faction_modifiers[7] * amount),
static_cast<float>(faction_modifiers[8] * amount),
static_cast<float>(faction_modifiers[9] * amount)
};
std::vector<int> 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<int> new_values = {
std::max(1, static_cast<int>(std::abs(temporary_values[0]))) * signs[0],
std::max(1, static_cast<int>(std::abs(temporary_values[1]))) * signs[1],
std::max(1, static_cast<int>(std::abs(temporary_values[2]))) * signs[2],
std::max(1, static_cast<int>(std::abs(temporary_values[3]))) * signs[3],
std::max(1, static_cast<int>(std::abs(temporary_values[4]))) * signs[4],
std::max(1, static_cast<int>(std::abs(temporary_values[5]))) * signs[5],
std::max(1, static_cast<int>(std::abs(temporary_values[6]))) * signs[6],
std::max(1, static_cast<int>(std::abs(temporary_values[7]))) * signs[7],
std::max(1, static_cast<int>(std::abs(temporary_values[8]))) * signs[8],
std::max(1, static_cast<int>(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<int32>(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");

View File

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

View File

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

View File

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

View File

@ -390,9 +390,9 @@ int Lua_Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 race, uint
return static_cast<int>(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) {

View File

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

View File

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

View File

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

View File

@ -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<struct NPCFaction*>::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<struct NPCFaction*>::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
}
}

View File

@ -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 <deque>
#include <list>
@ -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<struct NPCFaction*> faction_list;
uint32 copper;
uint32 silver;
uint32 gold;
@ -573,6 +573,8 @@ protected:
uint32 spawn_group_id;
uint16 wp_m;
std::list<NpcFactionEntriesRepository::NpcFactionEntries> faction_list;
int32 npc_faction_id;
int32 primary_faction;
int32 faction_amount;

View File

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

View File

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

View File

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

View File

@ -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!");

View File

@ -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<uint32>& npc_faction_ids);
void ClearNPCFactions();
void ReloadNPCFactions();
NpcFactionRepository::NpcFaction* GetNPCFaction(const uint32 npc_faction_id);
std::vector<NpcFactionEntriesRepository::NpcFactionEntries> GetNPCFactionEntries(const uint32 npc_faction_id) const;
void LoadNPCFactionAssociation(const uint32 npc_faction_id);
void LoadNPCFactionAssociations(const std::vector<uint32>& npc_faction_ids);
void LoadFactionAssociation(const uint32 faction_id);
void LoadFactionAssociations(const std::vector<uint32>& 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<NpcFactionRepository::NpcFaction> m_npc_factions = { };
std::vector<NpcFactionEntriesRepository::NpcFactionEntries> m_npc_faction_entries = { };
std::vector<FactionAssociationRepository::FactionAssociation> m_faction_associations = { };
};
#endif

277
zone/zone_npc_factions.cpp Normal file
View File

@ -0,0 +1,277 @@
#include <vector>
#include "zone.h"
#include "../common/repositories/npc_faction_repository.h"
#include "../common/repositories/npc_faction_entries_repository.h"
void Zone::LoadNPCFactions(const std::vector<uint32> &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<uint32> 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<uint32> 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<NpcFactionEntriesRepository::NpcFactionEntries> Zone::GetNPCFactionEntries(const uint32 npc_faction_id) const
{
std::vector<NpcFactionEntriesRepository::NpcFactionEntries> npc_faction_entries = { };
std::vector<uint32> 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<uint32>& 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<uint32> 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<uint32>& 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<uint32> 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;
}

View File

@ -1746,6 +1746,7 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
}
std::vector<uint32> npc_ids;
std::vector<uint32> 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; i<MAX_NPC_FACTIONS; i++) {
faction_id[i] = nfl->factionid[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<struct NPCFaction*> *faction_list, int32* primary_faction) {
if (nfl_id <= 0) {
std::list<struct NPCFaction*>::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<NpcFactionEntriesRepository::NpcFactionEntries> *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<struct NPCFaction*>::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; i<MAX_NPC_FACTIONS; i++) {
struct NPCFaction *pFac;
if (nfl->factionid[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;
}

View File

@ -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<struct NPCFaction*> *faction_list, int32* primary_faction = 0); // improve faction handling
bool GetFactionIDsForNPC(uint32 npc_faction_id, std::list<NpcFactionEntriesRepository::NpcFactionEntries>* 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; }