mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
* asdsa * Final push * Update character_corpses_repository.h * Update character_corpses_repository.h * Update zonedb.cpp * Update zonedb.cpp * Final push * Update character_corpses_repository.h
4429 lines
120 KiB
C++
Executable File
4429 lines
120 KiB
C++
Executable File
|
|
#include "../common/eqemu_logsys.h"
|
|
#include "../common/extprofile.h"
|
|
#include "../common/rulesys.h"
|
|
#include "../common/strings.h"
|
|
|
|
#include "client.h"
|
|
#include "corpse.h"
|
|
#include "groups.h"
|
|
#include "merc.h"
|
|
#include "zone.h"
|
|
#include "zonedb.h"
|
|
#include "aura.h"
|
|
#include "../common/repositories/blocked_spells_repository.h"
|
|
#include "../common/repositories/character_tribute_repository.h"
|
|
#include "../common/repositories/character_data_repository.h"
|
|
#include "../common/repositories/character_disciplines_repository.h"
|
|
#include "../common/repositories/npc_types_repository.h"
|
|
#include "../common/repositories/character_bind_repository.h"
|
|
#include "../common/repositories/character_pet_buffs_repository.h"
|
|
#include "../common/repositories/character_pet_inventory_repository.h"
|
|
#include "../common/repositories/character_pet_info_repository.h"
|
|
#include "../common/repositories/character_buffs_repository.h"
|
|
#include "../common/repositories/character_languages_repository.h"
|
|
#include "../common/repositories/criteria/content_filter_criteria.h"
|
|
#include "../common/repositories/spawn2_disabled_repository.h"
|
|
#include "../common/repositories/character_leadership_abilities_repository.h"
|
|
#include "../common/repositories/character_material_repository.h"
|
|
#include "../common/repositories/character_memmed_spells_repository.h"
|
|
#include "../common/repositories/character_spells_repository.h"
|
|
#include "../common/repositories/character_skills_repository.h"
|
|
#include "../common/repositories/character_potionbelt_repository.h"
|
|
#include "../common/repositories/character_bandolier_repository.h"
|
|
#include "../common/repositories/character_currency_repository.h"
|
|
#include "../common/repositories/character_alternate_abilities_repository.h"
|
|
#include "../common/repositories/character_auras_repository.h"
|
|
#include "../common/repositories/character_alt_currency_repository.h"
|
|
#include "../common/repositories/character_item_recast_repository.h"
|
|
#include "../common/repositories/account_repository.h"
|
|
#include "../common/repositories/respawn_times_repository.h"
|
|
#include "../common/repositories/object_contents_repository.h"
|
|
#include "../common/repositories/mercs_repository.h"
|
|
#include "../common/repositories/merc_buffs_repository.h"
|
|
#include "../common/repositories/merc_inventory_repository.h"
|
|
#include "../common/repositories/merc_subtypes_repository.h"
|
|
#include "../common/repositories/npc_types_tint_repository.h"
|
|
#include "../common/repositories/merchantlist_temp_repository.h"
|
|
#include "../common/repositories/character_data_repository.h"
|
|
#include "../common/repositories/character_corpses_repository.h"
|
|
#include "../common/repositories/character_corpse_items_repository.h"
|
|
|
|
#include <ctime>
|
|
#include <iostream>
|
|
#include <fmt/format.h>
|
|
|
|
extern Zone* zone;
|
|
|
|
ZoneDatabase database;
|
|
ZoneDatabase content_db;
|
|
|
|
ZoneDatabase::ZoneDatabase()
|
|
: SharedDatabase()
|
|
{
|
|
ZDBInitVars();
|
|
}
|
|
|
|
ZoneDatabase::ZoneDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port)
|
|
: SharedDatabase(host, user, passwd, database, port)
|
|
{
|
|
ZDBInitVars();
|
|
}
|
|
|
|
void ZoneDatabase::ZDBInitVars() {
|
|
npc_spellseffects_cache = 0;
|
|
npc_spellseffects_loadtried = 0;
|
|
max_faction = 0;
|
|
faction_array = nullptr;
|
|
}
|
|
|
|
ZoneDatabase::~ZoneDatabase() {
|
|
if (npc_spellseffects_cache) {
|
|
for (int x = 0; x <= npc_spellseffects_maxid; x++) {
|
|
safe_delete_array(npc_spellseffects_cache[x]);
|
|
}
|
|
safe_delete_array(npc_spellseffects_cache);
|
|
}
|
|
safe_delete_array(npc_spellseffects_loadtried);
|
|
|
|
if (faction_array != nullptr) {
|
|
for (int x = 0; x <= max_faction; x++) {
|
|
if (faction_array[x] != 0)
|
|
safe_delete(faction_array[x]);
|
|
}
|
|
safe_delete_array(faction_array);
|
|
}
|
|
}
|
|
|
|
bool ZoneDatabase::SaveZoneCFG(uint32 zone_id, uint16 instance_version, NewZone_Struct* zd)
|
|
{
|
|
const auto& l = ZoneRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`zoneidnumber` = {} AND `version` = {}",
|
|
zone_id,
|
|
instance_version
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
auto e = l.front();
|
|
|
|
e.underworld = zd->underworld;
|
|
e.minclip = zd->minclip;
|
|
e.maxclip = zd->maxclip;
|
|
e.fog_minclip = zd->fog_minclip[0];
|
|
e.fog_maxclip = zd->fog_maxclip[0];
|
|
e.fog_blue = zd->fog_blue[0];
|
|
e.fog_red = zd->fog_red[0];
|
|
e.fog_green = zd->fog_green[0];
|
|
e.sky = zd->sky;
|
|
e.ztype = zd->ztype;
|
|
e.zone_exp_multiplier = zd->zone_exp_multiplier;
|
|
e.safe_x = zd->safe_x;
|
|
e.safe_y = zd->safe_y;
|
|
e.safe_z = zd->safe_z;
|
|
e.safe_heading = zd->safe_heading;
|
|
|
|
return ZoneRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
void ZoneDatabase::UpdateRespawnTime(uint32 spawn2_id, uint16 instance_id, uint32 time_left)
|
|
{
|
|
timeval tv;
|
|
gettimeofday(&tv, nullptr);
|
|
uint32 current_time = tv.tv_sec;
|
|
|
|
/* If we pass timeleft as 0 that means we clear from respawn time
|
|
otherwise we update with a REPLACE INTO
|
|
*/
|
|
|
|
if (!time_left) {
|
|
RespawnTimesRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`id` = {} AND `instance_id` = {}",
|
|
spawn2_id,
|
|
instance_id
|
|
)
|
|
);
|
|
return;
|
|
}
|
|
|
|
RespawnTimesRepository::ReplaceOne(
|
|
*this,
|
|
RespawnTimesRepository::RespawnTimes{
|
|
.id = static_cast<int32_t>(spawn2_id),
|
|
.start = static_cast<int32_t>(current_time),
|
|
.duration = static_cast<int32_t>(time_left),
|
|
.instance_id = static_cast<int16_t>(instance_id)
|
|
}
|
|
);
|
|
}
|
|
|
|
//Gets the respawn time left in the database for the current spawn id
|
|
uint32 ZoneDatabase::GetSpawnTimeLeft(uint32 spawn2_id, uint16 instance_id)
|
|
{
|
|
timeval tv;
|
|
gettimeofday(&tv, nullptr);
|
|
|
|
return RespawnTimesRepository::GetTimeRemaining(*this, spawn2_id, instance_id, tv.tv_sec);
|
|
}
|
|
|
|
void ZoneDatabase::UpdateSpawn2Status(uint32 id, uint8 new_status, uint32 instance_id)
|
|
{
|
|
auto spawns = Spawn2DisabledRepository::GetWhere(
|
|
*this,
|
|
fmt::format("spawn2_id = {} and instance_id = {}", id, instance_id)
|
|
);
|
|
if (!spawns.empty()) {
|
|
auto spawn = spawns[0];
|
|
// 1 = enabled 0 = disabled
|
|
spawn.disabled = new_status ? 0 : 1;
|
|
spawn.instance_id = instance_id;
|
|
Spawn2DisabledRepository::UpdateOne(*this, spawn);
|
|
return;
|
|
}
|
|
|
|
auto spawn = Spawn2DisabledRepository::NewEntity();
|
|
spawn.spawn2_id = id;
|
|
spawn.instance_id = instance_id;
|
|
spawn.disabled = new_status ? 0 : 1;
|
|
Spawn2DisabledRepository::InsertOne(*this, spawn);
|
|
}
|
|
|
|
bool ZoneDatabase::SetSpecialAttkFlag(uint8 id, const char* flag) {
|
|
|
|
std::string query = StringFormat("UPDATE npc_types SET npcspecialattks='%s' WHERE id = %i;", flag, id);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
return false;
|
|
|
|
return results.RowsAffected() != 0;
|
|
}
|
|
|
|
void ZoneDatabase::LoadWorldContainer(uint32 parent_id, EQ::ItemInstance* container)
|
|
{
|
|
if (!container) {
|
|
return;
|
|
}
|
|
|
|
const auto& l = ObjectContentsRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`parentid` = {}",
|
|
parent_id
|
|
)
|
|
);
|
|
|
|
for (const auto& e : l) {
|
|
const uint32 augments[EQ::invaug::SOCKET_COUNT] = {
|
|
e.augslot1,
|
|
e.augslot2,
|
|
e.augslot3,
|
|
e.augslot4,
|
|
e.augslot5,
|
|
static_cast<uint32>(e.augslot6)
|
|
};
|
|
|
|
auto inst = database.CreateItem(e.itemid, e.charges);
|
|
|
|
if (inst && inst->GetItem()->IsClassCommon()) {
|
|
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
|
if (augments[i]) {
|
|
inst->PutAugment(&database, i, augments[i]);
|
|
}
|
|
}
|
|
|
|
container->PutItem(e.bagidx, *inst);
|
|
}
|
|
|
|
safe_delete(inst);
|
|
}
|
|
}
|
|
|
|
void ZoneDatabase::SaveWorldContainer(uint32 zone_id, uint32 parent_id, const EQ::ItemInstance* container)
|
|
{
|
|
if (!container) {
|
|
return;
|
|
}
|
|
|
|
DeleteWorldContainer(parent_id, zone_id);
|
|
|
|
for (uint8 index = EQ::invbag::SLOT_BEGIN; index <= EQ::invbag::SLOT_END; index++) {
|
|
auto inst = container->GetItem(index);
|
|
if (!inst) {
|
|
continue;
|
|
}
|
|
|
|
uint32 augments[EQ::invaug::SOCKET_COUNT] = { 0, 0, 0, 0, 0, 0 };
|
|
|
|
if (inst->IsType(EQ::item::ItemClassCommon)) {
|
|
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
|
auto augment = inst->GetAugment(i);
|
|
augments[i] = (augment && augment->GetItem()) ? augment->GetItem()->ID : 0;
|
|
}
|
|
}
|
|
|
|
ObjectContentsRepository::ReplaceOne(
|
|
*this,
|
|
ObjectContentsRepository::ObjectContents{
|
|
.zoneid = zone_id,
|
|
.parentid = parent_id,
|
|
.bagidx = index,
|
|
.itemid = inst->GetItem()->ID,
|
|
.charges = inst->GetCharges(),
|
|
.droptime = std::time(nullptr),
|
|
.augslot1 = augments[0],
|
|
.augslot2 = augments[1],
|
|
.augslot3 = augments[2],
|
|
.augslot4 = augments[3],
|
|
.augslot5 = augments[4],
|
|
.augslot6 = static_cast<int32_t>(augments[5])
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
void ZoneDatabase::DeleteWorldContainer(uint32 parent_id, uint32 zone_id)
|
|
{
|
|
ObjectContentsRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`parentid` = {} AND `zoneid` = {}",
|
|
parent_id,
|
|
zone_id
|
|
)
|
|
);
|
|
}
|
|
|
|
Trader_Struct* ZoneDatabase::LoadTraderItem(uint32 char_id)
|
|
{
|
|
auto loadti = new Trader_Struct;
|
|
memset(loadti,0,sizeof(Trader_Struct));
|
|
|
|
std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i ORDER BY slot_id LIMIT 80", char_id);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success()) {
|
|
LogTrading("Failed to load trader information!\n");
|
|
return loadti;
|
|
}
|
|
|
|
loadti->Code = BazaarTrader_ShowItems;
|
|
for (auto& row = results.begin(); row != results.end(); ++row) {
|
|
if (Strings::ToInt(row[5]) >= 80 || Strings::ToInt(row[4]) < 0) {
|
|
LogTrading("Bad Slot number when trying to load trader information!\n");
|
|
continue;
|
|
}
|
|
|
|
loadti->Items[Strings::ToInt(row[5])] = Strings::ToInt(row[1]);
|
|
loadti->ItemCost[Strings::ToInt(row[5])] = Strings::ToInt(row[4]);
|
|
}
|
|
return loadti;
|
|
}
|
|
|
|
TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id)
|
|
{
|
|
auto loadti = new TraderCharges_Struct;
|
|
memset(loadti,0,sizeof(TraderCharges_Struct));
|
|
|
|
std::string query = StringFormat("SELECT * FROM trader WHERE char_id=%i ORDER BY slot_id LIMIT 80", char_id);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success()) {
|
|
LogTrading("Failed to load trader information!\n");
|
|
return loadti;
|
|
}
|
|
|
|
for (auto& row = results.begin(); row != results.end(); ++row) {
|
|
if (Strings::ToInt(row[5]) >= 80 || Strings::ToInt(row[5]) < 0) {
|
|
LogTrading("Bad Slot number when trying to load trader information!\n");
|
|
continue;
|
|
}
|
|
|
|
loadti->ItemID[Strings::ToInt(row[5])] = Strings::ToInt(row[1]);
|
|
loadti->SerialNumber[Strings::ToInt(row[5])] = Strings::ToInt(row[2]);
|
|
loadti->Charges[Strings::ToInt(row[5])] = Strings::ToInt(row[3]);
|
|
loadti->ItemCost[Strings::ToInt(row[5])] = Strings::ToInt(row[4]);
|
|
}
|
|
return loadti;
|
|
}
|
|
|
|
EQ::ItemInstance* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) {
|
|
std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i AND serialnumber = %i "
|
|
"ORDER BY slot_id LIMIT 80", CharID, SerialNumber);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
return nullptr;
|
|
|
|
if (results.RowCount() == 0) {
|
|
LogTrading("Bad result from query\n"); fflush(stdout);
|
|
return nullptr;
|
|
}
|
|
|
|
auto& row = results.begin();
|
|
|
|
int ItemID = Strings::ToInt(row[1]);
|
|
int Charges = Strings::ToInt(row[3]);
|
|
int Cost = Strings::ToInt(row[4]);
|
|
|
|
const EQ::ItemData *item = database.GetItem(ItemID);
|
|
|
|
if(!item) {
|
|
LogTrading("Unable to create item\n");
|
|
fflush(stdout);
|
|
return nullptr;
|
|
}
|
|
|
|
if (item->NoDrop == 0)
|
|
return nullptr;
|
|
|
|
EQ::ItemInstance* inst = database.CreateItem(item);
|
|
if(!inst) {
|
|
LogTrading("Unable to create item instance\n");
|
|
fflush(stdout);
|
|
return nullptr;
|
|
}
|
|
|
|
inst->SetCharges(Charges);
|
|
inst->SetSerialNumber(SerialNumber);
|
|
inst->SetMerchantSlot(SerialNumber);
|
|
inst->SetPrice(Cost);
|
|
|
|
if(inst->IsStackable())
|
|
inst->SetMerchantCount(Charges);
|
|
|
|
return inst;
|
|
}
|
|
|
|
void ZoneDatabase::SaveTraderItem(uint32 CharID, uint32 ItemID, uint32 SerialNumber, int32 Charges, uint32 ItemCost, uint8 Slot){
|
|
|
|
std::string query = StringFormat("REPLACE INTO trader VALUES(%i, %i, %i, %i, %i, %i)",
|
|
CharID, ItemID, SerialNumber, Charges, ItemCost, Slot);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to save trader item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str());
|
|
|
|
}
|
|
|
|
void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) {
|
|
LogTrading("ZoneDatabase::UpdateTraderItemCharges([{}], [{}], [{}])", CharID, SerialNumber, Charges);
|
|
|
|
std::string query = StringFormat("UPDATE trader SET charges = %i WHERE char_id = %i AND serialnumber = %i",
|
|
Charges, CharID, SerialNumber);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to update charges for trader item: [{}] for char_id: [{}], the error was: [{}]\n", SerialNumber, CharID, results.ErrorMessage().c_str());
|
|
|
|
}
|
|
|
|
void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice) {
|
|
|
|
LogTrading("ZoneDatabase::UpdateTraderPrice([{}], [{}], [{}], [{}])", CharID, ItemID, Charges, NewPrice);
|
|
|
|
const EQ::ItemData *item = database.GetItem(ItemID);
|
|
|
|
if(!item)
|
|
return;
|
|
|
|
if(NewPrice == 0) {
|
|
LogTrading("Removing Trader items from the DB for CharID [{}], ItemID [{}]", CharID, ItemID);
|
|
|
|
std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i AND item_id = %i",CharID, ItemID);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to remove trader item(s): [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str());
|
|
|
|
return;
|
|
}
|
|
|
|
if(!item->Stackable) {
|
|
std::string query = StringFormat("UPDATE trader SET item_cost = %i "
|
|
"WHERE char_id = %i AND item_id = %i AND charges=%i",
|
|
NewPrice, CharID, ItemID, Charges);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to update price for trader item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str());
|
|
|
|
return;
|
|
}
|
|
|
|
std::string query = StringFormat("UPDATE trader SET item_cost = %i "
|
|
"WHERE char_id = %i AND item_id = %i",
|
|
NewPrice, CharID, ItemID);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to update price for trader item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str());
|
|
}
|
|
|
|
void ZoneDatabase::DeleteTraderItem(uint32 char_id){
|
|
|
|
if(char_id==0) {
|
|
const std::string query = "DELETE FROM trader";
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to delete all trader items data, the error was: [{}]\n", results.ErrorMessage().c_str());
|
|
|
|
return;
|
|
}
|
|
|
|
std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i", char_id);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to delete trader item data for char_id: [{}], the error was: [{}]\n", char_id, results.ErrorMessage().c_str());
|
|
|
|
}
|
|
void ZoneDatabase::DeleteTraderItem(uint32 CharID,uint16 SlotID) {
|
|
|
|
std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i And slot_id = %i", CharID, SlotID);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to delete trader item data for char_id: [{}], the error was: [{}]\n",CharID, results.ErrorMessage().c_str());
|
|
}
|
|
|
|
void ZoneDatabase::DeleteBuyLines(uint32 CharID) {
|
|
|
|
if(CharID==0) {
|
|
const std::string query = "DELETE FROM buyer";
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to delete all buyer items data, the error was: [{}]\n",results.ErrorMessage().c_str());
|
|
|
|
return;
|
|
}
|
|
|
|
std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i", CharID);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to delete buyer item data for charid: [{}], the error was: [{}]\n",CharID,results.ErrorMessage().c_str());
|
|
|
|
}
|
|
|
|
void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char* ItemName, uint32 Quantity, uint32 Price) {
|
|
std::string query = StringFormat("REPLACE INTO buyer VALUES(%i, %i, %i, \"%s\", %i, %i)",
|
|
CharID, BuySlot, ItemID, ItemName, Quantity, Price);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to save buline item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str());
|
|
|
|
}
|
|
|
|
void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) {
|
|
std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i AND buyslot = %i", CharID, BuySlot);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to delete buyslot [{}] for charid: [{}], the error was: [{}]\n", BuySlot, CharID, results.ErrorMessage().c_str());
|
|
|
|
}
|
|
|
|
void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) {
|
|
if(Quantity <= 0) {
|
|
RemoveBuyLine(CharID, BuySlot);
|
|
return;
|
|
}
|
|
|
|
std::string query = StringFormat("UPDATE buyer SET quantity = %i WHERE charid = %i AND buyslot = %i", Quantity, CharID, BuySlot);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success())
|
|
LogDebug("[CLIENT] Failed to update quantity in buyslot [{}] for charid: [{}], the error was: [{}]\n", BuySlot, CharID, results.ErrorMessage().c_str());
|
|
|
|
}
|
|
|
|
#define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1))
|
|
|
|
bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){
|
|
const auto& e = CharacterDataRepository::FindOne(database, character_id);
|
|
if (!e.id) {
|
|
return false;
|
|
}
|
|
|
|
strcpy(pp->name, e.name.c_str());
|
|
strcpy(pp->last_name, e.last_name.c_str());
|
|
strcpy(pp->title, e.title.c_str());
|
|
strcpy(pp->suffix, e.suffix.c_str());
|
|
|
|
pp->gender = e.gender;
|
|
pp->race = e.race;
|
|
pp->class_ = e.class_;
|
|
pp->level = e.level;
|
|
pp->deity = e.deity;
|
|
pp->birthday = e.birthday;
|
|
pp->lastlogin = e.last_login;
|
|
pp->timePlayedMin = e.time_played;
|
|
pp->pvp = e.pvp_status;
|
|
pp->level2 = e.level2;
|
|
pp->anon = e.anon;
|
|
pp->gm = e.gm;
|
|
pp->intoxication = e.intoxication;
|
|
pp->haircolor = e.hair_color;
|
|
pp->beardcolor = e.beard_color;
|
|
pp->eyecolor1 = e.eye_color_1;
|
|
pp->eyecolor2 = e.eye_color_2;
|
|
pp->hairstyle = e.hair_style;
|
|
pp->beard = e.beard;
|
|
pp->ability_time_seconds = e.ability_time_seconds;
|
|
pp->ability_number = e.ability_number;
|
|
pp->ability_time_minutes = e.ability_time_minutes;
|
|
pp->ability_time_hours = e.ability_time_hours;
|
|
pp->exp = e.exp;
|
|
pp->points = e.points;
|
|
pp->mana = e.mana;
|
|
pp->cur_hp = e.cur_hp;
|
|
pp->STR = e.str;
|
|
pp->STA = e.sta;
|
|
pp->CHA = e.cha;
|
|
pp->DEX = e.dex;
|
|
pp->INT = e.int_;
|
|
pp->AGI = e.agi;
|
|
pp->WIS = e.wis;
|
|
pp->face = e.face;
|
|
pp->y = e.y;
|
|
pp->x = e.x;
|
|
pp->z = e.z;
|
|
pp->heading = e.heading;
|
|
pp->pvp2 = e.pvp2;
|
|
pp->pvptype = e.pvp_type;
|
|
pp->autosplit = e.autosplit_enabled;
|
|
pp->zone_change_count = e.zone_change_count;
|
|
pp->drakkin_heritage = e.drakkin_heritage;
|
|
pp->drakkin_tattoo = e.drakkin_tattoo;
|
|
pp->drakkin_details = e.drakkin_details;
|
|
pp->toxicity = e.toxicity;
|
|
pp->hunger_level = e.hunger_level;
|
|
pp->thirst_level = e.thirst_level;
|
|
pp->ability_up = e.ability_up;
|
|
pp->zone_id = e.zone_id;
|
|
pp->zoneInstance = e.zone_instance;
|
|
pp->leadAAActive = e.leadership_exp_on;
|
|
pp->ldon_points_guk = e.ldon_points_guk;
|
|
pp->ldon_points_mir = e.ldon_points_mir;
|
|
pp->ldon_points_mmc = e.ldon_points_mmc;
|
|
pp->ldon_points_ruj = e.ldon_points_ruj;
|
|
pp->ldon_points_tak = e.ldon_points_tak;
|
|
pp->ldon_points_available = e.ldon_points_available;
|
|
pp->tribute_time_remaining = e.tribute_time_remaining;
|
|
pp->showhelm = e.show_helm;
|
|
pp->career_tribute_points = e.career_tribute_points;
|
|
pp->tribute_points = e.tribute_points;
|
|
pp->tribute_active = e.tribute_active;
|
|
pp->endurance = e.endurance;
|
|
pp->group_leadership_exp = e.group_leadership_exp;
|
|
pp->raid_leadership_exp = e.raid_leadership_exp;
|
|
pp->group_leadership_points = e.group_leadership_points;
|
|
pp->raid_leadership_points = e.raid_leadership_points;
|
|
pp->air_remaining = e.air_remaining;
|
|
pp->PVPKills = e.pvp_kills;
|
|
pp->PVPDeaths = e.pvp_deaths;
|
|
pp->PVPCurrentPoints = e.pvp_current_points;
|
|
pp->PVPCareerPoints = e.pvp_career_points;
|
|
pp->PVPBestKillStreak = e.pvp_best_kill_streak;
|
|
pp->PVPWorstDeathStreak = e.pvp_worst_death_streak;
|
|
pp->PVPCurrentKillStreak = e.pvp_current_kill_streak;
|
|
pp->aapoints_spent = e.aa_points_spent;
|
|
pp->expAA = e.aa_exp;
|
|
pp->aapoints = e.aa_points;
|
|
pp->groupAutoconsent = e.group_auto_consent;
|
|
pp->raidAutoconsent = e.raid_auto_consent;
|
|
pp->guildAutoconsent = e.guild_auto_consent;
|
|
pp->RestTimer = e.RestTimer;
|
|
m_epp->aa_effects = e.e_aa_effects;
|
|
m_epp->perAA = e.e_percent_to_aa;
|
|
m_epp->expended_aa = e.e_expended_aa_spent;
|
|
m_epp->last_invsnapshot_time = e.e_last_invsnapshot;
|
|
m_epp->next_invsnapshot_time = m_epp->last_invsnapshot_time + (RuleI(Character, InvSnapshotMinIntervalM) * 60);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterFactionValues(uint32 character_id, faction_map & val_list) {
|
|
std::string query = StringFormat("SELECT `faction_id`, `current_value` FROM `faction_values` WHERE `char_id` = %i", character_id);
|
|
auto results = database.QueryDatabase(query);
|
|
for (auto& row = results.begin(); row != results.end(); ++row) { val_list[Strings::ToInt(row[0])] = Strings::ToInt(row[1]); }
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp)
|
|
{
|
|
const auto& l = CharacterMemmedSpellsRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`id` = {} ORDER BY `slot_id`",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
for (int i = 0; i < EQ::spells::SPELL_GEM_COUNT; i++) { // Initialize Spells
|
|
pp->mem_spells[i] = UINT32_MAX;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
if (e.slot_id < EQ::spells::SPELL_GEM_COUNT && IsValidSpell(e.spell_id)) {
|
|
pp->mem_spells[e.slot_id] = e.spell_id;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp)
|
|
{
|
|
const auto& l = CharacterSpellsRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`id` = {} ORDER BY `slot_id`",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
memset(pp->spell_book, UINT8_MAX, (sizeof(uint32) * EQ::spells::SPELLBOOK_SIZE));
|
|
|
|
// We have the ability to block loaded spells by max id on a per-client basis..
|
|
// but, we do not have to ability to keep players from using older clients after
|
|
// they have scribed spells on a newer one that exceeds the older one's limit.
|
|
// Load them all so that server actions are valid..but, nix them in translators.
|
|
|
|
for (const auto& e : l) {
|
|
if (!EQ::ValueWithin(e.slot_id, 0, EQ::spells::SPELLBOOK_SIZE)) {
|
|
continue;
|
|
}
|
|
|
|
if (!IsValidSpell(e.spell_id)) {
|
|
continue;
|
|
}
|
|
|
|
pp->spell_book[e.slot_id] = e.spell_id;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp)
|
|
{
|
|
const auto& l = CharacterLanguagesRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`id` = {} ORDER BY `lang_id`",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < MAX_PP_LANGUAGE; ++i) { // Initialize Languages
|
|
pp->languages[i] = 0;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
if (EQ::ValueWithin(e.lang_id, Language::CommonTongue, Language::Unknown27)) {
|
|
pp->languages[e.lang_id] = e.value;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterLeadershipAbilities(uint32 character_id, PlayerProfile_Struct* pp)
|
|
{
|
|
const auto& l = CharacterLeadershipAbilitiesRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`id` = {}",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
for (const auto& e : l) {
|
|
pp->leader_abilities.ranks[e.slot] = e.rank;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp){
|
|
|
|
const auto& l = CharacterDisciplinesRepository::GetWhere(
|
|
database, fmt::format(
|
|
"`id` = {} ORDER BY `slot_id`",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
for (int slot_id = 0; slot_id < MAX_PP_DISCIPLINES; slot_id++) { // Initialize Disciplines
|
|
pp->disciplines.values[slot_id] = 0;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
if (IsValidSpell(e.disc_id) && e.slot_id < MAX_PP_DISCIPLINES) {
|
|
pp->disciplines.values[e.slot_id] = e.disc_id;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp)
|
|
{
|
|
const auto& l = CharacterSkillsRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`id` = {} ORDER BY `skill_id`",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
for (int i = 0; i < MAX_PP_SKILL; ++i) { // Initialize Skills
|
|
pp->skills[i] = 0;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
if (e.skill_id < MAX_PP_SKILL) {
|
|
pp->skills[e.skill_id] = e.value;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp)
|
|
{
|
|
const auto& e = CharacterCurrencyRepository::FindOne(*this, character_id);
|
|
if (!e.id) {
|
|
return false;
|
|
}
|
|
|
|
pp->platinum = e.platinum;
|
|
pp->platinum_bank = e.platinum_bank;
|
|
pp->platinum_cursor = e.platinum_cursor;
|
|
pp->gold = e.gold;
|
|
pp->gold_bank = e.gold_bank;
|
|
pp->gold_cursor = e.gold_cursor;
|
|
pp->silver = e.silver;
|
|
pp->silver_bank = e.silver_bank;
|
|
pp->silver_cursor = e.silver_cursor;
|
|
pp->copper = e.copper;
|
|
pp->copper_bank = e.copper_bank;
|
|
pp->copper_cursor = e.copper_cursor;
|
|
pp->currentRadCrystals = e.radiant_crystals;
|
|
pp->careerRadCrystals = e.career_radiant_crystals;
|
|
pp->currentEbonCrystals = e.ebon_crystals;
|
|
pp->careerEbonCrystals = e.career_ebon_crystals;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp)
|
|
{
|
|
const auto& l = CharacterMaterialRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`id` = {} LIMIT 9",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
for (const auto& e : l) {
|
|
pp->item_tint.Slot[e.slot].Blue = e.blue;
|
|
pp->item_tint.Slot[e.slot].Green = e.green;
|
|
pp->item_tint.Slot[e.slot].Red = e.red;
|
|
pp->item_tint.Slot[e.slot].UseTint = e.use_tint;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp)
|
|
{
|
|
const auto& l = CharacterBandolierRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`id` = {} LIMIT {}",
|
|
character_id,
|
|
EQ::profile::BANDOLIERS_SIZE
|
|
)
|
|
);
|
|
|
|
for (int i = 0; i < EQ::profile::BANDOLIERS_SIZE; i++) {
|
|
pp->bandoliers[i].Name[0] = '\0';
|
|
|
|
for (int si = 0; si < EQ::profile::BANDOLIER_ITEM_COUNT; si++) {
|
|
pp->bandoliers[i].Items[si].ID = 0;
|
|
pp->bandoliers[i].Items[si].Icon = 0;
|
|
|
|
pp->bandoliers[i].Items[si].Name[0] = '\0';
|
|
}
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
const auto* item_data = database.GetItem(e.item_id);
|
|
if (item_data) {
|
|
pp->bandoliers[e.bandolier_id].Items[e.bandolier_slot].ID = item_data->ID;
|
|
pp->bandoliers[e.bandolier_id].Items[e.bandolier_slot].Icon = e.icon;
|
|
|
|
strncpy(
|
|
pp->bandoliers[e.bandolier_id].Items[e.bandolier_slot].Name,
|
|
item_data->Name,
|
|
64
|
|
);
|
|
} else {
|
|
pp->bandoliers[e.bandolier_id].Items[e.bandolier_slot].ID = 0;
|
|
pp->bandoliers[e.bandolier_id].Items[e.bandolier_slot].Icon = 0;
|
|
|
|
pp->bandoliers[e.bandolier_id].Items[e.bandolier_slot].Name[0] = '\0';
|
|
}
|
|
|
|
strncpy(pp->bandoliers[e.bandolier_id].Name, e.bandolier_name.c_str(), 32);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ZoneDatabase::LoadCharacterTribute(Client* c){
|
|
const auto& l = CharacterTributeRepository::GetWhere(database, fmt::format("character_id = {}", c->CharacterID()));
|
|
|
|
for (auto& t : c->GetPP().tributes) {
|
|
t.tier = 0;
|
|
t.tribute = TRIBUTE_NONE;
|
|
}
|
|
|
|
auto i = 0;
|
|
|
|
for (const auto& e : l) {
|
|
if (e.tribute != TRIBUTE_NONE) {
|
|
c->GetPP().tributes[i].tier = e.tier;
|
|
c->GetPP().tributes[i].tribute = e.tribute;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterPotionBelt(uint32 character_id, PlayerProfile_Struct *pp)
|
|
{
|
|
const auto& l = CharacterPotionbeltRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`id` = {} LIMIT {}",
|
|
character_id,
|
|
EQ::profile::POTION_BELT_SIZE
|
|
)
|
|
);
|
|
|
|
for (int i = 0; i < EQ::profile::POTION_BELT_SIZE; i++) { // Initialize Potion Belt
|
|
pp->potionbelt.Items[i].Icon = 0;
|
|
pp->potionbelt.Items[i].ID = 0;
|
|
pp->potionbelt.Items[i].Name[0] = '\0';
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
const auto* item_data = database.GetItem(e.item_id);
|
|
if (!item_data) {
|
|
continue;
|
|
}
|
|
|
|
pp->potionbelt.Items[e.potion_id].ID = item_data->ID;
|
|
pp->potionbelt.Items[e.potion_id].Icon = e.icon;
|
|
|
|
strncpy(pp->potionbelt.Items[e.potion_id].Name, item_data->Name, 64);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct *pp)
|
|
{
|
|
const auto& l = CharacterBindRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`id` = {} LIMIT 5",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return true;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
if (!EQ::ValueWithin(e.slot, 0, 4)) {
|
|
continue;
|
|
}
|
|
|
|
pp->binds[e.slot].zone_id = e.zone_id;
|
|
pp->binds[e.slot].instance_id = e.instance_id;
|
|
pp->binds[e.slot].x = e.x;
|
|
pp->binds[e.slot].y = e.y;
|
|
pp->binds[e.slot].z = e.z;
|
|
pp->binds[e.slot].heading = e.heading;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterLanguage(uint32 character_id, uint32 language_id, uint32 value)
|
|
{
|
|
return CharacterLanguagesRepository::ReplaceOne(
|
|
*this,
|
|
CharacterLanguagesRepository::CharacterLanguages{
|
|
.id = character_id,
|
|
.lang_id = static_cast<uint16_t>(language_id),
|
|
.value = static_cast<uint16_t>(value)
|
|
}
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint8 slot_id, uint32 color)
|
|
{
|
|
const uint8 red = (color & 0x00FF0000) >> 16;
|
|
const uint8 green = (color & 0x0000FF00) >> 8;
|
|
const uint8 blue = (color & 0x000000FF);
|
|
|
|
return CharacterMaterialRepository::ReplaceOne(
|
|
*this,
|
|
CharacterMaterialRepository::CharacterMaterial{
|
|
.id = character_id,
|
|
.slot = slot_id,
|
|
.blue = blue,
|
|
.green = green,
|
|
.red = red,
|
|
.use_tint = UINT8_MAX,
|
|
.color = color
|
|
}
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value)
|
|
{
|
|
return CharacterSkillsRepository::ReplaceOne(
|
|
*this,
|
|
CharacterSkillsRepository::CharacterSkills{
|
|
.id = character_id,
|
|
.skill_id = static_cast<uint16_t>(skill_id),
|
|
.value = static_cast<uint16_t>(value)
|
|
}
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterDiscipline(uint32 character_id, uint32 slot_id, uint32 disc_id)
|
|
{
|
|
return CharacterDisciplinesRepository::ReplaceOne(
|
|
*this,
|
|
CharacterDisciplinesRepository::CharacterDisciplines{
|
|
.id = character_id,
|
|
.slot_id = static_cast<uint16_t>(slot_id),
|
|
.disc_id = static_cast<uint16_t>(disc_id)
|
|
}
|
|
);
|
|
}
|
|
|
|
void ZoneDatabase::SaveCharacterTribute(Client* c)
|
|
{
|
|
std::vector<CharacterTributeRepository::CharacterTribute> tributes = {};
|
|
CharacterTributeRepository::CharacterTribute tribute = {};
|
|
|
|
uint32 tribute_count = 0;
|
|
for (auto& t : c->GetPP().tributes) {
|
|
if (t.tribute != TRIBUTE_NONE) {
|
|
tribute_count++;
|
|
}
|
|
}
|
|
|
|
tributes.reserve(tribute_count);
|
|
|
|
for (auto& t : c->GetPP().tributes) {
|
|
if (t.tribute != TRIBUTE_NONE) {
|
|
tribute.character_id = c->CharacterID();
|
|
tribute.tier = t.tier;
|
|
tribute.tribute = t.tribute;
|
|
|
|
tributes.emplace_back(tribute);
|
|
}
|
|
}
|
|
|
|
CharacterTributeRepository::DeleteWhere(database, fmt::format("character_id = {}", c->CharacterID()));
|
|
|
|
if (tribute_count > 0) {
|
|
CharacterTributeRepository::InsertMany(database, tributes);
|
|
}
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterBandolier(
|
|
uint32 character_id,
|
|
uint8 bandolier_id,
|
|
uint8 bandolier_slot,
|
|
uint32 item_id,
|
|
uint32 icon,
|
|
const char* bandolier_name
|
|
)
|
|
{
|
|
return CharacterBandolierRepository::ReplaceOne(
|
|
*this,
|
|
CharacterBandolierRepository::CharacterBandolier{
|
|
.id = character_id,
|
|
.bandolier_id = bandolier_id,
|
|
.bandolier_slot = bandolier_slot,
|
|
.item_id = item_id,
|
|
.icon = icon,
|
|
.bandolier_name = bandolier_name
|
|
}
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon)
|
|
{
|
|
return CharacterPotionbeltRepository::ReplaceOne(
|
|
*this,
|
|
CharacterPotionbeltRepository::CharacterPotionbelt{
|
|
.id = character_id,
|
|
.potion_id = potion_id,
|
|
.item_id = item_id,
|
|
.icon = icon
|
|
}
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterLeadershipAbilities(uint32 character_id, PlayerProfile_Struct* pp)
|
|
{
|
|
std::vector<CharacterLeadershipAbilitiesRepository::CharacterLeadershipAbilities> v;
|
|
|
|
auto e = CharacterLeadershipAbilitiesRepository::NewEntity();
|
|
|
|
for (int slot_id = 0; slot_id < MAX_LEADERSHIP_AA_ARRAY; slot_id++) {
|
|
if (pp->leader_abilities.ranks[slot_id] > 0) {
|
|
e.id = character_id;
|
|
e.slot = slot_id;
|
|
e.rank = pp->leader_abilities.ranks[slot_id];
|
|
|
|
v.emplace_back(e);
|
|
}
|
|
}
|
|
|
|
return CharacterLeadershipAbilitiesRepository::ReplaceMany(*this, v);
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterData(
|
|
Client* c,
|
|
PlayerProfile_Struct* pp,
|
|
ExtendedProfile_Struct* m_epp
|
|
) {
|
|
if (!c) {
|
|
return false;
|
|
}
|
|
|
|
/* If this is ever zero - the client hasn't fully loaded and potentially crashed during zone */
|
|
if (c->AccountID() <= 0) {
|
|
return false;
|
|
}
|
|
|
|
clock_t t = std::clock(); /* Function timer start */
|
|
|
|
auto e = CharacterDataRepository::FindOne(database, c->CharacterID());
|
|
if (!e.id) {
|
|
return false;
|
|
}
|
|
|
|
e.id = c->CharacterID();
|
|
e.account_id = c->AccountID();
|
|
e.name = pp->name;
|
|
e.last_name = pp->last_name;
|
|
e.gender = pp->gender;
|
|
e.race = pp->race;
|
|
e.class_ = pp->class_;
|
|
e.level = pp->level;
|
|
e.deity = pp->deity;
|
|
e.birthday = pp->birthday;
|
|
e.last_login = pp->lastlogin;
|
|
e.time_played = pp->timePlayedMin;
|
|
e.pvp_status = pp->pvp;
|
|
e.level2 = pp->level2;
|
|
e.anon = pp->anon;
|
|
e.gm = pp->gm;
|
|
e.intoxication = pp->intoxication;
|
|
e.hair_color = pp->haircolor;
|
|
e.beard_color = pp->beardcolor;
|
|
e.eye_color_1 = pp->eyecolor1;
|
|
e.eye_color_2 = pp->eyecolor2;
|
|
e.hair_style = pp->hairstyle;
|
|
e.beard = pp->beard;
|
|
e.ability_time_seconds = pp->ability_time_seconds;
|
|
e.ability_number = pp->ability_number;
|
|
e.ability_time_minutes = pp->ability_time_minutes;
|
|
e.ability_time_hours = pp->ability_time_hours;
|
|
e.title = pp->title;
|
|
e.suffix = pp->suffix;
|
|
e.exp = pp->exp;
|
|
e.exp_enabled = c->IsEXPEnabled();
|
|
e.points = pp->points;
|
|
e.mana = pp->mana;
|
|
e.cur_hp = pp->cur_hp;
|
|
e.str = pp->STR;
|
|
e.sta = pp->STA;
|
|
e.cha = pp->CHA;
|
|
e.dex = pp->DEX;
|
|
e.int_ = pp->INT;
|
|
e.agi = pp->AGI;
|
|
e.wis = pp->WIS;
|
|
e.face = pp->face;
|
|
e.y = pp->y;
|
|
e.x = pp->x;
|
|
e.z = pp->z;
|
|
e.heading = pp->heading;
|
|
e.pvp2 = pp->pvp2;
|
|
e.pvp_type = pp->pvptype;
|
|
e.autosplit_enabled = pp->autosplit;
|
|
e.zone_change_count = pp->zone_change_count;
|
|
e.drakkin_heritage = pp->drakkin_heritage;
|
|
e.drakkin_tattoo = pp->drakkin_tattoo;
|
|
e.drakkin_details = pp->drakkin_details;
|
|
e.toxicity = pp->toxicity;
|
|
e.hunger_level = pp->hunger_level;
|
|
e.thirst_level = pp->thirst_level;
|
|
e.ability_up = pp->ability_up;
|
|
e.zone_id = pp->zone_id;
|
|
e.zone_instance = pp->zoneInstance;
|
|
e.leadership_exp_on = pp->leadAAActive;
|
|
e.ldon_points_guk = pp->ldon_points_guk;
|
|
e.ldon_points_mir = pp->ldon_points_mir;
|
|
e.ldon_points_mmc = pp->ldon_points_mmc;
|
|
e.ldon_points_ruj = pp->ldon_points_ruj;
|
|
e.ldon_points_tak = pp->ldon_points_tak;
|
|
e.ldon_points_available = pp->ldon_points_available;
|
|
e.tribute_time_remaining = pp->tribute_time_remaining;
|
|
e.show_helm = pp->showhelm;
|
|
e.career_tribute_points = pp->career_tribute_points;
|
|
e.tribute_points = pp->tribute_points;
|
|
e.tribute_active = pp->tribute_active;
|
|
e.endurance = pp->endurance;
|
|
e.group_leadership_exp = pp->group_leadership_exp;
|
|
e.raid_leadership_exp = pp->raid_leadership_exp;
|
|
e.group_leadership_points = pp->group_leadership_points;
|
|
e.raid_leadership_points = pp->raid_leadership_points;
|
|
e.air_remaining = pp->air_remaining;
|
|
e.pvp_kills = pp->PVPKills;
|
|
e.pvp_deaths = pp->PVPDeaths;
|
|
e.pvp_current_points = pp->PVPCurrentPoints;
|
|
e.pvp_career_points = pp->PVPCareerPoints;
|
|
e.pvp_best_kill_streak = pp->PVPBestKillStreak;
|
|
e.pvp_worst_death_streak = pp->PVPWorstDeathStreak;
|
|
e.pvp_current_kill_streak = pp->PVPCurrentKillStreak;
|
|
e.aa_points_spent = pp->aapoints_spent;
|
|
e.aa_exp = pp->expAA;
|
|
e.aa_points = pp->aapoints;
|
|
e.group_auto_consent = pp->groupAutoconsent;
|
|
e.raid_auto_consent = pp->raidAutoconsent;
|
|
e.guild_auto_consent = pp->guildAutoconsent;
|
|
e.RestTimer = pp->RestTimer;
|
|
e.e_aa_effects = m_epp->aa_effects;
|
|
e.e_percent_to_aa = m_epp->perAA;
|
|
e.e_expended_aa_spent = m_epp->expended_aa;
|
|
e.e_last_invsnapshot = m_epp->last_invsnapshot_time;
|
|
e.mailkey = c->GetMailKeyFull();
|
|
|
|
const int replaced = CharacterDataRepository::ReplaceOne(database, e);
|
|
|
|
if (!replaced) {
|
|
LogError("Failed to save character data for [{}] ID [{}].", c->GetCleanName(), c->CharacterID());
|
|
return false;
|
|
}
|
|
|
|
LogDebug(
|
|
"ZoneDatabase::SaveCharacterData [{}], done Took [{}] seconds",
|
|
c->CharacterID(),
|
|
((float)(std::clock() - t)) / CLOCKS_PER_SEC
|
|
);
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp)
|
|
{
|
|
ZeroPlayerProfileCurrency(pp);
|
|
|
|
auto e = CharacterCurrencyRepository::NewEntity();
|
|
|
|
return CharacterCurrencyRepository::ReplaceOne(
|
|
*this,
|
|
CharacterCurrencyRepository::CharacterCurrency{
|
|
.id = character_id,
|
|
.platinum = static_cast<uint32_t>(pp->platinum),
|
|
.gold = static_cast<uint32_t>(pp->gold),
|
|
.silver = static_cast<uint32_t>(pp->silver),
|
|
.copper = static_cast<uint32_t>(pp->copper),
|
|
.platinum_bank = static_cast<uint32_t>(pp->platinum_bank),
|
|
.gold_bank = static_cast<uint32_t>(pp->gold_bank),
|
|
.silver_bank = static_cast<uint32_t>(pp->silver_bank),
|
|
.copper_bank = static_cast<uint32_t>(pp->copper_bank),
|
|
.platinum_cursor = static_cast<uint32_t>(pp->platinum_cursor),
|
|
.gold_cursor = static_cast<uint32_t>(pp->gold_cursor),
|
|
.silver_cursor = static_cast<uint32_t>(pp->silver_cursor),
|
|
.copper_cursor = static_cast<uint32_t>(pp->copper_cursor),
|
|
.radiant_crystals = pp->currentRadCrystals,
|
|
.career_radiant_crystals = pp->careerRadCrystals,
|
|
.ebon_crystals = pp->currentEbonCrystals,
|
|
.career_ebon_crystals = pp->careerEbonCrystals
|
|
}
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){
|
|
if (!IsValidSpell(spell_id)) {
|
|
return false;
|
|
}
|
|
|
|
return CharacterMemmedSpellsRepository::ReplaceOne(
|
|
*this,
|
|
CharacterMemmedSpellsRepository::CharacterMemmedSpells{
|
|
.id = character_id,
|
|
.slot_id = static_cast<uint16_t>(slot_id),
|
|
.spell_id = static_cast<uint16_t>(spell_id)
|
|
}
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id)
|
|
{
|
|
if (!IsValidSpell(spell_id)) {
|
|
return false;
|
|
}
|
|
|
|
return CharacterSpellsRepository::ReplaceOne(
|
|
*this,
|
|
CharacterSpellsRepository::CharacterSpells{
|
|
.id = character_id,
|
|
.slot_id = static_cast<uint16_t>(slot_id),
|
|
.spell_id = static_cast<uint16_t>(spell_id)
|
|
}
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::DeleteCharacterSpell(uint32 character_id, uint32 slot_id)
|
|
{
|
|
return CharacterSpellsRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`id` = {} AND `slot_id` = {}",
|
|
character_id,
|
|
slot_id
|
|
)
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::DeleteCharacterDiscipline(uint32 character_id, uint32 slot_id)
|
|
{
|
|
return CharacterDisciplinesRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`id` = {} AND `slot_id` = {}",
|
|
character_id,
|
|
slot_id
|
|
)
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::DeleteCharacterBandolier(uint32 character_id, uint32 bandolier_id)
|
|
{
|
|
return CharacterBandolierRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`id` = {} AND `bandolier_id` = {}",
|
|
character_id,
|
|
bandolier_id
|
|
)
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::DeleteCharacterLeadershipAbilities(uint32 character_id)
|
|
{
|
|
return CharacterLeadershipAbilitiesRepository::DeleteOne(*this, character_id);
|
|
}
|
|
|
|
bool ZoneDatabase::DeleteCharacterAAs(uint32 character_id)
|
|
{
|
|
return CharacterAlternateAbilitiesRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`id` = {} AND `aa_id` NOT IN (SELECT a.first_rank_id FROM aa_ability a WHERE a.grant_only != 0)",
|
|
character_id
|
|
)
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::DeleteCharacterMaterialColor(uint32 character_id)
|
|
{
|
|
return CharacterMaterialRepository::DeleteOne(*this, character_id);
|
|
}
|
|
|
|
bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 slot_id)
|
|
{
|
|
return CharacterMemmedSpellsRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`id` = {} AND `slot_id` = {}",
|
|
character_id,
|
|
slot_id
|
|
)
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::NoRentExpired(const std::string& name)
|
|
{
|
|
const uint32 seconds = CharacterDataRepository::GetSecondsSinceLastLogin(*this, name);
|
|
|
|
return seconds > 1800;
|
|
}
|
|
|
|
bool ZoneDatabase::SaveCharacterInvSnapshot(uint32 character_id) {
|
|
uint32 time_index = time(nullptr);
|
|
std::string query = StringFormat(
|
|
"INSERT "
|
|
"INTO"
|
|
" `inventory_snapshots` "
|
|
"(`time_index`,"
|
|
" `charid`,"
|
|
" `slotid`,"
|
|
" `itemid`,"
|
|
" `charges`,"
|
|
" `color`,"
|
|
" `augslot1`,"
|
|
" `augslot2`,"
|
|
" `augslot3`,"
|
|
" `augslot4`,"
|
|
" `augslot5`,"
|
|
" `augslot6`,"
|
|
" `instnodrop`,"
|
|
" `custom_data`,"
|
|
" `ornamenticon`,"
|
|
" `ornamentidfile`,"
|
|
" `ornament_hero_model`"
|
|
") "
|
|
"SELECT"
|
|
" %u,"
|
|
" `charid`,"
|
|
" `slotid`,"
|
|
" `itemid`,"
|
|
" `charges`,"
|
|
" `color`,"
|
|
" `augslot1`,"
|
|
" `augslot2`,"
|
|
" `augslot3`,"
|
|
" `augslot4`,"
|
|
" `augslot5`,"
|
|
" `augslot6`,"
|
|
" `instnodrop`,"
|
|
" `custom_data`,"
|
|
" `ornamenticon`,"
|
|
" `ornamentidfile`,"
|
|
" `ornament_hero_model` "
|
|
"FROM"
|
|
" `inventory` "
|
|
"WHERE"
|
|
" `charid` = %u",
|
|
time_index,
|
|
character_id
|
|
);
|
|
auto results = database.QueryDatabase(query);
|
|
LogInventory("[{}] ([{}])", character_id, (results.Success() ? "pass" : "fail"));
|
|
return results.Success();
|
|
}
|
|
|
|
int ZoneDatabase::CountCharacterInvSnapshots(uint32 character_id) {
|
|
std::string query = StringFormat(
|
|
"SELECT"
|
|
" COUNT(*) "
|
|
"FROM "
|
|
"("
|
|
"SELECT * FROM"
|
|
" `inventory_snapshots` a "
|
|
"WHERE"
|
|
" `charid` = %u "
|
|
"GROUP BY"
|
|
" `time_index`"
|
|
") b",
|
|
character_id
|
|
);
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success())
|
|
return -1;
|
|
|
|
auto& row = results.begin();
|
|
|
|
int64 count = Strings::ToBigInt(row[0]);
|
|
if (count > 2147483647)
|
|
return -2;
|
|
if (count < 0)
|
|
return -3;
|
|
|
|
return count;
|
|
}
|
|
|
|
void ZoneDatabase::ClearCharacterInvSnapshots(uint32 character_id, bool from_now) {
|
|
uint32 del_time = time(nullptr);
|
|
if (!from_now) { del_time -= RuleI(Character, InvSnapshotHistoryD) * 86400; }
|
|
|
|
std::string query = StringFormat(
|
|
"DELETE "
|
|
"FROM"
|
|
" `inventory_snapshots` "
|
|
"WHERE"
|
|
" `charid` = %u "
|
|
"AND"
|
|
" `time_index` <= %lu",
|
|
character_id,
|
|
(unsigned long)del_time
|
|
);
|
|
QueryDatabase(query);
|
|
}
|
|
|
|
void ZoneDatabase::ListCharacterInvSnapshots(uint32 character_id, std::list<std::pair<uint32, int>> &is_list) {
|
|
std::string query = StringFormat(
|
|
"SELECT"
|
|
" `time_index`,"
|
|
" COUNT(*) "
|
|
"FROM"
|
|
" `inventory_snapshots` "
|
|
"WHERE"
|
|
" `charid` = %u "
|
|
"GROUP BY"
|
|
" `time_index` "
|
|
"ORDER BY"
|
|
" `time_index` "
|
|
"DESC",
|
|
character_id
|
|
);
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success())
|
|
return;
|
|
|
|
for (auto row : results)
|
|
is_list.emplace_back(std::pair<uint32, int>(Strings::ToUnsignedInt(row[0]), Strings::ToInt(row[1])));
|
|
}
|
|
|
|
bool ZoneDatabase::ValidateCharacterInvSnapshotTimestamp(uint32 character_id, uint32 timestamp) {
|
|
if (!character_id || !timestamp)
|
|
return false;
|
|
|
|
std::string query = StringFormat(
|
|
"SELECT"
|
|
" * "
|
|
"FROM"
|
|
" `inventory_snapshots` "
|
|
"WHERE"
|
|
" `charid` = %u "
|
|
"AND"
|
|
" `time_index` = %u "
|
|
"LIMIT 1",
|
|
character_id,
|
|
timestamp
|
|
);
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success() || results.RowCount() == 0)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ZoneDatabase::ParseCharacterInvSnapshot(uint32 character_id, uint32 timestamp, std::list<std::pair<int16, uint32>> &parse_list) {
|
|
std::string query = StringFormat(
|
|
"SELECT"
|
|
" `slotid`,"
|
|
" `itemid` "
|
|
"FROM"
|
|
" `inventory_snapshots` "
|
|
"WHERE"
|
|
" `charid` = %u "
|
|
"AND"
|
|
" `time_index` = %u "
|
|
"ORDER BY"
|
|
" `slotid`",
|
|
character_id,
|
|
timestamp
|
|
);
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success())
|
|
return;
|
|
|
|
for (auto row : results)
|
|
parse_list.emplace_back(std::pair<int16, uint32>(Strings::ToInt(row[0]), Strings::ToUnsignedInt(row[1])));
|
|
}
|
|
|
|
void ZoneDatabase::DivergeCharacterInvSnapshotFromInventory(uint32 character_id, uint32 timestamp, std::list<std::pair<int16, uint32>> &compare_list) {
|
|
std::string query = StringFormat(
|
|
"SELECT"
|
|
" slotid,"
|
|
" itemid "
|
|
"FROM"
|
|
" `inventory_snapshots` "
|
|
"WHERE"
|
|
" `time_index` = %u "
|
|
"AND"
|
|
" `charid` = %u "
|
|
"AND"
|
|
" `slotid` NOT IN "
|
|
"("
|
|
"SELECT"
|
|
" a.`slotid` "
|
|
"FROM"
|
|
" `inventory_snapshots` a "
|
|
"JOIN"
|
|
" `inventory` b "
|
|
"USING"
|
|
" (`slotid`, `itemid`) "
|
|
"WHERE"
|
|
" a.`time_index` = %u "
|
|
"AND"
|
|
" a.`charid` = %u "
|
|
"AND"
|
|
" b.`charid` = %u"
|
|
")",
|
|
timestamp,
|
|
character_id,
|
|
timestamp,
|
|
character_id,
|
|
character_id
|
|
);
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success())
|
|
return;
|
|
|
|
for (auto row : results)
|
|
compare_list.emplace_back(std::pair<int16, uint32>(Strings::ToInt(row[0]), Strings::ToUnsignedInt(row[1])));
|
|
}
|
|
|
|
void ZoneDatabase::DivergeCharacterInventoryFromInvSnapshot(uint32 character_id, uint32 timestamp, std::list<std::pair<int16, uint32>> &compare_list) {
|
|
std::string query = StringFormat(
|
|
"SELECT"
|
|
" `slotid`,"
|
|
" `itemid` "
|
|
"FROM"
|
|
" `inventory` "
|
|
"WHERE"
|
|
" `charid` = %u "
|
|
"AND"
|
|
" `slotid` NOT IN "
|
|
"("
|
|
"SELECT"
|
|
" a.`slotid` "
|
|
"FROM"
|
|
" `inventory` a "
|
|
"JOIN"
|
|
" `inventory_snapshots` b "
|
|
"USING"
|
|
" (`slotid`, `itemid`) "
|
|
"WHERE"
|
|
" b.`time_index` = %u "
|
|
"AND"
|
|
" b.`charid` = %u "
|
|
"AND"
|
|
" a.`charid` = %u"
|
|
")",
|
|
character_id,
|
|
timestamp,
|
|
character_id,
|
|
character_id
|
|
);
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success())
|
|
return;
|
|
|
|
for (auto row : results)
|
|
compare_list.emplace_back(std::pair<int16, uint32>(Strings::ToInt(row[0]), Strings::ToUnsignedInt(row[1])));
|
|
}
|
|
|
|
bool ZoneDatabase::RestoreCharacterInvSnapshot(uint32 character_id, uint32 timestamp) {
|
|
// we should know what we're doing by the time we call this function..but,
|
|
// this is to prevent inventory deletions where no timestamp entries exists
|
|
if (!ValidateCharacterInvSnapshotTimestamp(character_id, timestamp)) {
|
|
LogError("called for id: [{}] without valid snapshot entries @ [{}]", character_id, timestamp);
|
|
return false;
|
|
}
|
|
|
|
std::string query = StringFormat(
|
|
"DELETE "
|
|
"FROM"
|
|
" `inventory` "
|
|
"WHERE"
|
|
" `charid` = %u",
|
|
character_id
|
|
);
|
|
auto results = database.QueryDatabase(query);
|
|
if (!results.Success())
|
|
return false;
|
|
|
|
query = StringFormat(
|
|
"INSERT "
|
|
"INTO"
|
|
" `inventory` "
|
|
"(`charid`,"
|
|
" `slotid`,"
|
|
" `itemid`,"
|
|
" `charges`,"
|
|
" `color`,"
|
|
" `augslot1`,"
|
|
" `augslot2`,"
|
|
" `augslot3`,"
|
|
" `augslot4`,"
|
|
" `augslot5`,"
|
|
" `augslot6`,"
|
|
" `instnodrop`,"
|
|
" `custom_data`,"
|
|
" `ornamenticon`,"
|
|
" `ornamentidfile`,"
|
|
" `ornament_hero_model`"
|
|
") "
|
|
"SELECT"
|
|
" `charid`,"
|
|
" `slotid`,"
|
|
" `itemid`,"
|
|
" `charges`,"
|
|
" `color`,"
|
|
" `augslot1`,"
|
|
" `augslot2`,"
|
|
" `augslot3`,"
|
|
" `augslot4`,"
|
|
" `augslot5`,"
|
|
" `augslot6`,"
|
|
" `instnodrop`,"
|
|
" `custom_data`,"
|
|
" `ornamenticon`,"
|
|
" `ornamentidfile`,"
|
|
" `ornament_hero_model` "
|
|
"FROM"
|
|
" `inventory_snapshots` "
|
|
"WHERE"
|
|
" `charid` = %u "
|
|
"AND"
|
|
" `time_index` = %u",
|
|
character_id,
|
|
timestamp
|
|
);
|
|
results = database.QueryDatabase(query);
|
|
|
|
LogInventory("[{}] snapshot for [{}] @ [{}]",
|
|
(results.Success() ? "restored" : "failed to restore"), character_id, timestamp);
|
|
|
|
return results.Success();
|
|
}
|
|
|
|
const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load /*= false*/)
|
|
{
|
|
const NPCType *npc = nullptr;
|
|
|
|
/* If there is a cached NPC entry, load it */
|
|
auto itr = zone->npctable.find(npc_type_id);
|
|
if (itr != zone->npctable.end()) {
|
|
return itr->second;
|
|
}
|
|
|
|
std::string filter = fmt::format("id = {}", npc_type_id);
|
|
|
|
if (bulk_load) {
|
|
LogDebug("Performing bulk NPC Types load");
|
|
|
|
filter = fmt::format(
|
|
SQL(
|
|
id IN (
|
|
select npcID from spawnentry where spawngroupID IN (
|
|
select spawngroupID from spawn2 where `zone` = '{}' and (`version` = {} OR `version` = -1)
|
|
)
|
|
)
|
|
),
|
|
zone->GetShortName(),
|
|
zone->GetInstanceVersion()
|
|
);
|
|
}
|
|
|
|
std::vector<uint32> npc_ids;
|
|
|
|
for (NpcTypesRepository::NpcTypes &n : NpcTypesRepository::GetWhere((Database &) content_db, filter)) {
|
|
NPCType *t;
|
|
t = new NPCType;
|
|
memset(t, 0, sizeof *t);
|
|
|
|
t->npc_id = n.id;
|
|
|
|
strn0cpy(t->name, n.name.c_str(), 50);
|
|
|
|
t->level = n.level;
|
|
t->race = n.race;
|
|
t->class_ = n.class_;
|
|
t->max_hp = n.hp;
|
|
t->current_hp = n.hp;
|
|
t->Mana = n.mana;
|
|
t->gender = n.gender;
|
|
t->texture = n.texture;
|
|
t->helmtexture = n.helmtexture;
|
|
t->herosforgemodel = n.herosforgemodel;
|
|
t->size = n.size;
|
|
t->loottable_id = n.loottable_id;
|
|
t->merchanttype = n.merchant_id;
|
|
t->alt_currency_type = n.alt_currency_id;
|
|
t->adventure_template = n.adventure_template_id;
|
|
t->trap_template = n.trap_template;
|
|
t->attack_speed = n.attack_speed;
|
|
t->STR = n.STR;
|
|
t->STA = n.STA;
|
|
t->DEX = n.DEX;
|
|
t->AGI = n.AGI;
|
|
t->INT = n._INT;
|
|
t->WIS = n.WIS;
|
|
t->CHA = n.CHA;
|
|
t->MR = n.MR;
|
|
t->CR = n.CR;
|
|
t->DR = n.DR;
|
|
t->FR = n.FR;
|
|
t->PR = n.PR;
|
|
t->Corrup = n.Corrup;
|
|
t->PhR = n.PhR;
|
|
t->min_dmg = n.mindmg;
|
|
t->max_dmg = n.maxdmg;
|
|
t->attack_count = n.attack_count;
|
|
|
|
if (!n.special_abilities.empty()) {
|
|
strn0cpy(t->special_abilities, n.special_abilities.c_str(), 512);
|
|
}
|
|
else {
|
|
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;
|
|
t->d_melee_texture2 = n.d_melee_texture2;
|
|
strn0cpy(t->ammo_idfile, n.ammo_idfile.c_str(), 30);
|
|
t->prim_melee_type = n.prim_melee_type;
|
|
t->sec_melee_type = n.sec_melee_type;
|
|
t->ranged_type = n.ranged_type;
|
|
t->runspeed = n.runspeed;
|
|
t->findable = n.findable != 0;
|
|
t->is_quest_npc = n.isquest != 0;
|
|
t->trackable = n.trackable != 0;
|
|
t->hp_regen = n.hp_regen_rate;
|
|
t->mana_regen = n.mana_regen_rate;
|
|
|
|
// set default value for aggroradius
|
|
t->aggroradius = (int32) n.aggroradius;
|
|
if (t->aggroradius <= 0) {
|
|
t->aggroradius = 70;
|
|
}
|
|
|
|
t->assistradius = (int32) n.assistradius;
|
|
if (t->assistradius <= 0) {
|
|
t->assistradius = t->aggroradius;
|
|
}
|
|
|
|
if (n.bodytype > 0) {
|
|
t->bodytype = n.bodytype;
|
|
}
|
|
else {
|
|
t->bodytype = 0;
|
|
}
|
|
|
|
// facial features
|
|
t->npc_faction_id = n.npc_faction_id;
|
|
t->luclinface = n.face;
|
|
t->hairstyle = n.luclin_hairstyle;
|
|
t->haircolor = n.luclin_haircolor;
|
|
t->eyecolor1 = n.luclin_eyecolor;
|
|
t->eyecolor2 = n.luclin_eyecolor2;
|
|
t->beardcolor = n.luclin_beardcolor;
|
|
t->beard = n.luclin_beard;
|
|
t->drakkin_heritage = n.drakkin_heritage;
|
|
t->drakkin_tattoo = n.drakkin_tattoo;
|
|
t->drakkin_details = n.drakkin_details;
|
|
|
|
// armor tint
|
|
uint32 armor_tint_id = n.armortint_id;
|
|
t->armor_tint.Head.Color = (n.armortint_red & 0xFF) << 16;
|
|
t->armor_tint.Head.Color |= (n.armortint_green & 0xFF) << 8;
|
|
t->armor_tint.Head.Color |= (n.armortint_blue & 0xFF);
|
|
t->armor_tint.Head.Color |= (t->armor_tint.Head.Color) ? (0xFF << 24) : 0;
|
|
|
|
if (armor_tint_id != 0) {
|
|
|
|
std::string armortint_query = StringFormat(
|
|
"SELECT red1h, grn1h, blu1h, "
|
|
"red2c, grn2c, blu2c, "
|
|
"red3a, grn3a, blu3a, "
|
|
"red4b, grn4b, blu4b, "
|
|
"red5g, grn5g, blu5g, "
|
|
"red6l, grn6l, blu6l, "
|
|
"red7f, grn7f, blu7f, "
|
|
"red8x, grn8x, blu8x, "
|
|
"red9x, grn9x, blu9x "
|
|
"FROM npc_types_tint WHERE id = %d",
|
|
armor_tint_id
|
|
);
|
|
|
|
auto armortint_results = QueryDatabase(armortint_query);
|
|
if (!armortint_results.Success() || armortint_results.RowCount() == 0) {
|
|
armor_tint_id = 0;
|
|
}
|
|
else {
|
|
auto& armorTint_row = armortint_results.begin();
|
|
|
|
for (int index = EQ::textures::textureBegin; index <= EQ::textures::LastTexture; index++) {
|
|
t->armor_tint.Slot[index].Color = Strings::ToInt(armorTint_row[index * 3]) << 16;
|
|
t->armor_tint.Slot[index].Color |= Strings::ToInt(armorTint_row[index * 3 + 1]) << 8;
|
|
t->armor_tint.Slot[index].Color |= Strings::ToInt(armorTint_row[index * 3 + 2]);
|
|
t->armor_tint.Slot[index].Color |= (t->armor_tint.Slot[index].Color)
|
|
? (0xFF << 24) : 0;
|
|
}
|
|
}
|
|
}
|
|
// Try loading npc_types tint fields if armor tint is 0 or query failed to get results
|
|
if (armor_tint_id == 0) {
|
|
for (int index = EQ::textures::armorChest; index < EQ::textures::materialCount; index++) {
|
|
t->armor_tint.Slot[index].Color = t->armor_tint.Slot[0].Color; // odd way to 'zero-out' the array...
|
|
}
|
|
}
|
|
|
|
t->see_invis = n.see_invis;
|
|
t->see_invis_undead = n.see_invis_undead != 0; // Set see_invis_undead flag
|
|
|
|
if (!RuleB(NPC, DisableLastNames) && !n.lastname.empty()) {
|
|
strn0cpy(t->lastname, n.lastname.c_str(), sizeof(t->lastname));
|
|
}
|
|
|
|
t->qglobal = n.qglobal != 0; // qglobal
|
|
t->AC = n.AC;
|
|
t->npc_aggro = n.npc_aggro != 0;
|
|
t->spawn_limit = n.spawn_limit;
|
|
t->see_hide = n.see_hide != 0;
|
|
t->see_improved_hide = n.see_improved_hide != 0;
|
|
t->ATK = n.ATK;
|
|
t->accuracy_rating = n.Accuracy;
|
|
t->avoidance_rating = n.Avoidance;
|
|
t->slow_mitigation = n.slow_mitigation;
|
|
t->maxlevel = n.maxlevel;
|
|
t->scalerate = n.scalerate;
|
|
t->private_corpse = n.private_corpse != 0;
|
|
t->unique_spawn_by_name = n.unique_spawn_by_name != 0;
|
|
t->underwater = n.underwater != 0;
|
|
t->emoteid = n.emoteid;
|
|
t->spellscale = n.spellscale;
|
|
t->healscale = n.healscale;
|
|
t->no_target_hotkey = n.no_target_hotkey != 0;
|
|
t->raid_target = n.raid_target != 0;
|
|
t->attack_delay = n.attack_delay * 100; // TODO: fix DB
|
|
t->light = (n.light & 0x0F);
|
|
t->armtexture = n.armtexture;
|
|
t->bracertexture = n.bracertexture;
|
|
t->handtexture = n.handtexture;
|
|
t->legtexture = n.legtexture;
|
|
t->feettexture = n.feettexture;
|
|
t->ignore_despawn = n.ignore_despawn != 0;
|
|
t->show_name = n.show_name != 0;
|
|
t->untargetable = n.untargetable != 0;
|
|
t->charm_ac = n.charm_ac;
|
|
t->charm_min_dmg = n.charm_min_dmg;
|
|
t->charm_max_dmg = n.charm_max_dmg;
|
|
t->charm_attack_delay = n.charm_attack_delay * 100; // TODO: fix DB
|
|
t->charm_accuracy_rating = n.charm_accuracy_rating;
|
|
t->charm_avoidance_rating = n.charm_avoidance_rating;
|
|
t->charm_atk = n.charm_atk;
|
|
t->skip_global_loot = n.skip_global_loot != 0;
|
|
t->rare_spawn = n.rare_spawn != 0;
|
|
t->stuck_behavior = n.stuck_behavior;
|
|
t->use_model = n.model;
|
|
t->flymode = n.flymode;
|
|
t->always_aggro = n.always_aggro != 0;
|
|
t->exp_mod = n.exp_mod;
|
|
t->skip_auto_scale = false; // hardcoded here for now
|
|
t->hp_regen_per_second = n.hp_regen_per_second;
|
|
t->heroic_strikethrough = n.heroic_strikethrough;
|
|
t->faction_amount = n.faction_amount;
|
|
t->keeps_sold_items = n.keeps_sold_items;
|
|
|
|
// If NPC with duplicate NPC id already in table,
|
|
// free item we attempted to add.
|
|
if (zone->npctable.find(t->npc_id) != zone->npctable.end()) {
|
|
std::cerr << "Error loading duplicate NPC " << t->npc_id << std::endl;
|
|
delete t;
|
|
return nullptr;
|
|
}
|
|
|
|
zone->npctable[t->npc_id] = t;
|
|
npc = t;
|
|
|
|
// If NPC ID is not in npc_ids, add to vector
|
|
if (!std::count(npc_ids.begin(), npc_ids.end(), t->npc_id)) {
|
|
npc_ids.emplace_back(t->npc_id);
|
|
}
|
|
}
|
|
|
|
DataBucket::BulkLoadEntities(DataBucketLoadType::NPC, npc_ids);
|
|
|
|
return npc;
|
|
}
|
|
|
|
const NPCType* ZoneDatabase::GetMercenaryType(uint32 merc_npc_type_id, uint16 race_id, uint32 owner_level)
|
|
{
|
|
const uint32 merc_type_id = merc_npc_type_id * 100 + owner_level;
|
|
|
|
auto i = zone->merctable.find(merc_type_id);
|
|
if (i != zone->merctable.end()) {
|
|
return i->second;
|
|
}
|
|
|
|
if (!merc_npc_type_id) {
|
|
return nullptr;
|
|
}
|
|
|
|
const std::string& query = fmt::format(
|
|
SQL(
|
|
SELECT
|
|
m_stats.merc_npc_type_id,
|
|
'' AS name,
|
|
m_stats.level,
|
|
m_types.race_id,
|
|
m_subtypes.class_id,
|
|
m_stats.hp,
|
|
m_stats.mana,
|
|
0 AS gender,
|
|
m_armorinfo.texture,
|
|
m_armorinfo.helmtexture,
|
|
m_stats.attack_delay,
|
|
m_stats.STR,
|
|
m_stats.STA,
|
|
m_stats.DEX,
|
|
m_stats.AGI,
|
|
m_stats._INT,
|
|
m_stats.WIS,
|
|
m_stats.CHA,
|
|
m_stats.MR,
|
|
m_stats.CR,
|
|
m_stats.DR,
|
|
m_stats.FR,
|
|
m_stats.PR,
|
|
m_stats.Corrup,
|
|
m_stats.mindmg,
|
|
m_stats.maxdmg,
|
|
m_stats.attack_count,
|
|
m_stats.special_abilities,
|
|
m_weaponinfo.d_melee_texture1,
|
|
m_weaponinfo.d_melee_texture2,
|
|
m_weaponinfo.prim_melee_type,
|
|
m_weaponinfo.sec_melee_type,
|
|
m_stats.runspeed,
|
|
m_stats.hp_regen_rate,
|
|
m_stats.mana_regen_rate,
|
|
1 AS bodytype,
|
|
m_armorinfo.armortint_id,
|
|
m_armorinfo.armortint_red,
|
|
m_armorinfo.armortint_green,
|
|
m_armorinfo.armortint_blue,
|
|
m_stats.AC,
|
|
m_stats.ATK,
|
|
m_stats.Accuracy,
|
|
m_stats.statscale,
|
|
m_stats.spellscale,
|
|
m_stats.healscale
|
|
FROM merc_stats m_stats
|
|
INNER JOIN merc_armorinfo m_armorinfo
|
|
ON m_stats.merc_npc_type_id = m_armorinfo.merc_npc_type_id
|
|
AND m_armorinfo.minlevel <= m_stats.level AND m_armorinfo.maxlevel >= m_stats.level
|
|
INNER JOIN merc_weaponinfo m_weaponinfo
|
|
ON m_stats.merc_npc_type_id = m_weaponinfo.merc_npc_type_id
|
|
AND m_weaponinfo.minlevel <= m_stats.level AND m_weaponinfo.maxlevel >= m_stats.level
|
|
INNER JOIN merc_templates m_templates
|
|
ON m_templates.merc_npc_type_id = m_stats.merc_npc_type_id
|
|
INNER JOIN merc_types m_types
|
|
ON m_templates.merc_type_id = m_types.merc_type_id
|
|
INNER JOIN merc_subtypes m_subtypes
|
|
ON m_templates.merc_subtype_id = m_subtypes.merc_subtype_id
|
|
WHERE m_templates.merc_npc_type_id = {} AND m_types.race_id = {} AND m_stats.clientlevel = {}
|
|
),
|
|
merc_npc_type_id,
|
|
race_id,
|
|
owner_level
|
|
);
|
|
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success() || !results.RowCount()) {
|
|
return nullptr;
|
|
}
|
|
|
|
const NPCType* n = nullptr;
|
|
|
|
auto row = results.begin();
|
|
|
|
NPCType* t = new NPCType;
|
|
|
|
memset(t, 0, sizeof *t);
|
|
|
|
t->npc_id = Strings::ToInt(row[0]);
|
|
|
|
strn0cpy(t->name, row[1], sizeof(t->name));
|
|
|
|
t->level = Strings::ToInt(row[2]);
|
|
t->race = Strings::ToInt(row[3]);
|
|
t->class_ = Strings::ToInt(row[4]);
|
|
t->max_hp = Strings::ToInt(row[5]);
|
|
t->current_hp = t->max_hp;
|
|
t->Mana = Strings::ToInt(row[6]);
|
|
t->gender = Strings::ToInt(row[7]);
|
|
t->texture = Strings::ToInt(row[8]);
|
|
t->helmtexture = Strings::ToInt(row[9]);
|
|
t->attack_delay = Strings::ToInt(row[10]) * 100; // TODO: fix DB
|
|
t->STR = Strings::ToInt(row[11]);
|
|
t->STA = Strings::ToInt(row[12]);
|
|
t->DEX = Strings::ToInt(row[13]);
|
|
t->AGI = Strings::ToInt(row[14]);
|
|
t->INT = Strings::ToInt(row[15]);
|
|
t->WIS = Strings::ToInt(row[16]);
|
|
t->CHA = Strings::ToInt(row[17]);
|
|
t->MR = Strings::ToInt(row[18]);
|
|
t->CR = Strings::ToInt(row[19]);
|
|
t->DR = Strings::ToInt(row[20]);
|
|
t->FR = Strings::ToInt(row[21]);
|
|
t->PR = Strings::ToInt(row[22]);
|
|
t->Corrup = Strings::ToInt(row[23]);
|
|
t->min_dmg = Strings::ToInt(row[24]);
|
|
t->max_dmg = Strings::ToInt(row[25]);
|
|
t->attack_count = Strings::ToInt(row[26]);
|
|
|
|
if (row[27]) {
|
|
strn0cpy(t->special_abilities, row[27], sizeof(t->special_abilities));
|
|
} else {
|
|
t->special_abilities[0] = '\0';
|
|
}
|
|
|
|
t->d_melee_texture1 = Strings::ToUnsignedInt(row[28]);
|
|
t->d_melee_texture2 = Strings::ToUnsignedInt(row[29]);
|
|
t->prim_melee_type = Strings::ToInt(row[30]);
|
|
t->sec_melee_type = Strings::ToInt(row[31]);
|
|
t->runspeed = Strings::ToFloat(row[32]);
|
|
t->hp_regen = Strings::ToInt(row[33]);
|
|
t->mana_regen = Strings::ToInt(row[34]);
|
|
t->aggroradius = RuleI(Mercs, AggroRadius);
|
|
t->bodytype = row[35] && strlen(row[35]) ? static_cast<uint8>(Strings::ToUnsignedInt(row[35])) : 1;
|
|
|
|
uint32 armor_tint_id = Strings::ToInt(row[36]);
|
|
|
|
t->armor_tint.Slot[0].Color = (Strings::ToInt(row[37]) & 0xFF) << 16;
|
|
t->armor_tint.Slot[0].Color |= (Strings::ToInt(row[38]) & 0xFF) << 8;
|
|
t->armor_tint.Slot[0].Color |= (Strings::ToInt(row[39]) & 0xFF);
|
|
t->armor_tint.Slot[0].Color |= (t->armor_tint.Slot[0].Color) ? (0xFF << 24) : 0;
|
|
|
|
if (armor_tint_id == 0) {
|
|
for (int index = EQ::textures::armorChest; index <= EQ::textures::LastTexture; index++) {
|
|
t->armor_tint.Slot[index].Color = t->armor_tint.Slot[0].Color;
|
|
}
|
|
} else if (t->armor_tint.Slot[0].Color == 0) {
|
|
const auto& e = NpcTypesTintRepository::FindOne(*this, armor_tint_id);
|
|
if (!e.id) {
|
|
armor_tint_id = 0;
|
|
} else {
|
|
t->armor_tint.Slot[EQ::textures::armorHead].Color = e.red1h << 16;
|
|
t->armor_tint.Slot[EQ::textures::armorHead].Color = e.grn1h << 8;
|
|
t->armor_tint.Slot[EQ::textures::armorHead].Color = e.blu1h;
|
|
|
|
t->armor_tint.Slot[EQ::textures::armorChest].Color = e.red2c << 16;
|
|
t->armor_tint.Slot[EQ::textures::armorChest].Color = e.grn2c << 8;
|
|
t->armor_tint.Slot[EQ::textures::armorChest].Color = e.blu2c;
|
|
|
|
t->armor_tint.Slot[EQ::textures::armorArms].Color = e.red3a << 16;
|
|
t->armor_tint.Slot[EQ::textures::armorArms].Color = e.grn3a << 8;
|
|
t->armor_tint.Slot[EQ::textures::armorArms].Color = e.blu3a;
|
|
|
|
t->armor_tint.Slot[EQ::textures::armorWrist].Color = e.red4b << 16;
|
|
t->armor_tint.Slot[EQ::textures::armorWrist].Color = e.grn4b << 8;
|
|
t->armor_tint.Slot[EQ::textures::armorWrist].Color = e.blu4b;
|
|
|
|
t->armor_tint.Slot[EQ::textures::armorHands].Color = e.red5g << 16;
|
|
t->armor_tint.Slot[EQ::textures::armorHands].Color = e.grn5g << 8;
|
|
t->armor_tint.Slot[EQ::textures::armorHands].Color = e.blu5g;
|
|
|
|
t->armor_tint.Slot[EQ::textures::armorLegs].Color = e.red6l << 16;
|
|
t->armor_tint.Slot[EQ::textures::armorLegs].Color = e.grn6l << 8;
|
|
t->armor_tint.Slot[EQ::textures::armorLegs].Color = e.blu6l;
|
|
|
|
t->armor_tint.Slot[EQ::textures::armorFeet].Color = e.red7f << 16;
|
|
t->armor_tint.Slot[EQ::textures::armorFeet].Color = e.grn7f << 8;
|
|
t->armor_tint.Slot[EQ::textures::armorFeet].Color = e.blu7f;
|
|
|
|
t->armor_tint.Slot[EQ::textures::weaponPrimary].Color = e.red8x << 16;
|
|
t->armor_tint.Slot[EQ::textures::weaponPrimary].Color = e.grn8x << 8;
|
|
t->armor_tint.Slot[EQ::textures::weaponPrimary].Color = e.blu8x;
|
|
|
|
t->armor_tint.Slot[EQ::textures::weaponSecondary].Color = e.red9x << 16;
|
|
t->armor_tint.Slot[EQ::textures::weaponSecondary].Color = e.grn9x << 8;
|
|
t->armor_tint.Slot[EQ::textures::weaponSecondary].Color = e.blu9x;
|
|
}
|
|
}
|
|
|
|
t->AC = Strings::ToInt(row[40]);
|
|
t->ATK = Strings::ToInt(row[41]);
|
|
t->accuracy_rating = Strings::ToInt(row[42]);
|
|
t->scalerate = Strings::ToInt(row[43]);
|
|
t->spellscale = Strings::ToInt(row[44]);
|
|
t->healscale = Strings::ToInt(row[45]);
|
|
t->skip_global_loot = true;
|
|
t->skip_auto_scale = true;
|
|
|
|
// If Merc with duplicate NPC id already in table,
|
|
// free item we attempted to add.
|
|
if (zone->merctable.find(merc_type_id) != zone->merctable.end()) {
|
|
delete t;
|
|
return nullptr;
|
|
}
|
|
|
|
zone->merctable[merc_type_id] = t;
|
|
n = t;
|
|
|
|
return n;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadMercenaryInfo(Client* c)
|
|
{
|
|
const auto& l = MercsRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`OwnerCharacterID` = {} ORDER BY `Slot`",
|
|
c->CharacterID()
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
if (e.Slot >= MAXMERCS) {
|
|
continue;
|
|
}
|
|
|
|
auto& m = c->GetMercInfo(e.Slot);
|
|
|
|
strn0cpy(m.merc_name, e.Name.c_str(), sizeof(m.merc_name));
|
|
|
|
m.mercid = e.MercID;
|
|
m.slot = e.Slot;
|
|
m.MercTemplateID = e.TemplateID;
|
|
m.SuspendedTime = e.SuspendedTime;
|
|
m.IsSuspended = e.IsSuspended;
|
|
m.MercTimerRemaining = e.TimerRemaining;
|
|
m.Gender = e.Gender;
|
|
m.MercSize = e.MercSize;
|
|
m.State = 5;
|
|
m.Stance = e.StanceID;
|
|
m.hp = e.HP;
|
|
m.mana = e.Mana;
|
|
m.endurance = e.Endurance;
|
|
m.face = e.Face;
|
|
m.luclinHairStyle = e.LuclinHairStyle;
|
|
m.luclinHairColor = e.LuclinHairColor;
|
|
m.luclinEyeColor = e.LuclinEyeColor;
|
|
m.luclinEyeColor2 = e.LuclinEyeColor2;
|
|
m.luclinBeardColor = e.LuclinBeardColor;
|
|
m.luclinBeard = e.LuclinBeard;
|
|
m.drakkinHeritage = e.DrakkinHeritage;
|
|
m.drakkinTattoo = e.DrakkinTattoo;
|
|
m.drakkinDetails = e.DrakkinDetails;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCurrentMercenary(Client* c)
|
|
{
|
|
const uint8 mercenary_slot = c->GetMercSlot();
|
|
|
|
if (mercenary_slot > MAXMERCS) {
|
|
return false;
|
|
}
|
|
|
|
const auto& e = MercsRepository::GetMercenaryBySlot(*this, c);
|
|
|
|
if (!e.MercID) {
|
|
return false;
|
|
}
|
|
|
|
auto& m = c->GetMercInfo(mercenary_slot);
|
|
|
|
strn0cpy(m.merc_name, e.Name.c_str(), sizeof(m.merc_name));
|
|
|
|
m.mercid = e.MercID;
|
|
m.slot = e.Slot;
|
|
m.MercTemplateID = e.TemplateID;
|
|
m.SuspendedTime = e.SuspendedTime;
|
|
m.IsSuspended = e.IsSuspended;
|
|
m.MercTimerRemaining = e.TimerRemaining;
|
|
m.Gender = e.Gender;
|
|
m.MercSize = e.MercSize;
|
|
m.State = e.StanceID;
|
|
m.hp = e.HP;
|
|
m.mana = e.Mana;
|
|
m.endurance = e.Endurance;
|
|
m.face = e.Face;
|
|
m.luclinHairStyle = e.LuclinHairStyle;
|
|
m.luclinHairColor = e.LuclinHairColor;
|
|
m.luclinEyeColor = e.LuclinEyeColor;
|
|
m.luclinEyeColor2 = e.LuclinEyeColor2;
|
|
m.luclinBeardColor = e.LuclinBeardColor;
|
|
m.luclinBeard = e.LuclinBeard;
|
|
m.drakkinHeritage = e.DrakkinHeritage;
|
|
m.drakkinTattoo = e.DrakkinTattoo;
|
|
m.drakkinDetails = e.DrakkinDetails;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::SaveMercenary(Merc* m)
|
|
{
|
|
Client* c = m->GetMercenaryOwner();
|
|
|
|
if (!c) {
|
|
return false;
|
|
}
|
|
|
|
if (!m->GetMercenaryID()) {
|
|
auto e = MercsRepository::NewEntity();
|
|
|
|
e.OwnerCharacterID = m->GetMercenaryCharacterID();
|
|
e.Slot = c->GetNumberOfMercenaries();
|
|
e.Name = m->GetCleanName();
|
|
e.TemplateID = m->GetMercenaryTemplateID();
|
|
e.SuspendedTime = c->GetMercInfo().SuspendedTime;
|
|
e.IsSuspended = m->IsSuspended();
|
|
e.TimerRemaining = c->GetMercInfo().MercTimerRemaining;
|
|
e.Gender = m->GetGender();
|
|
e.MercSize = m->GetSize();
|
|
e.StanceID = m->GetStance();
|
|
e.HP = m->GetHP();
|
|
e.Mana = m->GetMana();
|
|
e.Endurance = m->GetEndurance();
|
|
e.Face = m->GetLuclinFace();
|
|
e.LuclinHairStyle = m->GetHairStyle();
|
|
e.LuclinHairColor = m->GetHairColor();
|
|
e.LuclinEyeColor = m->GetEyeColor1();
|
|
e.LuclinEyeColor2 = m->GetEyeColor2();
|
|
e.LuclinBeardColor = m->GetBeardColor();
|
|
e.LuclinBeard = m->GetBeard();
|
|
e.DrakkinHeritage = m->GetDrakkinHeritage();
|
|
e.DrakkinTattoo = m->GetDrakkinTattoo();
|
|
e.DrakkinDetails = m->GetDrakkinDetails();
|
|
|
|
e = MercsRepository::InsertOne(*this, e);
|
|
|
|
if (!e.MercID) {
|
|
c->Message(Chat::Red, "Unable to save mercenary to the database.");
|
|
return false;
|
|
}
|
|
|
|
m->SetMercID(e.MercID);
|
|
m->UpdateMercInfo(c);
|
|
|
|
database.SaveMercenaryBuffs(m);
|
|
|
|
return true;
|
|
}
|
|
|
|
auto e = MercsRepository::FindOne(*this, m->GetMercenaryID());
|
|
|
|
e.OwnerCharacterID = m->GetMercenaryCharacterID();
|
|
e.Slot = c->GetNumberOfMercenaries();
|
|
e.Name = m->GetCleanName();
|
|
e.TemplateID = m->GetMercenaryTemplateID();
|
|
e.SuspendedTime = c->GetMercInfo().SuspendedTime;
|
|
e.IsSuspended = m->IsSuspended();
|
|
e.TimerRemaining = c->GetMercInfo().MercTimerRemaining;
|
|
e.Gender = m->GetGender();
|
|
e.MercSize = m->GetSize();
|
|
e.StanceID = m->GetStance();
|
|
e.HP = m->GetHP();
|
|
e.Mana = m->GetMana();
|
|
e.Endurance = m->GetEndurance();
|
|
e.Face = m->GetLuclinFace();
|
|
e.LuclinHairStyle = m->GetHairStyle();
|
|
e.LuclinHairColor = m->GetHairColor();
|
|
e.LuclinEyeColor = m->GetEyeColor1();
|
|
e.LuclinEyeColor2 = m->GetEyeColor2();
|
|
e.LuclinBeardColor = m->GetBeardColor();
|
|
e.LuclinBeard = m->GetBeard();
|
|
e.DrakkinHeritage = m->GetDrakkinHeritage();
|
|
e.DrakkinTattoo = m->GetDrakkinTattoo();
|
|
e.DrakkinDetails = m->GetDrakkinDetails();
|
|
|
|
const int updated = MercsRepository::UpdateOne(*this, e);
|
|
|
|
if (!updated) {
|
|
c->Message(Chat::Red, "Unable to save mercenary to the database.");
|
|
return false;
|
|
}
|
|
|
|
m->UpdateMercInfo(c);
|
|
|
|
database.SaveMercenaryBuffs(m);
|
|
|
|
return true;
|
|
}
|
|
|
|
void ZoneDatabase::SaveMercenaryBuffs(Merc* m)
|
|
{
|
|
auto buffs = m->GetBuffs();
|
|
|
|
MercBuffsRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`MercID` = {}",
|
|
m->GetMercenaryID()
|
|
)
|
|
);
|
|
|
|
std::vector<MercBuffsRepository::MercBuffs> v;
|
|
|
|
auto e = MercBuffsRepository::NewEntity();
|
|
|
|
for (uint32 slot_id = 0; slot_id <= BUFF_COUNT; slot_id++) {
|
|
if (!IsValidSpell(buffs[slot_id].spellid)) {
|
|
continue;
|
|
}
|
|
|
|
e.MercId = m->GetMercenaryID();
|
|
e.SpellId = buffs[slot_id].spellid;
|
|
e.CasterLevel = buffs[slot_id].casterlevel;
|
|
e.DurationFormula = spells[buffs[slot_id].spellid].buff_duration_formula;
|
|
e.TicsRemaining = buffs[slot_id].ticsremaining;
|
|
e.PoisonCounters = CalculatePoisonCounters(buffs[slot_id].spellid) > 0 ? buffs[slot_id].counters : 0;
|
|
e.DiseaseCounters = CalculateDiseaseCounters(buffs[slot_id].spellid) > 0 ? buffs[slot_id].counters : 0;
|
|
e.CurseCounters = CalculateCurseCounters(buffs[slot_id].spellid) > 0 ? buffs[slot_id].counters : 0;
|
|
e.CorruptionCounters = CalculateCorruptionCounters(buffs[slot_id].spellid) > 0 ? buffs[slot_id].counters : 0;
|
|
e.HitCount = buffs[slot_id].hit_number;
|
|
e.MeleeRune = buffs[slot_id].melee_rune;
|
|
e.MagicRune = buffs[slot_id].magic_rune;
|
|
e.dot_rune = buffs[slot_id].dot_rune;
|
|
e.caston_x = buffs[slot_id].caston_x;
|
|
e.caston_y = buffs[slot_id].caston_y;
|
|
e.caston_z = buffs[slot_id].caston_z;
|
|
e.Persistent = buffs[slot_id].persistant_buff ? 1 : 0;
|
|
e.ExtraDIChance = buffs[slot_id].ExtraDIChance;
|
|
|
|
v.emplace_back(e);
|
|
}
|
|
|
|
MercBuffsRepository::InsertMany(*this, v);
|
|
}
|
|
|
|
void ZoneDatabase::LoadMercenaryBuffs(Merc* m)
|
|
{
|
|
auto buffs = m->GetBuffs();
|
|
const int max_slots = m->GetMaxBuffSlots();
|
|
|
|
const auto& l = MercBuffsRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`MercID` = {}",
|
|
m->GetMercenaryID()
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return;
|
|
}
|
|
|
|
uint32 slot_id = 0;
|
|
|
|
for (const auto& e : l) {
|
|
if (slot_id == BUFF_COUNT) {
|
|
break;
|
|
}
|
|
|
|
buffs[slot_id].spellid = e.SpellId;
|
|
buffs[slot_id].casterlevel = e.CasterLevel;
|
|
buffs[slot_id].ticsremaining = e.TicsRemaining;
|
|
buffs[slot_id].hit_number = e.HitCount;
|
|
buffs[slot_id].melee_rune = e.MeleeRune;
|
|
buffs[slot_id].magic_rune = e.MagicRune;
|
|
buffs[slot_id].dot_rune = e.dot_rune;
|
|
buffs[slot_id].caston_x = e.caston_x;
|
|
buffs[slot_id].caston_y = e.caston_y;
|
|
buffs[slot_id].caston_z = e.caston_z;
|
|
buffs[slot_id].casterid = 0;
|
|
buffs[slot_id].ExtraDIChance = e.ExtraDIChance;
|
|
buffs[slot_id].persistant_buff = e.Persistent;
|
|
|
|
if (CalculatePoisonCounters(buffs[slot_id].spellid) > 0) {
|
|
buffs[slot_id].counters = e.PoisonCounters;
|
|
}
|
|
|
|
if (CalculateDiseaseCounters(buffs[slot_id].spellid) > 0) {
|
|
buffs[slot_id].counters = e.DiseaseCounters;
|
|
}
|
|
|
|
if (CalculateCurseCounters(buffs[slot_id].spellid) > 0) {
|
|
buffs[slot_id].counters = e.CurseCounters;
|
|
}
|
|
|
|
if (CalculateCorruptionCounters(buffs[slot_id].spellid) > 0) {
|
|
buffs[slot_id].counters = e.CorruptionCounters;
|
|
}
|
|
}
|
|
|
|
MercBuffsRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`MercID` = {}",
|
|
m->GetMercenaryID()
|
|
)
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::DeleteMercenary(uint32 mercenary_id)
|
|
{
|
|
if (!mercenary_id) {
|
|
return false;
|
|
}
|
|
|
|
MercBuffsRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`MercID` = {}",
|
|
mercenary_id
|
|
)
|
|
);
|
|
|
|
const int deleted = MercsRepository::DeleteOne(*this, mercenary_id);
|
|
if (!deleted) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ZoneDatabase::LoadMercenaryEquipment(Merc* m)
|
|
{
|
|
const int merc_subtype_id = MercSubtypesRepository::GetSubtype(*this, m->GetClass(), m->GetTierID());
|
|
|
|
const auto& l = MercInventoryRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`merc_subtype_id` = {} AND `min_level` <= {} AND `max_level` >= {}",
|
|
merc_subtype_id,
|
|
m->GetLevel(),
|
|
m->GetLevel()
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return;
|
|
}
|
|
|
|
uint32 item_count = 0;
|
|
|
|
for (const auto& e: l) {
|
|
if (item_count == EQ::invslot::EQUIPMENT_COUNT) {
|
|
break;
|
|
}
|
|
|
|
if (!e.item_id) {
|
|
continue;
|
|
}
|
|
|
|
m->AddItem(item_count, e.item_id);
|
|
|
|
item_count++;
|
|
}
|
|
}
|
|
|
|
void ZoneDatabase::SaveMerchantTemp(
|
|
uint32 npc_id,
|
|
uint32 slot_id,
|
|
uint32 zone_id,
|
|
uint32 instance_id,
|
|
uint32 item_id,
|
|
uint32 charges
|
|
)
|
|
{
|
|
auto e = MerchantlistTempRepository::NewEntity();
|
|
|
|
e.npcid = npc_id;
|
|
e.slot = slot_id;
|
|
e.zone_id = zone_id;
|
|
e.instance_id = instance_id;
|
|
e.itemid = item_id;
|
|
e.charges = charges;
|
|
|
|
MerchantlistTempRepository::ReplaceOne(*this, e);
|
|
}
|
|
|
|
void ZoneDatabase::DeleteMerchantTemp(uint32 npc_id, uint32 slot_id, uint32 zone_id, uint32 instance_id)
|
|
{
|
|
MerchantlistTempRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`npcid` = {} AND `slot` = {} AND `zone_id` = {} AND `instance_id` = {}",
|
|
npc_id,
|
|
slot_id,
|
|
zone_id,
|
|
instance_id
|
|
)
|
|
);
|
|
}
|
|
|
|
//New functions for timezone
|
|
uint32 ZoneDatabase::GetZoneTZ(uint32 zoneid, uint32 version) {
|
|
|
|
std::string query = StringFormat("SELECT timezone FROM zone WHERE zoneidnumber = %i "
|
|
"AND (version = %i OR version = 0) ORDER BY version DESC",
|
|
zoneid, version);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success()) {
|
|
return 0;
|
|
}
|
|
|
|
if (results.RowCount() == 0)
|
|
return 0;
|
|
|
|
auto& row = results.begin();
|
|
return Strings::ToInt(row[0]);
|
|
}
|
|
|
|
bool ZoneDatabase::SetZoneTZ(uint32 zoneid, uint32 version, uint32 tz) {
|
|
|
|
std::string query = StringFormat("UPDATE zone SET timezone = %i "
|
|
"WHERE zoneidnumber = %i AND version = %i",
|
|
tz, zoneid, version);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success()) {
|
|
return false;
|
|
}
|
|
|
|
return results.RowsAffected() == 1;
|
|
}
|
|
|
|
void ZoneDatabase::RefreshGroupFromDB(Client *client){
|
|
if (!client) {
|
|
return;
|
|
}
|
|
|
|
Group *group = client->GetGroup();
|
|
|
|
if (!group) {
|
|
return;
|
|
}
|
|
|
|
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate2_Struct));
|
|
GroupUpdate2_Struct* gu = (GroupUpdate2_Struct*)outapp->pBuffer;
|
|
gu->action = groupActUpdate;
|
|
|
|
strcpy(gu->yourname, client->GetName());
|
|
GetGroupLeadershipInfo(group->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas);
|
|
gu->NPCMarkerID = group->GetNPCMarkerID();
|
|
|
|
int index = 0;
|
|
|
|
auto query = fmt::format(
|
|
"SELECT name FROM group_id WHERE groupid = {}",
|
|
group->GetID()
|
|
);
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (results.Success()) {
|
|
for (auto row : results) {
|
|
if (index >= 6) {
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(client->GetName(), row[0])) {
|
|
continue;
|
|
}
|
|
|
|
strcpy(gu->membername[index], row[0]);
|
|
index++;
|
|
}
|
|
}
|
|
|
|
client->QueuePacket(outapp);
|
|
safe_delete(outapp);
|
|
|
|
if (client->ClientVersion() >= EQ::versions::ClientVersion::SoD) {
|
|
group->NotifyMainTank(client, 1);
|
|
group->NotifyPuller(client, 1);
|
|
}
|
|
|
|
group->NotifyMainAssist(client, 1);
|
|
group->NotifyMarkNPC(client);
|
|
group->NotifyAssistTarget(client);
|
|
group->NotifyTankTarget(client);
|
|
group->NotifyPullerTarget(client);
|
|
group->SendMarkedNPCsToMember(client);
|
|
}
|
|
|
|
int64 ZoneDatabase::GetBlockedSpellsCount(uint32 zone_id)
|
|
{
|
|
return BlockedSpellsRepository::Count(
|
|
*this,
|
|
fmt::format(
|
|
"zoneid = {} {}",
|
|
zone_id,
|
|
ContentFilterCriteria::apply()
|
|
)
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::LoadBlockedSpells(int64 blocked_spells_count, ZoneSpellsBlocked* into, uint32 zone_id)
|
|
{
|
|
LogInfo("Loading Blocked Spells from database for {} ({}).", zone_store.GetZoneName(zone_id, true), zone_id);
|
|
|
|
const auto& l = BlockedSpellsRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"zoneid = {} {} ORDER BY id ASC",
|
|
zone_id,
|
|
ContentFilterCriteria::apply()
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return true;
|
|
}
|
|
|
|
int64 i = 0;
|
|
|
|
for (const auto& e : l) {
|
|
if (i >= blocked_spells_count) {
|
|
LogError(
|
|
"Blocked spells count of {} exceeded for {} ({}).",
|
|
blocked_spells_count,
|
|
zone_store.GetZoneName(zone_id, true),
|
|
zone_id
|
|
);
|
|
break;
|
|
}
|
|
|
|
memset(&into[i], 0, sizeof(ZoneSpellsBlocked));
|
|
into[i].spellid = e.spellid;
|
|
into[i].type = e.type;
|
|
into[i].m_Location = glm::vec3(e.x, e.y, e.z);
|
|
into[i].m_Difference = glm::vec3(e.x_diff, e.y_diff, e.z_diff);
|
|
strn0cpy(into[i].message, e.message.c_str(), sizeof(into[i].message));
|
|
i++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int ZoneDatabase::getZoneShutDownDelay(uint32 zoneID, uint32 version)
|
|
{
|
|
auto z = GetZoneVersionWithFallback(zoneID, version);
|
|
|
|
return z ? z->shutdowndelay : RuleI(Zone, AutoShutdownDelay);
|
|
}
|
|
|
|
uint32 ZoneDatabase::GetKarma(uint32 account_id)
|
|
{
|
|
const auto& e = AccountRepository::FindOne(*this, account_id);
|
|
|
|
return !e.id ? 0 : e.karma;
|
|
}
|
|
|
|
void ZoneDatabase::UpdateKarma(uint32 account_id, uint32 amount)
|
|
{
|
|
auto e = AccountRepository::FindOne(*this, account_id);
|
|
if (!e.id) {
|
|
return;
|
|
}
|
|
|
|
e.karma = amount;
|
|
|
|
AccountRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
void ZoneDatabase::ListAllInstances(Client* client, uint32 character_id)
|
|
{
|
|
if (!client) {
|
|
return;
|
|
}
|
|
|
|
std::string query = fmt::format(
|
|
"SELECT instance_list.id, zone, version, start_time, duration, never_expires "
|
|
"FROM instance_list JOIN instance_list_player "
|
|
"ON instance_list.id = instance_list_player.id "
|
|
"WHERE instance_list_player.charid = {}",
|
|
character_id
|
|
);
|
|
auto results = QueryDatabase(query);
|
|
if (!results.Success()) {
|
|
return;
|
|
}
|
|
|
|
auto character_name = database.GetCharNameByID(character_id);
|
|
bool is_same_client = client->CharacterID() == character_id;
|
|
if (character_name.empty()) {
|
|
client->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"Character ID '{}' does not exist.",
|
|
character_id
|
|
).c_str()
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (!results.RowCount()) {
|
|
client->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"{} not in any Instances.",
|
|
(
|
|
is_same_client ?
|
|
"You are" :
|
|
fmt::format(
|
|
"{} ({}) is",
|
|
character_name,
|
|
character_id
|
|
)
|
|
)
|
|
).c_str()
|
|
);
|
|
return;
|
|
}
|
|
|
|
client->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"{} in the following Instances.",
|
|
(
|
|
is_same_client ?
|
|
"You are" :
|
|
fmt::format(
|
|
"{} ({}) is",
|
|
character_name,
|
|
character_id
|
|
)
|
|
)
|
|
).c_str()
|
|
);
|
|
|
|
uint32 instance_count = 0;
|
|
for (auto row : results) {
|
|
auto instance_id = Strings::ToUnsignedInt(row[0]);
|
|
auto zone_id = Strings::ToUnsignedInt(row[1]);
|
|
auto version = Strings::ToUnsignedInt(row[2]);
|
|
auto start_time = Strings::ToUnsignedInt(row[3]);
|
|
auto duration = Strings::ToUnsignedInt(row[4]);
|
|
auto never_expires = Strings::ToInt(row[5]) ? true : false;
|
|
std::string remaining_time_string = "Never";
|
|
timeval time_value;
|
|
gettimeofday(&time_value, nullptr);
|
|
auto current_time = time_value.tv_sec;
|
|
auto remaining_time = ((start_time + duration) - current_time);
|
|
if (!never_expires) {
|
|
if (remaining_time > 0) {
|
|
remaining_time_string = Strings::SecondsToTime(remaining_time);
|
|
} else {
|
|
remaining_time_string = "Already Expired";
|
|
}
|
|
}
|
|
|
|
client->Message(
|
|
Chat::White,
|
|
fmt::format("Instance {} | Zone: {} ({}){}",
|
|
instance_id,
|
|
ZoneLongName(zone_id),
|
|
zone_id,
|
|
(
|
|
version ?
|
|
fmt::format(
|
|
" Version: {}",
|
|
version
|
|
) :
|
|
""
|
|
)
|
|
).c_str()
|
|
);
|
|
|
|
client->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"Instance {} | Expires: {}",
|
|
instance_id,
|
|
remaining_time_string,
|
|
remaining_time
|
|
).c_str()
|
|
);
|
|
|
|
instance_count++;
|
|
}
|
|
|
|
client->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"{} in {} Instance{}.",
|
|
(
|
|
is_same_client ?
|
|
"You are" :
|
|
fmt::format(
|
|
"{} ({}) is",
|
|
character_name,
|
|
character_id
|
|
)
|
|
),
|
|
instance_count,
|
|
instance_count != 1 ? "s" : ""
|
|
).c_str()
|
|
);
|
|
}
|
|
|
|
void ZoneDatabase::QGlobalPurge()
|
|
{
|
|
const std::string query = "DELETE FROM quest_globals WHERE expdate < UNIX_TIMESTAMP()";
|
|
database.QueryDatabase(query);
|
|
}
|
|
|
|
void ZoneDatabase::LoadAltCurrencyValues(uint32 char_id, std::map<uint32, uint32> ¤cy)
|
|
{
|
|
const auto& l = CharacterAltCurrencyRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`char_id` = {}",
|
|
char_id
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
currency[e.currency_id] = e.amount;
|
|
}
|
|
}
|
|
|
|
void ZoneDatabase::UpdateAltCurrencyValue(uint32 char_id, uint32 currency_id, uint32 value)
|
|
{
|
|
auto e = CharacterAltCurrencyRepository::NewEntity();
|
|
|
|
e.char_id = char_id;
|
|
e.currency_id = currency_id;
|
|
e.amount = value;
|
|
|
|
CharacterAltCurrencyRepository::ReplaceOne(*this, e);
|
|
}
|
|
|
|
void ZoneDatabase::SaveBuffs(Client *client)
|
|
{
|
|
CharacterBuffsRepository::DeleteWhere(
|
|
database,
|
|
fmt::format(
|
|
"`character_id` = {}",
|
|
client->CharacterID()
|
|
)
|
|
);
|
|
|
|
auto buffs = client->GetBuffs();
|
|
const int max_buff_slots = client->GetMaxBuffSlots();
|
|
|
|
std::vector<CharacterBuffsRepository::CharacterBuffs> v;
|
|
|
|
auto e = CharacterBuffsRepository::NewEntity();
|
|
|
|
uint32 character_buff_count = 0;
|
|
|
|
for (int slot_id = 0; slot_id < max_buff_slots; slot_id++) {
|
|
if (!IsValidSpell(buffs[slot_id].spellid)) {
|
|
continue;
|
|
}
|
|
|
|
character_buff_count++;
|
|
}
|
|
|
|
v.reserve(character_buff_count);
|
|
|
|
for (int slot_id = 0; slot_id < max_buff_slots; slot_id++) {
|
|
if (!IsValidSpell(buffs[slot_id].spellid)) {
|
|
continue;
|
|
}
|
|
|
|
e.character_id = client->CharacterID();
|
|
e.slot_id = slot_id;
|
|
e.spell_id = buffs[slot_id].spellid;
|
|
e.caster_level = buffs[slot_id].casterlevel;
|
|
e.caster_name = buffs[slot_id].caster_name;
|
|
e.ticsremaining = buffs[slot_id].ticsremaining;
|
|
e.counters = buffs[slot_id].counters;
|
|
e.numhits = buffs[slot_id].hit_number;
|
|
e.melee_rune = buffs[slot_id].melee_rune;
|
|
e.magic_rune = buffs[slot_id].magic_rune;
|
|
e.persistent = buffs[slot_id].persistant_buff;
|
|
e.dot_rune = buffs[slot_id].dot_rune;
|
|
e.caston_x = buffs[slot_id].caston_x;
|
|
e.caston_y = buffs[slot_id].caston_y;
|
|
e.caston_z = buffs[slot_id].caston_z;
|
|
e.ExtraDIChance = buffs[slot_id].ExtraDIChance;
|
|
e.instrument_mod = buffs[slot_id].instrument_mod;
|
|
|
|
v.emplace_back(e);
|
|
}
|
|
|
|
if (!v.empty()) {
|
|
CharacterBuffsRepository::ReplaceMany(database, v);
|
|
}
|
|
}
|
|
|
|
void ZoneDatabase::LoadBuffs(Client *client)
|
|
{
|
|
auto buffs = client->GetBuffs();
|
|
int max_buff_slots = client->GetMaxBuffSlots();
|
|
|
|
for (int slot_id = 0; slot_id < max_buff_slots; ++slot_id) {
|
|
buffs[slot_id].spellid = SPELL_UNKNOWN;
|
|
}
|
|
|
|
const auto& l = CharacterBuffsRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`character_id` = {}",
|
|
client->CharacterID()
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
if (e.slot_id >= max_buff_slots) {
|
|
continue;
|
|
}
|
|
|
|
if (!IsValidSpell(e.spell_id)) {
|
|
continue;
|
|
}
|
|
|
|
Client* c = entity_list.GetClientByName(e.caster_name.c_str());
|
|
|
|
buffs[e.slot_id].spellid = e.spell_id;
|
|
buffs[e.slot_id].casterlevel = e.caster_level;
|
|
|
|
if (c) {
|
|
buffs[e.slot_id].casterid = c->GetID();
|
|
buffs[e.slot_id].client = true;
|
|
|
|
strncpy(buffs[e.slot_id].caster_name, c->GetName(), 64);
|
|
} else {
|
|
buffs[e.slot_id].casterid = 0;
|
|
buffs[e.slot_id].client = false;
|
|
|
|
strncpy(buffs[e.slot_id].caster_name, "", 64);
|
|
}
|
|
|
|
buffs[e.slot_id].ticsremaining = e.ticsremaining;
|
|
buffs[e.slot_id].counters = e.counters;
|
|
buffs[e.slot_id].hit_number = e.numhits;
|
|
buffs[e.slot_id].melee_rune = e.melee_rune;
|
|
buffs[e.slot_id].magic_rune = e.magic_rune;
|
|
buffs[e.slot_id].persistant_buff = e.persistent ? true : false;
|
|
buffs[e.slot_id].dot_rune = e.dot_rune;
|
|
buffs[e.slot_id].caston_x = e.caston_x;
|
|
buffs[e.slot_id].caston_y = e.caston_y;
|
|
buffs[e.slot_id].caston_z = e.caston_z;
|
|
buffs[e.slot_id].ExtraDIChance = e.ExtraDIChance;
|
|
buffs[e.slot_id].RootBreakChance = 0;
|
|
buffs[e.slot_id].virus_spread_time = 0;
|
|
buffs[e.slot_id].UpdateClient = false;
|
|
buffs[e.slot_id].instrument_mod = e.instrument_mod;
|
|
}
|
|
|
|
// We load up to the most our client supports
|
|
max_buff_slots = EQ::spells::StaticLookup(client->ClientVersion())->LongBuffs;
|
|
|
|
for (int slot_id = 0; slot_id < max_buff_slots; ++slot_id) {
|
|
if (!IsValidSpell(buffs[slot_id].spellid)) {
|
|
continue;
|
|
}
|
|
|
|
if (IsEffectInSpell(buffs[slot_id].spellid, SE_Charm)) {
|
|
buffs[slot_id].spellid = SPELL_UNKNOWN;
|
|
break;
|
|
}
|
|
|
|
if (IsEffectInSpell(buffs[slot_id].spellid, SE_Illusion)) {
|
|
if (buffs[slot_id].persistant_buff) {
|
|
break;
|
|
}
|
|
|
|
buffs[slot_id].spellid = SPELL_UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneDatabase::SaveAuras(Client *c)
|
|
{
|
|
CharacterAurasRepository::DeleteOne(database, c->CharacterID());
|
|
|
|
std::vector<CharacterAurasRepository::CharacterAuras> v;
|
|
|
|
auto e = CharacterAurasRepository::NewEntity();
|
|
|
|
const auto& auras = c->GetAuraMgr();
|
|
|
|
for (int slot_id = 0; slot_id < auras.count; ++slot_id) {
|
|
Aura* a = auras.auras[slot_id].aura;
|
|
if (a && a->AuraZones()) {
|
|
e.id = c->CharacterID();
|
|
e.slot = slot_id;
|
|
e.spell_id = a->GetAuraID();
|
|
|
|
v.emplace_back(e);
|
|
}
|
|
}
|
|
|
|
if (!v.empty()) {
|
|
CharacterAurasRepository::ReplaceMany(database, v);
|
|
}
|
|
}
|
|
|
|
void ZoneDatabase::LoadAuras(Client *c)
|
|
{
|
|
const auto& l = CharacterAurasRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`id` = {} ORDER BY `slot`",
|
|
c->CharacterID()
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
c->MakeAura(e.spell_id);
|
|
}
|
|
}
|
|
|
|
void ZoneDatabase::SavePetInfo(Client *client)
|
|
{
|
|
PetInfo* p = nullptr;
|
|
|
|
std::vector<CharacterPetInfoRepository::CharacterPetInfo> pet_infos;
|
|
auto pet_info = CharacterPetInfoRepository::NewEntity();
|
|
|
|
std::vector<CharacterPetBuffsRepository::CharacterPetBuffs> pet_buffs;
|
|
auto pet_buff = CharacterPetBuffsRepository::NewEntity();
|
|
|
|
std::vector<CharacterPetInventoryRepository::CharacterPetInventory> inventory;
|
|
auto item = CharacterPetInventoryRepository::NewEntity();
|
|
|
|
for (int pet_info_type = PetInfoType::Current; pet_info_type <= PetInfoType::Suspended; pet_info_type++) {
|
|
p = client->GetPetInfo(pet_info_type);
|
|
if (!p) {
|
|
continue;
|
|
}
|
|
|
|
pet_info.char_id = client->CharacterID();
|
|
pet_info.pet = pet_info_type;
|
|
pet_info.petname = p->Name;
|
|
pet_info.petpower = p->petpower;
|
|
pet_info.spell_id = p->SpellID;
|
|
pet_info.hp = p->HP;
|
|
pet_info.mana = p->Mana;
|
|
pet_info.size = p->size;
|
|
pet_info.taunting = p->taunting ? 1 : 0;
|
|
|
|
pet_infos.push_back(pet_info);
|
|
|
|
uint32 pet_buff_count = 0;
|
|
|
|
const uint32 max_slots = (
|
|
RuleI(Spells, MaxTotalSlotsPET) > PET_BUFF_COUNT ?
|
|
PET_BUFF_COUNT :
|
|
RuleI(Spells, MaxTotalSlotsPET)
|
|
);
|
|
|
|
for (int slot_id = 0; slot_id < max_slots; slot_id++) {
|
|
if (!IsValidSpell(p->Buffs[slot_id].spellid)) {
|
|
continue;
|
|
}
|
|
|
|
pet_buff_count++;
|
|
}
|
|
|
|
pet_buffs.reserve(pet_buff_count);
|
|
|
|
for (int slot_id = 0; slot_id < max_slots; slot_id++) {
|
|
if (!IsValidSpell(p->Buffs[slot_id].spellid)) {
|
|
continue;
|
|
}
|
|
|
|
pet_buff.char_id = client->CharacterID();
|
|
pet_buff.pet = pet_info_type;
|
|
pet_buff.slot = slot_id;
|
|
pet_buff.spell_id = p->Buffs[slot_id].spellid;
|
|
pet_buff.caster_level = p->Buffs[slot_id].level;
|
|
pet_buff.ticsremaining = p->Buffs[slot_id].duration;
|
|
pet_buff.counters = p->Buffs[slot_id].counters;
|
|
pet_buff.instrument_mod = p->Buffs[slot_id].bard_modifier;
|
|
|
|
pet_buffs.push_back(pet_buff);
|
|
}
|
|
|
|
uint32 pet_inventory_count = 0;
|
|
|
|
for (
|
|
int slot_id = EQ::invslot::EQUIPMENT_BEGIN;
|
|
slot_id <= EQ::invslot::EQUIPMENT_END;
|
|
slot_id++
|
|
) {
|
|
if (!p->Items[slot_id]) {
|
|
continue;
|
|
}
|
|
|
|
pet_inventory_count++;
|
|
}
|
|
|
|
inventory.reserve(pet_inventory_count);
|
|
|
|
for (
|
|
int slot_id = EQ::invslot::EQUIPMENT_BEGIN;
|
|
slot_id <= EQ::invslot::EQUIPMENT_END;
|
|
slot_id++
|
|
) {
|
|
if (!p->Items[slot_id]) {
|
|
continue;
|
|
}
|
|
|
|
item.char_id = client->CharacterID();
|
|
item.pet = pet_info_type;
|
|
item.slot = slot_id;
|
|
item.item_id = p->Items[slot_id];
|
|
|
|
inventory.push_back(item);
|
|
}
|
|
}
|
|
|
|
CharacterPetInfoRepository::DeleteWhere(
|
|
database,
|
|
fmt::format(
|
|
"`char_id` = {}",
|
|
client->CharacterID()
|
|
)
|
|
);
|
|
|
|
if (!pet_infos.empty()) {
|
|
CharacterPetInfoRepository::InsertMany(database, pet_infos);
|
|
}
|
|
|
|
CharacterPetBuffsRepository::DeleteWhere(
|
|
database,
|
|
fmt::format(
|
|
"`char_id` = {}",
|
|
client->CharacterID()
|
|
)
|
|
);
|
|
|
|
if (!pet_buffs.empty()) {
|
|
CharacterPetBuffsRepository::InsertMany(database, pet_buffs);
|
|
}
|
|
|
|
CharacterPetInventoryRepository::DeleteWhere(
|
|
database,
|
|
fmt::format(
|
|
"`char_id` = {}",
|
|
client->CharacterID()
|
|
)
|
|
);
|
|
|
|
if (!inventory.empty()) {
|
|
CharacterPetInventoryRepository::InsertMany(database, inventory);
|
|
}
|
|
}
|
|
|
|
void ZoneDatabase::RemoveTempFactions(Client *client) {
|
|
|
|
std::string query = StringFormat("DELETE FROM faction_values "
|
|
"WHERE temp = 1 AND char_id = %u",
|
|
client->CharacterID());
|
|
QueryDatabase(query);
|
|
}
|
|
|
|
void ZoneDatabase::UpdateItemRecast(uint32 character_id, uint32 recast_type, uint32 timestamp)
|
|
{
|
|
CharacterItemRecastRepository::ReplaceOne(
|
|
*this,
|
|
CharacterItemRecastRepository::CharacterItemRecast{
|
|
.id = character_id,
|
|
.recast_type = recast_type,
|
|
.timestamp = timestamp,
|
|
}
|
|
);
|
|
}
|
|
|
|
void ZoneDatabase::DeleteItemRecast(uint32 character_id, uint32 recast_type)
|
|
{
|
|
CharacterItemRecastRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`id` = {} AND `recast_type` = {}",
|
|
character_id,
|
|
recast_type
|
|
)
|
|
);
|
|
}
|
|
|
|
void ZoneDatabase::LoadPetInfo(Client *client)
|
|
{
|
|
// Load current pet and suspended pet
|
|
auto pet_info = client->GetPetInfo(PetInfoType::Current);
|
|
auto suspended_pet_info = client->GetPetInfo(PetInfoType::Suspended);
|
|
|
|
memset(pet_info, 0, sizeof(PetInfo));
|
|
memset(suspended_pet_info, 0, sizeof(PetInfo));
|
|
|
|
const auto& info = CharacterPetInfoRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`char_id` = {}",
|
|
client->CharacterID()
|
|
)
|
|
);
|
|
|
|
if (info.empty()) {
|
|
return;
|
|
}
|
|
|
|
PetInfo* p;
|
|
|
|
for (const auto& e : info) {
|
|
if (e.pet == PetInfoType::Current) {
|
|
p = pet_info;
|
|
} else if (e.pet == PetInfoType::Suspended) {
|
|
p = suspended_pet_info;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
strn0cpy(p->Name, e.petname.c_str(), sizeof(p->Name));
|
|
|
|
p->petpower = e.petpower;
|
|
p->SpellID = e.spell_id;
|
|
p->HP = e.hp;
|
|
p->Mana = e.mana;
|
|
p->size = e.size;
|
|
p->taunting = e.taunting;
|
|
}
|
|
|
|
const auto& buffs = CharacterPetBuffsRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`char_id` = {}",
|
|
client->CharacterID()
|
|
)
|
|
);
|
|
|
|
if (!buffs.empty()) {
|
|
for (const auto& e : buffs) {
|
|
if (e.pet == PetInfoType::Current) {
|
|
p = pet_info;
|
|
} else if (e.pet == PetInfoType::Suspended) {
|
|
p = suspended_pet_info;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
if (e.slot >= RuleI(Spells, MaxTotalSlotsPET)) {
|
|
continue;
|
|
}
|
|
|
|
if (!IsValidSpell(e.spell_id)) {
|
|
continue;
|
|
}
|
|
|
|
p->Buffs[e.slot].spellid = e.spell_id;
|
|
p->Buffs[e.slot].level = e.caster_level;
|
|
p->Buffs[e.slot].player_id = 0;
|
|
p->Buffs[e.slot].effect_type = BuffEffectType::Buff;
|
|
p->Buffs[e.slot].duration = e.ticsremaining;
|
|
p->Buffs[e.slot].counters = e.counters;
|
|
p->Buffs[e.slot].bard_modifier = e.instrument_mod;
|
|
}
|
|
}
|
|
|
|
const auto& inventory = CharacterPetInventoryRepository::GetWhere(
|
|
database,
|
|
fmt::format(
|
|
"`char_id` = {}",
|
|
client->CharacterID()
|
|
)
|
|
);
|
|
|
|
if (!inventory.empty()) {
|
|
for (const auto& e : inventory) {
|
|
if (e.pet == PetInfoType::Current) {
|
|
p = pet_info;
|
|
} else if (e.pet == PetInfoType::Suspended) {
|
|
p = suspended_pet_info;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
if (!EQ::ValueWithin(e.slot, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END)) {
|
|
continue;
|
|
}
|
|
|
|
p->Items[e.slot] = e.item_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id) {
|
|
if (faction_id <= 0 || faction_id > (int32) max_faction)
|
|
return false;
|
|
|
|
if (faction_array[faction_id] == 0){
|
|
return false;
|
|
}
|
|
|
|
fm->base = faction_array[faction_id]->base;
|
|
fm->min = faction_array[faction_id]->min; // The lowest your personal earned faction can go - before race/class/deity adjustments.
|
|
fm->max = faction_array[faction_id]->max; // The highest your personal earned faction can go - before race/class/deity adjustments.
|
|
|
|
if(class_mod > 0) {
|
|
char str[32];
|
|
sprintf(str, "c%u", class_mod);
|
|
|
|
std::map<std::string, int16>::const_iterator iter = faction_array[faction_id]->mods.find(str);
|
|
if(iter != faction_array[faction_id]->mods.end()) {
|
|
fm->class_mod = iter->second;
|
|
} else {
|
|
fm->class_mod = 0;
|
|
}
|
|
} else {
|
|
fm->class_mod = 0;
|
|
}
|
|
|
|
if(race_mod > 0) {
|
|
char str[32];
|
|
sprintf(str, "r%u", race_mod);
|
|
|
|
auto iter = faction_array[faction_id]->mods.find(str);
|
|
if(iter != faction_array[faction_id]->mods.end()) {
|
|
fm->race_mod = iter->second;
|
|
} else {
|
|
fm->race_mod = 0;
|
|
}
|
|
} else {
|
|
fm->race_mod = 0;
|
|
}
|
|
|
|
if(deity_mod > 0) {
|
|
char str[32];
|
|
sprintf(str, "d%u", deity_mod);
|
|
|
|
auto iter = faction_array[faction_id]->mods.find(str);
|
|
if(iter != faction_array[faction_id]->mods.end()) {
|
|
fm->deity_mod = iter->second;
|
|
} else {
|
|
fm->deity_mod = 0;
|
|
}
|
|
} else {
|
|
fm->deity_mod = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//o--------------------------------------------------------------
|
|
//| Name: GetFactionName; Dec. 16
|
|
//o--------------------------------------------------------------
|
|
//| Notes: Retrieves the name of the specified faction .Returns false on failure.
|
|
//o--------------------------------------------------------------
|
|
bool ZoneDatabase::GetFactionName(int32 faction_id, char* name, uint32 buflen) {
|
|
if ((faction_id <= 0) || faction_id > int32(max_faction) ||(faction_array[faction_id] == 0))
|
|
return false;
|
|
if (faction_array[faction_id]->name[0] != 0) {
|
|
strn0cpy(name, faction_array[faction_id]->name, buflen);
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
std::string ZoneDatabase::GetFactionName(int32 faction_id)
|
|
{
|
|
std::string faction_name;
|
|
if (
|
|
faction_id <= 0 ||
|
|
faction_id > static_cast<int>(max_faction) ||
|
|
!faction_array[faction_id]
|
|
) {
|
|
return faction_name;
|
|
}
|
|
|
|
faction_name = faction_array[faction_id]->name;
|
|
|
|
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--------------------------------------------------------------
|
|
//| Purpose: Update characters faction level with specified faction_id to specified value. Returns false on failure.
|
|
//o--------------------------------------------------------------
|
|
bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list)
|
|
{
|
|
|
|
std::string query;
|
|
|
|
if(temp == 2)
|
|
temp = 0;
|
|
|
|
if(temp == 3)
|
|
temp = 1;
|
|
|
|
query = StringFormat("INSERT INTO `faction_values` "
|
|
"(`char_id`, `faction_id`, `current_value`, `temp`) "
|
|
"VALUES (%i, %i, %i, %i) "
|
|
"ON DUPLICATE KEY UPDATE `current_value`=%i,`temp`=%i",
|
|
char_id, faction_id, value, temp, value, temp);
|
|
auto results = QueryDatabase(query);
|
|
|
|
if (!results.Success())
|
|
return false;
|
|
else
|
|
val_list[faction_id] = value;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadFactionData()
|
|
{
|
|
std::string query("SELECT MAX(`id`) FROM `faction_list`");
|
|
|
|
auto faction_max_results = QueryDatabase(query);
|
|
if (!faction_max_results.Success() || faction_max_results.RowCount() == 0) {
|
|
return false;
|
|
}
|
|
|
|
auto& fmr_row = faction_max_results.begin();
|
|
|
|
max_faction = Strings::ToUnsignedInt(fmr_row[0]);
|
|
faction_array = new Faction *[max_faction + 1];
|
|
|
|
memset(faction_array, 0, (sizeof(Faction*) * (max_faction + 1)));
|
|
|
|
std::vector<std::string> faction_ids;
|
|
|
|
// load factions
|
|
query = "SELECT `id`, `name`, `base` FROM `faction_list`";
|
|
|
|
auto faction_results = QueryDatabase(query);
|
|
if (!faction_results.Success()) {
|
|
return false;
|
|
}
|
|
|
|
for (auto fr_row : faction_results) {
|
|
|
|
uint32 index = Strings::ToUnsignedInt(fr_row[0]);
|
|
if (index > max_faction) {
|
|
Log(Logs::General, Logs::Error, "Faction '%u' is out-of-bounds for faction array size!", index);
|
|
continue;
|
|
}
|
|
|
|
// this should never hit since `id` is keyed..but, it alleviates any risk of lost pointers
|
|
if (faction_array[index] != nullptr) {
|
|
Log(Logs::General, Logs::Error, "Faction '%u' has already been assigned! (Duplicate Entry)", index);
|
|
continue;
|
|
}
|
|
|
|
faction_array[index] = new Faction;
|
|
strn0cpy(faction_array[index]->name, fr_row[1], 50);
|
|
faction_array[index]->base = Strings::ToInt(fr_row[2]);
|
|
faction_array[index]->min = MIN_PERSONAL_FACTION;
|
|
faction_array[index]->max = MAX_PERSONAL_FACTION;
|
|
|
|
faction_ids.push_back(fr_row[0]);
|
|
}
|
|
|
|
LogInfo("Loaded [{}] faction(s)", Strings::Commify(std::to_string(faction_ids.size())));
|
|
|
|
const std::string faction_id_criteria(Strings::Implode(",", faction_ids));
|
|
|
|
// load faction mins/maxes
|
|
query = fmt::format("SELECT `client_faction_id`, `min`, `max` FROM `faction_base_data` WHERE `client_faction_id` IN ({})", faction_id_criteria);
|
|
|
|
auto base_results = QueryDatabase(query);
|
|
if (base_results.Success()) {
|
|
|
|
for (auto br_row : base_results) {
|
|
|
|
uint32 index = Strings::ToUnsignedInt(br_row[0]);
|
|
if (index > max_faction) {
|
|
LogError("Faction [{}] is out-of-bounds for faction array size in Base adjustment!", index);
|
|
continue;
|
|
}
|
|
|
|
if (faction_array[index] == nullptr) {
|
|
LogError("Faction [{}] does not exist for Base adjustment!", index);
|
|
continue;
|
|
}
|
|
|
|
faction_array[index]->min = Strings::ToInt(br_row[1]);
|
|
faction_array[index]->max = Strings::ToInt(br_row[2]);
|
|
}
|
|
|
|
LogInfo("Loaded [{}] faction base(s)", Strings::Commify(std::to_string(base_results.RowCount())));
|
|
}
|
|
else {
|
|
LogInfo("Unable to load Faction Base data...");
|
|
}
|
|
|
|
// load race, class and deity modifiers
|
|
query = fmt::format("SELECT `faction_id`, `mod`, `mod_name` FROM `faction_list_mod` WHERE `faction_id` IN ({})", faction_id_criteria);
|
|
|
|
auto modifier_results = QueryDatabase(query);
|
|
if (modifier_results.Success()) {
|
|
|
|
for (auto mr_row : modifier_results) {
|
|
|
|
uint32 index = Strings::ToUnsignedInt(mr_row[0]);
|
|
if (index > max_faction) {
|
|
Log(Logs::General, Logs::Error, "Faction '%u' is out-of-bounds for faction array size in Modifier adjustment!", index);
|
|
continue;
|
|
}
|
|
|
|
if (faction_array[index] == nullptr) {
|
|
Log(Logs::General, Logs::Error, "Faction '%u' does not exist for Modifier adjustment!", index);
|
|
continue;
|
|
}
|
|
|
|
faction_array[index]->mods[mr_row[2]] = Strings::ToInt(mr_row[1]);
|
|
}
|
|
|
|
LogInfo("Loaded [{}] faction modifier(s)", Strings::Commify(std::to_string(modifier_results.RowCount())));
|
|
}
|
|
else {
|
|
LogError("Unable to load Faction Modifier data");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uint32 ZoneDatabase::SendCharacterCorpseToGraveyard(
|
|
uint32 corpse_id,
|
|
uint32 zone_id,
|
|
uint16 instance_id,
|
|
glm::vec4& position
|
|
)
|
|
{
|
|
position.x += zone->random.Real(-20, 20);
|
|
position.y += zone->random.Real(-20, 20);
|
|
|
|
return CharacterCorpsesRepository::SendToGraveyard(*this, corpse_id, zone_id, instance_id, position);
|
|
}
|
|
|
|
void ZoneDatabase::SendCharacterCorpseToNonInstance(uint32 corpse_id)
|
|
{
|
|
CharacterCorpsesRepository::SendToNonInstance(*this, corpse_id);
|
|
}
|
|
|
|
uint32 ZoneDatabase::GetCharacterCorpseDecayTimer(uint32 corpse_id)
|
|
{
|
|
return CharacterCorpsesRepository::GetDecayTimer(*this, corpse_id);
|
|
}
|
|
|
|
uint32 ZoneDatabase::UpdateCharacterCorpse(
|
|
uint32 corpse_id,
|
|
uint32 character_id,
|
|
const std::string& name,
|
|
uint32 zone_id,
|
|
uint16 instance_id,
|
|
const CharacterCorpseEntry& c,
|
|
const glm::vec4& position,
|
|
uint32 guild_consent_id,
|
|
bool is_resurrected
|
|
)
|
|
{
|
|
auto e = CharacterCorpsesRepository::FindOne(*this, corpse_id);
|
|
|
|
e.charname = name;
|
|
e.zone_id = zone_id;
|
|
e.instance_id = instance_id;
|
|
e.charid = character_id;
|
|
e.x = position.x;
|
|
e.y = position.y;
|
|
e.z = position.z;
|
|
e.heading = position.w;
|
|
e.guild_consent_id = guild_consent_id;
|
|
e.is_locked = c.locked;
|
|
e.exp = c.exp;
|
|
e.size = c.size;
|
|
e.level = c.level;
|
|
e.race = c.race;
|
|
e.gender = c.gender;
|
|
e.class_ = c.class_;
|
|
e.deity = c.deity;
|
|
e.texture = c.texture;
|
|
e.helm_texture = c.helmtexture;
|
|
e.copper = c.copper;
|
|
e.silver = c.silver;
|
|
e.gold = c.gold;
|
|
e.platinum = c.plat;
|
|
e.hair_color = c.haircolor;
|
|
e.beard_color = c.beardcolor;
|
|
e.eye_color_1 = c.eyecolor1;
|
|
e.eye_color_2 = c.eyecolor2;
|
|
e.hair_style = c.hairstyle;
|
|
e.face = c.face;
|
|
e.beard = c.beard;
|
|
e.drakkin_heritage = c.drakkin_heritage;
|
|
e.drakkin_details = c.drakkin_details;
|
|
e.drakkin_tattoo = c.drakkin_tattoo;
|
|
e.wc_1 = c.item_tint.Head.Color;
|
|
e.wc_2 = c.item_tint.Chest.Color;
|
|
e.wc_3 = c.item_tint.Arms.Color;
|
|
e.wc_4 = c.item_tint.Wrist.Color;
|
|
e.wc_5 = c.item_tint.Hands.Color;
|
|
e.wc_6 = c.item_tint.Legs.Color;
|
|
e.wc_7 = c.item_tint.Feet.Color;
|
|
e.wc_8 = c.item_tint.Primary.Color;
|
|
e.wc_9 = c.item_tint.Secondary.Color;
|
|
|
|
CharacterCorpsesRepository::UpdateOne(*this, e);
|
|
|
|
return corpse_id;
|
|
}
|
|
|
|
uint32 ZoneDatabase::UpdateCharacterCorpseConsent(uint32 character_id, uint32 guild_consent_id)
|
|
{
|
|
return CharacterCorpsesRepository::SetGuildConsentID(*this, character_id, guild_consent_id);
|
|
}
|
|
|
|
void ZoneDatabase::MarkCorpseAsResurrected(uint32 corpse_id)
|
|
{
|
|
CharacterCorpsesRepository::ResurrectCorpse(*this, corpse_id);
|
|
}
|
|
|
|
uint32 ZoneDatabase::SaveCharacterCorpse(
|
|
uint32 character_id,
|
|
const std::string& name,
|
|
uint32 zone_id,
|
|
uint16 instance_id,
|
|
const CharacterCorpseEntry& corpse,
|
|
const glm::vec4& position,
|
|
uint32 guild_consent_id
|
|
)
|
|
{
|
|
auto e = CharacterCorpsesRepository::NewEntity();
|
|
|
|
e.charname = name;
|
|
e.zone_id = zone_id;
|
|
e.instance_id = instance_id;
|
|
e.charid = character_id;
|
|
e.x = position.x;
|
|
e.y = position.y;
|
|
e.z = position.z;
|
|
e.heading = position.w;
|
|
e.guild_consent_id = guild_consent_id;
|
|
e.is_locked = corpse.locked;
|
|
e.exp = corpse.exp;
|
|
e.size = corpse.size;
|
|
e.level = corpse.level;
|
|
e.race = corpse.race;
|
|
e.gender = corpse.gender;
|
|
e.class_ = corpse.class_;
|
|
e.deity = corpse.deity;
|
|
e.texture = corpse.texture;
|
|
e.helm_texture = corpse.helmtexture;
|
|
e.copper = corpse.copper;
|
|
e.silver = corpse.silver;
|
|
e.gold = corpse.gold;
|
|
e.platinum = corpse.plat;
|
|
e.hair_color = corpse.haircolor;
|
|
e.beard_color = corpse.beardcolor;
|
|
e.eye_color_1 = corpse.eyecolor1;
|
|
e.eye_color_2 = corpse.eyecolor2;
|
|
e.hair_style = corpse.hairstyle;
|
|
e.face = corpse.face;
|
|
e.beard = corpse.beard;
|
|
e.drakkin_heritage = corpse.drakkin_heritage;
|
|
e.drakkin_tattoo = corpse.drakkin_tattoo;
|
|
e.drakkin_details = corpse.drakkin_details;
|
|
e.wc_1 = corpse.item_tint.Head.Color;
|
|
e.wc_2 = corpse.item_tint.Chest.Color;
|
|
e.wc_3 = corpse.item_tint.Arms.Color;
|
|
e.wc_4 = corpse.item_tint.Wrist.Color;
|
|
e.wc_5 = corpse.item_tint.Hands.Color;
|
|
e.wc_6 = corpse.item_tint.Legs.Color;
|
|
e.wc_7 = corpse.item_tint.Feet.Color;
|
|
e.wc_8 = corpse.item_tint.Primary.Color;
|
|
e.wc_9 = corpse.item_tint.Secondary.Color;
|
|
|
|
e = CharacterCorpsesRepository::InsertOne(*this, e);
|
|
|
|
std::vector<CharacterCorpseItemsRepository::CharacterCorpseItems> v;
|
|
|
|
v.reserve(corpse.items.size());
|
|
|
|
auto i = CharacterCorpseItemsRepository::NewEntity();
|
|
|
|
for (const auto& item : corpse.items) {
|
|
i.corpse_id = e.id;
|
|
i.equip_slot = item.equip_slot;
|
|
i.item_id = item.item_id;
|
|
i.charges = item.charges;
|
|
i.aug_1 = item.aug_1;
|
|
i.aug_2 = item.aug_2;
|
|
i.aug_3 = item.aug_3;
|
|
i.aug_4 = item.aug_4;
|
|
i.aug_5 = item.aug_5;
|
|
i.aug_6 = item.aug_6;
|
|
i.attuned = item.attuned;
|
|
i.custom_data = item.custom_data;
|
|
i.ornamenticon = item.ornamenticon;
|
|
i.ornamentidfile = item.ornamentidfile;
|
|
i.ornament_hero_model = item.ornament_hero_model;
|
|
|
|
v.emplace_back(i);
|
|
}
|
|
|
|
if (!v.empty()) {
|
|
CharacterCorpseItemsRepository::ReplaceMany(*this, v);
|
|
}
|
|
|
|
return e.id;
|
|
}
|
|
|
|
uint32 ZoneDatabase::GetCharacterBuriedCorpseCount(uint32 character_id)
|
|
{
|
|
return CharacterCorpsesRepository::Count(
|
|
*this,
|
|
fmt::format(
|
|
"`charid` = {} AND `is_buried` = 1",
|
|
character_id
|
|
)
|
|
);
|
|
}
|
|
|
|
int64 ZoneDatabase::GetCharacterCorpseCount(uint32 character_id)
|
|
{
|
|
return CharacterCorpsesRepository::Count(
|
|
*this,
|
|
fmt::format(
|
|
"`charid` = {}",
|
|
character_id
|
|
)
|
|
);
|
|
}
|
|
|
|
uint32 ZoneDatabase::GetCharacterCorpseID(uint32 character_id, uint8 corpse_limit)
|
|
{
|
|
const auto& l = CharacterCorpsesRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`charid` = {} LIMIT {}, 1",
|
|
character_id,
|
|
corpse_limit
|
|
)
|
|
);
|
|
|
|
return l.empty() ? 0 : l[0].id;
|
|
}
|
|
|
|
uint32 ZoneDatabase::GetCharacterCorpseItemAt(uint32 corpse_id, uint16 slot_id)
|
|
{
|
|
Corpse* c = LoadCharacterCorpse(corpse_id);
|
|
uint32 item_id = 0;
|
|
|
|
if (c) {
|
|
item_id = c->GetWornItem(slot_id);
|
|
c->DepopPlayerCorpse();
|
|
}
|
|
|
|
return item_id;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterCorpseData(uint32 corpse_id, CharacterCorpseEntry& corpse)
|
|
{
|
|
const auto& e = CharacterCorpsesRepository::FindOne(*this, corpse_id);
|
|
|
|
corpse.locked = e.is_locked;
|
|
corpse.exp = e.exp;
|
|
corpse.size = e.size;
|
|
corpse.level = e.level;
|
|
corpse.race = e.race;
|
|
corpse.gender = e.gender;
|
|
corpse.class_ = e.class_;
|
|
corpse.deity = e.deity;
|
|
corpse.texture = e.texture;
|
|
corpse.helmtexture = e.helm_texture;
|
|
corpse.copper = e.copper;
|
|
corpse.silver = e.silver;
|
|
corpse.gold = e.gold;
|
|
corpse.plat = e.platinum;
|
|
corpse.haircolor = e.hair_color;
|
|
corpse.beardcolor = e.beard_color;
|
|
corpse.eyecolor1 = e.eye_color_1;
|
|
corpse.eyecolor2 = e.eye_color_2;
|
|
corpse.hairstyle = e.hair_style;
|
|
corpse.face = e.face;
|
|
corpse.beard = e.beard;
|
|
corpse.drakkin_heritage = e.drakkin_heritage;
|
|
corpse.drakkin_tattoo = e.drakkin_tattoo;
|
|
corpse.drakkin_details = e.drakkin_details;
|
|
corpse.item_tint.Head.Color = e.wc_1;
|
|
corpse.item_tint.Chest.Color = e.wc_2;
|
|
corpse.item_tint.Arms.Color = e.wc_3;
|
|
corpse.item_tint.Wrist.Color = e.wc_4;
|
|
corpse.item_tint.Hands.Color = e.wc_5;
|
|
corpse.item_tint.Legs.Color = e.wc_6;
|
|
corpse.item_tint.Feet.Color = e.wc_7;
|
|
corpse.item_tint.Primary.Color = e.wc_8;
|
|
corpse.item_tint.Secondary.Color = e.wc_9;
|
|
|
|
const auto& l = CharacterCorpseItemsRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`corpse_id` = {}",
|
|
corpse_id
|
|
)
|
|
);
|
|
|
|
for (const auto& e : l) {
|
|
CharacterCorpseItemEntry item{
|
|
.item_id = e.item_id,
|
|
.equip_slot = static_cast<int16>(e.equip_slot),
|
|
.charges = static_cast<uint16>(e.charges),
|
|
.aug_1 = e.aug_1,
|
|
.aug_2 = e.aug_2,
|
|
.aug_3 = e.aug_3,
|
|
.aug_4 = e.aug_4,
|
|
.aug_5 = e.aug_5,
|
|
.aug_6 = static_cast<uint32>(e.aug_6),
|
|
.attuned = e.attuned == 1,
|
|
.custom_data = e.custom_data,
|
|
.ornamenticon = e.ornamenticon,
|
|
.ornamentidfile = e.ornamentidfile,
|
|
.ornament_hero_model = e.ornament_hero_model
|
|
};
|
|
|
|
corpse.items.emplace_back(std::move(item));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Corpse* ZoneDatabase::SummonBuriedCharacterCorpses(
|
|
uint32 character_id,
|
|
uint32 zone_id,
|
|
uint16 instance_id,
|
|
const glm::vec4& position
|
|
)
|
|
{
|
|
Corpse* c = nullptr;
|
|
|
|
const auto& l = CharacterCorpsesRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`charid` = {} AND `is_buried` = 1 ORDER BY `time_of_death` LIMIT 1",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
for (const auto& e : l) {
|
|
c = Corpse::LoadCharacterCorpseEntity(
|
|
e.id,
|
|
e.charid,
|
|
e.charname,
|
|
position,
|
|
std::to_string(e.time_of_death),
|
|
e.is_rezzed == 1,
|
|
RuleB(Zone, EnableShadowrest) ? 0 : e.was_at_graveyard,
|
|
e.guild_consent_id
|
|
);
|
|
|
|
if (!c) {
|
|
continue;
|
|
}
|
|
|
|
entity_list.AddCorpse(c);
|
|
c->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS));
|
|
c->Spawn();
|
|
|
|
if (!UnburyCharacterCorpse(c->GetCorpseDBID(), zone_id, instance_id, position)) {
|
|
LogError("Unable to unbury a summoned player corpse_id [{}] for character_id [{}]", e.id, character_id);
|
|
}
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
bool ZoneDatabase::SummonAllCharacterCorpses(
|
|
uint32 character_id,
|
|
uint32 zone_id,
|
|
uint16 instance_id,
|
|
const glm::vec4& position
|
|
)
|
|
{
|
|
Corpse* c = nullptr;
|
|
int64 corpse_count = 0;
|
|
|
|
auto l = CharacterCorpsesRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`charid` = {}",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
for (auto& e : l) {
|
|
e.zone_id = zone_id;
|
|
e.instance_id = instance_id;
|
|
e.x = position.x;
|
|
e.y = position.y;
|
|
e.z = position.z;
|
|
e.heading = position.w;
|
|
e.is_buried = 0;
|
|
e.was_at_graveyard = 0;
|
|
|
|
c = Corpse::LoadCharacterCorpseEntity(
|
|
e.id,
|
|
e.charid,
|
|
e.charname,
|
|
position,
|
|
std::to_string(e.time_of_death),
|
|
e.is_rezzed == 1,
|
|
RuleB(Zone, EnableShadowrest) ? 0 : e.was_at_graveyard,
|
|
e.guild_consent_id
|
|
);
|
|
|
|
if (c) {
|
|
entity_list.AddCorpse(c);
|
|
c->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS));
|
|
c->Spawn();
|
|
++corpse_count;
|
|
} else {
|
|
LogError("Unable to construct a player corpse_id [{}] for character_id [{}]", e.id, character_id);
|
|
}
|
|
}
|
|
|
|
CharacterCorpsesRepository::ReplaceMany(*this, l);
|
|
|
|
return corpse_count > 0;
|
|
}
|
|
|
|
int64 ZoneDatabase::CountCharacterCorpses(uint32 character_id)
|
|
{
|
|
return CharacterCorpsesRepository::Count(
|
|
*this,
|
|
fmt::format(
|
|
"`charid` = {}",
|
|
character_id
|
|
)
|
|
);
|
|
}
|
|
|
|
int64 ZoneDatabase::CountCharacterCorpsesByZoneID(uint32 character_id, uint32 zone_id)
|
|
{
|
|
return CharacterCorpsesRepository::Count(
|
|
*this,
|
|
fmt::format(
|
|
"`charid` = {} AND `zone_id` = {}",
|
|
character_id,
|
|
zone_id
|
|
)
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::UnburyCharacterCorpse(uint32 corpse_id, uint32 zone_id, uint16 instance_id, const glm::vec4& position)
|
|
{
|
|
return CharacterCorpsesRepository::UnburyCorpse(*this, corpse_id, zone_id, instance_id, position);
|
|
}
|
|
|
|
Corpse* ZoneDatabase::LoadCharacterCorpse(uint32 corpse_id)
|
|
{
|
|
const auto& e = CharacterCorpsesRepository::FindOne(*this, corpse_id);
|
|
|
|
Corpse* c = 0;
|
|
|
|
if (!e.id) {
|
|
return c;
|
|
}
|
|
|
|
glm::vec4 position = glm::vec4(e.x, e.y, e.z, e.heading);
|
|
|
|
c = Corpse::LoadCharacterCorpseEntity(
|
|
e.id,
|
|
e.charid,
|
|
e.charname,
|
|
position,
|
|
std::to_string(e.time_of_death),
|
|
e.is_rezzed == 1,
|
|
RuleB(Zone, EnableShadowrest) ? 0 : e.was_at_graveyard,
|
|
e.guild_consent_id
|
|
);
|
|
|
|
entity_list.AddCorpse(c);
|
|
|
|
return c;
|
|
}
|
|
|
|
bool ZoneDatabase::LoadCharacterCorpses(uint32 zone_id, uint16 instance_id)
|
|
{
|
|
const auto& l = CharacterCorpsesRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`zone_id` = {} AND `instance_id` = {}{}",
|
|
zone_id,
|
|
instance_id,
|
|
RuleB(Zone, EnableShadowrest) ? " AND `is_buried` = 0" : ""
|
|
)
|
|
);
|
|
|
|
for (const auto& e : l) {
|
|
glm::vec4 position = glm::vec4(e.x, e.y, e.z, e.heading);
|
|
|
|
entity_list.AddCorpse(
|
|
Corpse::LoadCharacterCorpseEntity(
|
|
e.id,
|
|
e.charid,
|
|
e.charname,
|
|
position,
|
|
std::to_string(e.time_of_death),
|
|
e.is_rezzed == 1,
|
|
RuleB(Zone, EnableShadowrest) ? 0 : e.was_at_graveyard,
|
|
e.guild_consent_id
|
|
)
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32 ZoneDatabase::GetFirstCorpseID(uint32 character_id)
|
|
{
|
|
const auto& l = CharacterCorpsesRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`charid` = {} AND `is_buried` = 0 ORDER BY `time_of_death` LIMIT 1",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return 0;
|
|
}
|
|
|
|
return l[0].id;
|
|
}
|
|
|
|
bool ZoneDatabase::DeleteItemOffCharacterCorpse(uint32 corpse_id, uint32 slot_id, uint32 item_id)
|
|
{
|
|
return CharacterCorpseItemsRepository::DeleteWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`corpse_id` = {} AND `equip_slot` = {} AND `item_id` = {}",
|
|
corpse_id,
|
|
slot_id,
|
|
item_id
|
|
)
|
|
);
|
|
}
|
|
|
|
bool ZoneDatabase::BuryCharacterCorpse(uint32 corpse_id)
|
|
{
|
|
return CharacterCorpsesRepository::BuryCorpse(*this, corpse_id);
|
|
}
|
|
|
|
bool ZoneDatabase::BuryAllCharacterCorpses(uint32 character_id)
|
|
{
|
|
const auto& l = CharacterCorpsesRepository::GetWhere(
|
|
*this,
|
|
fmt::format(
|
|
"`charid` = {}",
|
|
character_id
|
|
)
|
|
);
|
|
|
|
if (l.empty()) {
|
|
return false;
|
|
}
|
|
|
|
for (const auto& e : l) {
|
|
BuryCharacterCorpse(e.id);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ZoneDatabase::DeleteCharacterCorpse(uint32 corpse_id)
|
|
{
|
|
return CharacterCorpsesRepository::DeleteOne(*this, corpse_id);
|
|
}
|
|
|
|
double ZoneDatabase::GetAAEXPModifier(uint32 character_id, uint32 zone_id, int16 instance_version) const {
|
|
const std::string query = fmt::format(
|
|
SQL(
|
|
SELECT
|
|
`aa_modifier`
|
|
FROM
|
|
`character_exp_modifiers`
|
|
WHERE
|
|
`character_id` = {}
|
|
AND
|
|
(`zone_id` = {} OR `zone_id` = 0) AND
|
|
(`instance_version` = {} OR `instance_version` = -1)
|
|
ORDER BY `zone_id`, `instance_version` DESC
|
|
LIMIT 1
|
|
),
|
|
character_id,
|
|
zone_id,
|
|
instance_version
|
|
);
|
|
|
|
auto results = database.QueryDatabase(query);
|
|
for (auto& row = results.begin(); row != results.end(); ++row) {
|
|
return Strings::ToFloat(row[0]);
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
double ZoneDatabase::GetEXPModifier(uint32 character_id, uint32 zone_id, int16 instance_version) const {
|
|
const std::string query = fmt::format(
|
|
SQL(
|
|
SELECT
|
|
`exp_modifier`
|
|
FROM
|
|
`character_exp_modifiers`
|
|
WHERE
|
|
`character_id` = {}
|
|
AND
|
|
(`zone_id` = {} OR `zone_id` = 0) AND
|
|
(`instance_version` = {} OR `instance_version` = -1)
|
|
ORDER BY `zone_id`, `instance_version` DESC
|
|
LIMIT 1
|
|
),
|
|
character_id,
|
|
zone_id,
|
|
instance_version
|
|
);
|
|
|
|
auto results = database.QueryDatabase(query);
|
|
for (auto& row = results.begin(); row != results.end(); ++row) {
|
|
return Strings::ToFloat(row[0]);
|
|
}
|
|
|
|
return 1.0f;
|
|
}
|
|
|
|
void ZoneDatabase::SetAAEXPModifier(uint32 character_id, uint32 zone_id, double aa_modifier, int16 instance_version) {
|
|
float exp_modifier = GetEXPModifier(character_id, zone_id, instance_version);
|
|
std::string query = fmt::format(
|
|
SQL(
|
|
REPLACE INTO
|
|
`character_exp_modifiers`
|
|
VALUES
|
|
({}, {}, {}, {}, {})
|
|
),
|
|
character_id,
|
|
zone_id,
|
|
instance_version,
|
|
aa_modifier,
|
|
exp_modifier
|
|
);
|
|
database.QueryDatabase(query);
|
|
}
|
|
|
|
void ZoneDatabase::SetEXPModifier(uint32 character_id, uint32 zone_id, double exp_modifier, int16 instance_version) {
|
|
float aa_modifier = GetAAEXPModifier(character_id, zone_id, instance_version);
|
|
std::string query = fmt::format(
|
|
SQL(
|
|
REPLACE INTO
|
|
`character_exp_modifiers`
|
|
VALUES
|
|
({}, {}, {}, {}, {})
|
|
),
|
|
character_id,
|
|
zone_id,
|
|
instance_version,
|
|
aa_modifier,
|
|
exp_modifier
|
|
);
|
|
database.QueryDatabase(query);
|
|
}
|
|
|
|
void ZoneDatabase::UpdateGMStatus(uint32 account_id, int new_status)
|
|
{
|
|
auto e = AccountRepository::FindOne(*this, account_id);
|
|
if (!e.id) {
|
|
return;
|
|
}
|
|
|
|
e.status = new_status;
|
|
|
|
AccountRepository::UpdateOne(*this, e);
|
|
}
|
|
|
|
void ZoneDatabase::SaveCharacterBinds(Client *c)
|
|
{
|
|
std::vector<CharacterBindRepository::CharacterBind> v;
|
|
|
|
auto e = CharacterBindRepository::NewEntity();
|
|
|
|
uint32 bind_count = 0;
|
|
for (const auto &b : c->GetPP().binds) {
|
|
if (b.zone_id) {
|
|
bind_count++;
|
|
}
|
|
}
|
|
|
|
v.reserve(bind_count);
|
|
|
|
int slot_id = 0;
|
|
|
|
for (const auto &b : c->GetPP().binds) {
|
|
if (b.zone_id) {
|
|
e.id = c->CharacterID();
|
|
e.zone_id = b.zone_id;
|
|
e.instance_id = b.instance_id;
|
|
e.x = b.x;
|
|
e.y = b.y;
|
|
e.z = b.z;
|
|
e.heading = b.heading;
|
|
e.slot = slot_id;
|
|
|
|
v.emplace_back(e);
|
|
|
|
slot_id++;
|
|
}
|
|
}
|
|
|
|
if (bind_count > 0) {
|
|
CharacterBindRepository::ReplaceMany(database, v);
|
|
}
|
|
}
|
|
|
|
void ZoneDatabase::ZeroPlayerProfileCurrency(PlayerProfile_Struct* pp)
|
|
{
|
|
if (pp->copper < 0) {
|
|
pp->copper = 0;
|
|
}
|
|
|
|
if (pp->silver < 0) {
|
|
pp->silver = 0;
|
|
}
|
|
|
|
if (pp->gold < 0) {
|
|
pp->gold = 0;
|
|
}
|
|
|
|
if (pp->platinum < 0) {
|
|
pp->platinum = 0;
|
|
}
|
|
|
|
if (pp->copper_bank < 0) {
|
|
pp->copper_bank = 0;
|
|
}
|
|
|
|
if (pp->silver_bank < 0) {
|
|
pp->silver_bank = 0;
|
|
}
|
|
|
|
if (pp->gold_bank < 0) {
|
|
pp->gold_bank = 0;
|
|
}
|
|
|
|
if (pp->platinum_bank < 0) {
|
|
pp->platinum_bank = 0;
|
|
}
|
|
|
|
if (pp->platinum_cursor < 0) {
|
|
pp->platinum_cursor = 0;
|
|
}
|
|
|
|
if (pp->gold_cursor < 0) {
|
|
pp->gold_cursor = 0;
|
|
}
|
|
|
|
if (pp->silver_cursor < 0) {
|
|
pp->silver_cursor = 0;
|
|
}
|
|
|
|
if (pp->copper_cursor < 0) {
|
|
pp->copper_cursor = 0;
|
|
}
|
|
}
|