eqemu-server/zone/zonedb.cpp
2014-08-21 15:32:20 -07:00

2580 lines
97 KiB
C++

#include "zonedb.h"
#include "../common/Item.h"
#include "../common/StringUtil.h"
#include "../common/extprofile.h"
#include "../common/guilds.h"
#include "../common/rulesys.h"
#include "zone.h"
#include "client.h"
#include "merc.h"
#include "groups.h"
#include "raids.h"
#include <iostream>
#include <string>
#include <sstream>
extern Zone* zone;
ZoneDatabase database;
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() {
memset(door_isopen_array, 0, sizeof(door_isopen_array));
npc_spells_maxid = 0;
npc_spellseffects_maxid = 0;
npc_spells_cache = 0;
npc_spellseffects_cache = 0;
npc_spells_loadtried = 0;
npc_spellseffects_loadtried = 0;
max_faction = 0;
faction_array = nullptr;
}
ZoneDatabase::~ZoneDatabase() {
unsigned int x;
if (npc_spells_cache) {
for (x=0; x<=npc_spells_maxid; x++) {
safe_delete_array(npc_spells_cache[x]);
}
safe_delete_array(npc_spells_cache);
}
safe_delete_array(npc_spells_loadtried);
if (npc_spellseffects_cache) {
for (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 (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 zoneid, uint16 instance_id, NewZone_Struct* zd) {
std::string query = StringFormat("UPDATE zone SET underworld = %f, minclip = %f, "
"maxclip = %f, fog_minclip = %f, fog_maxclip = %f, "
"fog_blue = %i, fog_red = %i, fog_green = %i, "
"sky = %i, ztype = %i, zone_exp_multiplier = %f, "
"safe_x = %f, safe_y = %f, safe_z = %f "
"WHERE zoneidnumber = %i AND version = %i",
zd->underworld, zd->minclip,
zd->maxclip, zd->fog_minclip[0], zd->fog_maxclip[0],
zd->fog_blue[0], zd->fog_red[0], zd->fog_green[0],
zd->sky, zd->ztype, zd->zone_exp_multiplier,
zd->safe_x, zd->safe_y, zd->safe_z,
zoneid, instance_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in SaveZoneCFG query %s: %s", query.c_str(), results.ErrorMessage().c_str());
return false;
}
return true;
}
bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct *zone_data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &allow_mercs, uint8 &zone_type, int &ruleset, char **map_filename) {
*map_filename = new char[100];
zone_data->zone_id = zoneid;
std::string query = StringFormat("SELECT ztype, fog_red, fog_green, fog_blue, fog_minclip, fog_maxclip, " // 5
"fog_red2, fog_green2, fog_blue2, fog_minclip2, fog_maxclip2, " // 5
"fog_red3, fog_green3, fog_blue3, fog_minclip3, fog_maxclip3, " // 5
"fog_red4, fog_green4, fog_blue4, fog_minclip4, fog_maxclip4, " // 5
"fog_density, sky, zone_exp_multiplier, safe_x, safe_y, safe_z, underworld, " // 7
"minclip, maxclip, time_type, canbind, cancombat, canlevitate, " // 6
"castoutdoor, hotzone, ruleset, suspendbuffs, map_file_name, short_name, " // 6
"rain_chance1, rain_chance2, rain_chance3, rain_chance4, " // 4
"rain_duration1, rain_duration2, rain_duration3, rain_duration4, " // 4
"snow_chance1, snow_chance2, snow_chance3, snow_chance4, " // 4
"snow_duration1, snow_duration2, snow_duration3, snow_duration4 " // 4
"FROM zone WHERE zoneidnumber = %i AND version = %i", zoneid, instance_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetZoneCFG query %s: %s", query.c_str(), results.ErrorMessage().c_str());
strcpy(*map_filename, "default");
return false;
}
if (results.RowCount() == 0) {
strcpy(*map_filename, "default");
return false;
}
auto row = results.begin();
memset(zone_data, 0, sizeof(NewZone_Struct));
zone_data->ztype = atoi(row[0]);
zone_type = zone_data->ztype;
int index;
for(index = 0; index < 4; index++) {
zone_data->fog_red[index]=atoi(row[1 + index * 5]);
zone_data->fog_green[index]=atoi(row[2 + index * 5]);
zone_data->fog_blue[index]=atoi(row[3 + index * 5]);
zone_data->fog_minclip[index]=atof(row[4 + index * 5]);
zone_data->fog_maxclip[index]=atof(row[5 + index * 5]);
}
zone_data->fog_density = atof(row[22]);
zone_data->sky=atoi(row[23]);
zone_data->zone_exp_multiplier=atof(row[24]);
zone_data->safe_x=atof(row[25]);
zone_data->safe_y=atof(row[26]);
zone_data->safe_z=atof(row[27]);
zone_data->underworld=atof(row[28]);
zone_data->minclip=atof(row[29]);
zone_data->maxclip=atof(row[30]);
zone_data->time_type=atoi(row[31]);
//not in the DB yet:
zone_data->gravity = 0.4;
allow_mercs = true;
int bindable = 0;
bindable = atoi(row[32]);
can_bind = bindable == 0? false: true;
is_city = bindable == 2? true: false;
can_combat = atoi(row[33]) == 0? false: true;
can_levitate = atoi(row[34]) == 0? false: true;
can_castoutdoor = atoi(row[35]) == 0? false: true;
is_hotzone = atoi(row[36]) == 0? false: true;
ruleset = atoi(row[36]);
zone_data->SuspendBuffs = atoi(row[37]);
char *file = row[38];
if(file)
strcpy(*map_filename, file);
else
strcpy(*map_filename, row[39]);
for(index = 0; index < 4; index++)
zone_data->rain_chance[index]=atoi(row[40 + index]);
for(index = 0; index < 4; index++)
zone_data->rain_duration[index]=atoi(row[44 + index]);
for(index = 0; index < 4; index++)
zone_data->snow_chance[index]=atoi(row[49 + index]);
for(index = 0; index < 4; index++)
zone_data->snow_duration[index]=atof(row[53 + index]);
return true;
}
//updates or clears the respawn time in the database for the current spawn id
void ZoneDatabase::UpdateSpawn2Timeleft(uint32 id, uint16 instance_id, uint32 timeleft)
{
timeval tv;
gettimeofday(&tv, nullptr);
uint32 cur = tv.tv_sec;
//if we pass timeleft as 0 that means we clear from respawn time
//otherwise we update with a REPLACE INTO
if(timeleft == 0) {
std::string query = StringFormat("DELETE FROM respawn_times WHERE id=%lu "
"AND instance_id = %lu",(unsigned long)id, (unsigned long)instance_id);
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
std::string query = StringFormat("REPLACE INTO respawn_times (id, start, duration, instance_id) "
"VALUES (%lu, %lu, %lu, %lu)",
(unsigned long)id, (unsigned long)cur,
(unsigned long)timeleft, (unsigned long)instance_id);
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
//Gets the respawn time left in the database for the current spawn id
uint32 ZoneDatabase::GetSpawnTimeLeft(uint32 id, uint16 instance_id)
{
std::string query = StringFormat("SELECT start, duration FROM respawn_times "
"WHERE id = %lu AND instance_id = %lu",
(unsigned long)id, (unsigned long)zone->GetInstanceID());
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in GetSpawnTimeLeft query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
timeval tv;
gettimeofday(&tv, nullptr);
uint32 resStart = atoi(row[0]);
uint32 resDuration = atoi(row[1]);
//compare our values to current time
if((resStart + resDuration) <= tv.tv_sec) {
//our current time was expired
return 0;
}
//we still have time left on this timer
return ((resStart + resDuration) - tv.tv_sec);
}
void ZoneDatabase::UpdateSpawn2Status(uint32 id, uint8 new_status)
{
std::string query = StringFormat("UPDATE spawn2 SET enabled = %i WHERE id = %lu", new_status, (unsigned long)id);
auto results = QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error in UpdateSpawn2Status query %s: %s", query.c_str(), results.ErrorMessage().c_str());
}
bool ZoneDatabase::logevents(const char* accountname,uint32 accountid,uint8 status,const char* charname, const char* target,const char* descriptiontype, const char* description,int event_nid){
uint32 len = strlen(description);
uint32 len2 = strlen(target);
char* descriptiontext = new char[2*len+1];
char* targetarr = new char[2*len2+1];
memset(descriptiontext, 0, 2*len+1);
memset(targetarr, 0, 2*len2+1);
DoEscapeString(descriptiontext, description, len);
DoEscapeString(targetarr, target, len2);
std::string query = StringFormat("INSERT INTO eventlog (accountname, accountid, status, "
"charname, target, descriptiontype, description, event_nid) "
"VALUES('%s', %i, %i, '%s', '%s', '%s', '%s', '%i')",
accountname, accountid, status, charname, targetarr,
descriptiontype, descriptiontext, event_nid);
safe_delete_array(descriptiontext);
safe_delete_array(targetarr);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in logevents" << query << "' " << results.ErrorMessage() << std::endl;
return false;
}
return true;
}
void ZoneDatabase::UpdateBug(BugStruct* bug) {
uint32 len = strlen(bug->bug);
char* bugtext = nullptr;
if(len > 0)
{
bugtext = new char[2*len+1];
memset(bugtext, 0, 2*len+1);
DoEscapeString(bugtext, bug->bug, len);
}
len = strlen(bug->ui);
char* uitext = nullptr;
if(len > 0)
{
uitext = new char[2*len+1];
memset(uitext, 0, 2*len+1);
DoEscapeString(uitext, bug->ui, len);
}
len = strlen(bug->target_name);
char* targettext = nullptr;
if(len > 0)
{
targettext = new char[2*len+1];
memset(targettext, 0, 2*len+1);
DoEscapeString(targettext, bug->target_name, len);
}
//x and y are intentionally swapped because eq is inversexy coords
std::string query = StringFormat("INSERT INTO bugs (zone, name, ui, x, y, z, type, flag, target, bug, date) "
"VALUES('%s', '%s', '%s', '%.2f', '%.2f', '%.2f', '%s', %d, '%s', '%s', CURDATE())",
zone->GetShortName(), bug->name, uitext == nullptr ? "": uitext,
bug->x, bug->y, bug->z, bug->chartype, bug->type, targettext == nullptr? "Unknown Target": targettext,
bugtext==nullptr?"":bugtext);
safe_delete_array(bugtext);
safe_delete_array(uitext);
safe_delete_array(targettext);
auto results = QueryDatabase(query);
if (!results.Success())
std::cerr << "Error in UpdateBug '" << query << "' " << results.ErrorMessage() << std::endl;
}
void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug){
uint32 len = strlen(bug->text);
char* bugtext = new char[2*len+1];
memset(bugtext, 0, 2*len+1);
DoEscapeString(bugtext, bug->text, len);
std::string query = StringFormat("INSERT INTO bugs (type, name, bugtext, flag) "
"VALUES('%s', '%s', '%s', %i)",
"Petition", bug->name, bugtext, 25);
safe_delete_array(bugtext);
auto results = QueryDatabase(query);
if (!results.Success())
std::cerr << "Error in UpdateBug '" << query << "' " << results.ErrorMessage() << std::endl;
}
bool ZoneDatabase::GetAccountInfoForLogin_result(MYSQL_RES* result, int16* admin, char* account_name, uint32* lsaccountid, uint8* gmspeed, bool* revoked,bool* gmhideme, uint32* account_creation) {
MYSQL_ROW row;
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
if (admin)
*admin = atoi(row[0]);
if (account_name)
strcpy(account_name, row[1]);
if (lsaccountid) {
if (row[2])
*lsaccountid = atoi(row[2]);
else
*lsaccountid = 0;
}
if (gmspeed)
*gmspeed = atoi(row[3]);
if (revoked)
*revoked = atoi(row[4]);
if(gmhideme)
*gmhideme = atoi(row[5]);
if(account_creation)
*account_creation = atoul(row[6]);
return true;
}
else {
return false;
}
}
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;
}
bool ZoneDatabase::DoorIsOpen(uint8 door_id,const char* zone_name)
{
if(door_isopen_array[door_id] == 0) {
SetDoorPlace(1,door_id,zone_name);
return false;
}
else {
SetDoorPlace(0,door_id,zone_name);
return true;
}
}
void ZoneDatabase::SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name)
{
door_isopen_array[door_id] = value;
}
void ZoneDatabase::GetEventLogs(const char* name,char* target,uint32 account_id,uint8 eventid,char* detail,char* timestamp, CharacterEventLog_Struct* cel)
{
char modifications[200];
if(strlen(name) != 0)
sprintf(modifications,"charname=\'%s\'",name);
else if(account_id != 0)
sprintf(modifications,"accountid=%i",account_id);
if(strlen(target) != 0)
sprintf(modifications,"%s AND target LIKE \'%%%s%%\'",modifications,target);
if(strlen(detail) != 0)
sprintf(modifications,"%s AND description LIKE \'%%%s%%\'",modifications,detail);
if(strlen(timestamp) != 0)
sprintf(modifications,"%s AND time LIKE \'%%%s%%\'",modifications,timestamp);
if(eventid == 0)
eventid =1;
sprintf(modifications,"%s AND event_nid=%i",modifications,eventid);
std::string query = StringFormat("SELECT id, accountname, accountid, status, charname, target, "
"time, descriptiontype, description FROM eventlog WHERE %s", modifications);
auto results = QueryDatabase(query);
if (!results.Success())
return;
int index = 0;
for (auto row = results.begin(); row != results.end(); ++row, ++index) {
if(index == 255)
break;
cel->eld[index].id = atoi(row[0]);
strn0cpy(cel->eld[index].accountname,row[1],64);
cel->eld[index].account_id = atoi(row[2]);
cel->eld[index].status = atoi(row[3]);
strn0cpy(cel->eld[index].charactername,row[4],64);
strn0cpy(cel->eld[index].targetname,row[5],64);
sprintf(cel->eld[index].timestamp,"%s",row[6]);
strn0cpy(cel->eld[index].descriptiontype,row[7],64);
strn0cpy(cel->eld[index].details,row[8],128);
cel->eventid = eventid;
cel->count = index + 1;
}
}
// Load child objects for a world container (i.e., forge, bag dropped to ground, etc)
void ZoneDatabase::LoadWorldContainer(uint32 parentid, ItemInst* container)
{
if (!container) {
LogFile->write(EQEMuLog::Error, "Programming error: LoadWorldContainer passed nullptr pointer");
return;
}
std::string query = StringFormat("SELECT bagidx, itemid, charges, augslot1, augslot2, augslot3, augslot4, augslot5 "
"FROM object_contents WHERE parentid = %i", parentid);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in DB::LoadWorldContainer: %s", results.ErrorMessage().c_str());
return;
}
for (auto row = results.begin(); row != results.end(); ++row) {
uint8 index = (uint8)atoi(row[0]);
uint32 item_id = (uint32)atoi(row[1]);
int8 charges = (int8)atoi(row[2]);
uint32 aug[EmuConstants::ITEM_COMMON_SIZE];
aug[0] = (uint32)atoi(row[3]);
aug[1] = (uint32)atoi(row[4]);
aug[2] = (uint32)atoi(row[5]);
aug[3] = (uint32)atoi(row[6]);
aug[4] = (uint32)atoi(row[7]);
ItemInst* inst = database.CreateItem(item_id, charges);
if (inst && inst->GetItem()->ItemClass == ItemClassCommon) {
for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++)
if (aug[i])
inst->PutAugment(&database, i, aug[i]);
// Put item inside world container
container->PutItem(index, *inst);
safe_delete(inst);
}
}
}
// Save child objects for a world container (i.e., forge, bag dropped to ground, etc)
void ZoneDatabase::SaveWorldContainer(uint32 zone_id, uint32 parent_id, const ItemInst* container)
{
// Since state is not saved for each world container action, we'll just delete
// all and save from scratch .. we may come back later to optimize
if (!container)
return;
//Delete all items from container
DeleteWorldContainer(parent_id,zone_id);
// Save all 10 items, if they exist
for (uint8 index = SUB_BEGIN; index < EmuConstants::ITEM_CONTAINER_SIZE; index++) {
ItemInst* inst = container->GetItem(index);
if (!inst)
continue;
uint32 item_id = inst->GetItem()->ID;
uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM };
if (inst->IsType(ItemClassCommon)) {
for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) {
ItemInst *auginst=inst->GetAugment(i);
augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0;
}
}
std::string query = StringFormat("REPLACE INTO object_contents "
"(zoneid, parentid, bagidx, itemid, charges, "
"augslot1, augslot2, augslot3, augslot4, augslot5, droptime) "
"VALUES (%i, %i, %i, %i, %i, %i, %i, %i, %i, %i, now())",
zone_id, parent_id, index, item_id, inst->GetCharges(),
augslot[0], augslot[1], augslot[2], augslot[3], augslot[4]);
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::SaveWorldContainer: %s", results.ErrorMessage().c_str());
}
}
// Remove all child objects inside a world container (i.e., forge, bag dropped to ground, etc)
void ZoneDatabase::DeleteWorldContainer(uint32 parent_id, uint32 zone_id)
{
std::string query = StringFormat("DELETE FROM object_contents WHERE parentid = %i AND zoneid = %i", parent_id, zone_id);
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::DeleteWorldContainer: %s", results.ErrorMessage().c_str());
}
Trader_Struct* ZoneDatabase::LoadTraderItem(uint32 char_id){
Trader_Struct* 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()) {
_log(TRADING__CLIENT, "Failed to load trader information!\n");
return loadti;
}
loadti->Code = BazaarTrader_ShowItems;
for (auto row = results.begin(); row != results.end(); ++row) {
if(atoi(row[5])>=80 || atoi(row[4])<0) {
_log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n");
continue;
}
loadti->Items[atoi(row[5])] = atoi(row[1]);
loadti->ItemCost[atoi(row[5])] = atoi(row[4]);
}
}
TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id){
TraderCharges_Struct* 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()) {
_log(TRADING__CLIENT, "Failed to load trader information!\n");
return loadti;
}
for (auto row = results.begin(); row != results.end(); ++row) {
if(atoi(row[5])>=80 || atoi(row[5])<0) {
_log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n");
continue;
}
loadti->ItemID[atoi(row[5])] = atoi(row[1]);
loadti->SerialNumber[atoi(row[5])] = atoi(row[2]);
loadti->Charges[atoi(row[5])] = atoi(row[3]);
loadti->ItemCost[atoi(row[5])] = atoi(row[4]);
}
return loadti;
}
ItemInst* 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) {
_log(TRADING__CLIENT, "Bad result from query\n"); fflush(stdout);
return nullptr;
}
auto row = results.begin();
int ItemID = atoi(row[1]);
int Charges = atoi(row[3]);
int Cost = atoi(row[4]);
const Item_Struct *item = database.GetItem(ItemID);
if(!item) {
_log(TRADING__CLIENT, "Unable to create item\n");
fflush(stdout);
return nullptr;
}
if (item->NoDrop == 0)
return nullptr;
ItemInst* inst = database.CreateItem(item);
if(!inst) {
_log(TRADING__CLIENT, "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())
_log(TRADING__CLIENT, "Failed to save trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str());
}
void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) {
_log(TRADING__CLIENT, "ZoneDatabase::UpdateTraderItemCharges(%i, %i, %i)", 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())
_log(TRADING__CLIENT, "Failed to update charges for trader item: %i for char_id: %i, the error was: %s\n",
SerialNumber, CharID, results.ErrorMessage().c_str());
}
void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice) {
_log(TRADING__CLIENT, "ZoneDatabase::UpdateTraderPrice(%i, %i, %i, %i)", CharID, ItemID, Charges, NewPrice);
const Item_Struct *item = database.GetItem(ItemID);
if(!item)
return;
if(NewPrice == 0) {
_log(TRADING__CLIENT, "Removing Trader items from the DB for CharID %i, ItemID %i", 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())
_log(TRADING__CLIENT, "Failed to remove trader item(s): %i for char_id: %i, the error was: %s\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())
_log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\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())
_log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\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())
_log(TRADING__CLIENT, "Failed to delete all trader items data, the error was: %s\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())
_log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\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())
_log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\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())
_log(TRADING__CLIENT, "Failed to delete all buyer items data, the error was: %s\n",results.ErrorMessage().c_str());
return;
}
std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i", CharID);
auto results = QueryDatabase(query);
if (!results.Success())
_log(TRADING__CLIENT, "Failed to delete buyer item data for charid: %i, the error was: %s\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())
_log(TRADING__CLIENT, "Failed to save buline item: %i for char_id: %i, the error was: %s\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())
_log(TRADING__CLIENT, "Failed to delete buyslot %i for charid: %i, the error was: %s\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())
_log(TRADING__CLIENT, "Failed to update quantity in buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, results.ErrorMessage().c_str());
}
#define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1))
// Process results of GetCharacterInfoForLogin()
// Query this processes: SELECT id,profile,zonename,x,y,z,guild,guildrank,extprofile,class,level FROM character_ WHERE id=%i
bool ZoneDatabase::GetCharacterInfoForLogin_result(MYSQL_RES* result,
uint32* character_id, char* current_zone, PlayerProfile_Struct* pp, Inventory* inv,
ExtendedProfile_Struct *ext, uint32* pplen, uint32* guilddbid, uint8* guildrank,
uint8 *class_, uint8 *level, bool *LFP, bool *LFG, uint8 *NumXTargets, uint8* firstlogon) {
MYSQL_ROW row;
unsigned long* lengths;
if (mysql_num_rows(result) == 1) {
row = mysql_fetch_row(result);
lengths = mysql_fetch_lengths(result);
if (pp && pplen) {
if (lengths[1] == sizeof(PlayerProfile_Struct)) {
memcpy(pp, row[1], sizeof(PlayerProfile_Struct));
} else {
LogFile->write(EQEMuLog::Error, "Player profile length mismatch in GetCharacterInfo Expected: %i, Got: %i",
sizeof(PlayerProfile_Struct), lengths[1]);
return false;
}
*pplen = lengths[1];
pp->zone_id = GetZoneID(row[2]);
pp->zoneInstance = atoi(row[13]);
pp->x = atof(row[3]);
pp->y = atof(row[4]);
pp->z = atof(row[5]);
pp->lastlogin = time(nullptr);
if (pp->x == -1 && pp->y == -1 && pp->z == -1)
GetSafePoints(pp->zone_id, database.GetInstanceVersion(pp->zoneInstance), &pp->x, &pp->y, &pp->z);
}
uint32 char_id = atoi(row[0]);
if (RuleB(Character, SharedBankPlat))
pp->platinum_shared = database.GetSharedPlatinum(GetAccountIDByChar(char_id));
if (character_id)
*character_id = char_id;
if (current_zone)
strcpy(current_zone, row[2]);
if (guilddbid) {
if(row[6] != nullptr)
*guilddbid = atoi(row[6]);
else
*guilddbid = GUILD_NONE;
}
if (guildrank) {
if(row[7] != nullptr)
*guildrank = atoi(row[7]);
else
*guildrank = GUILD_RANK_NONE;
}
if(ext) {
//SetExtendedProfile handles any conversion
SetExtendedProfile(ext, row[8], lengths[8]);
}
if(class_)
*class_ = atoi(row[9]);
if(level)
*level = atoi(row[10]);
if(LFP)
*LFP = atoi(row[11]);
if(LFG)
*LFG = atoi(row[12]);
if(NumXTargets)
{
*NumXTargets = atoi(row[14]);
}
if(firstlogon)
{
*firstlogon = atoi(row[15]);
}
// Fix use_tint, previously it was set to 1 for a dyed slot, client wants it set to 0xFF
for(int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++)
if(pp->item_tint[i].rgb.use_tint == 1)
pp->item_tint[i].rgb.use_tint = 0xFF;
// Retrieve character inventory
return GetInventory(char_id, inv);
}
return false;
}
bool ZoneDatabase::NoRentExpired(const char* name){
std::string query = StringFormat("SELECT (UNIX_TIMESTAMP(NOW())-timelaston) "
"FROM character_ WHERE name = '%s'", name);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
if (results.RowCount() != 1)
return false;
auto row = results.begin();
uint32 seconds = atoi(row[0]);
return (seconds>1800);
}
/* Searches npctable for matching id, and returns the item if found,
* or nullptr otherwise. If id passed is 0, loads all npc_types for
* the current zone, returning the last item added.
*/
const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
const NPCType *npc=nullptr;
// If NPC is already in tree, return it.
auto itr = zone->npctable.find(id);
if(itr != zone->npctable.end())
return itr->second;
// Otherwise, get NPCs from database.
// If id is 0, load all npc_types for the current zone,
// according to spawn2.
std::string query = StringFormat("SELECT npc_types.id, npc_types.name, npc_types.level, npc_types.race, "
"npc_types.class, npc_types.hp, npc_types.mana, npc_types.gender, "
"npc_types.texture, npc_types.helmtexture, npc_types.size, "
"npc_types.loottable_id, npc_types.merchant_id, npc_types.alt_currency_id, "
"npc_types.adventure_template_id, npc_types.trap_template, npc_types.attack_speed, "
"npc_types.STR, npc_types.STA, npc_types.DEX, npc_types.AGI, npc_types._INT, "
"npc_types.WIS, npc_types.CHA, npc_types.MR, npc_types.CR, npc_types.DR, "
"npc_types.FR, npc_types.PR, npc_types.Corrup, npc_types.PhR,"
"npc_types.mindmg, npc_types.maxdmg, npc_types.attack_count, npc_types.special_abilities,"
"npc_types.npc_spells_id, npc_types.npc_spells_effects_id, npc_types.d_meele_texture1,"
"npc_types.d_meele_texture2, npc_types.ammo_idfile, npc_types.prim_melee_type,"
"npc_types.sec_melee_type, npc_types.ranged_type, npc_types.runspeed, npc_types.findable,"
"npc_types.trackable, npc_types.hp_regen_rate, npc_types.mana_regen_rate, "
"npc_types.aggroradius, npc_types.assistradius, npc_types.bodytype, npc_types.npc_faction_id, "
"npc_types.face, npc_types.luclin_hairstyle, npc_types.luclin_haircolor, "
"npc_types.luclin_eyecolor, npc_types.luclin_eyecolor2, npc_types.luclin_beardcolor,"
"npc_types.luclin_beard, npc_types.drakkin_heritage, npc_types.drakkin_tattoo, "
"npc_types.drakkin_details, npc_types.armortint_id, "
"npc_types.armortint_red, npc_types.armortint_green, npc_types.armortint_blue, "
"npc_types.see_invis, npc_types.see_invis_undead, npc_types.lastname, "
"npc_types.qglobal, npc_types.AC, npc_types.npc_aggro, npc_types.spawn_limit, "
"npc_types.see_hide, npc_types.see_improved_hide, npc_types.ATK, npc_types.Accuracy, "
"npc_types.Avoidance, npc_types.slow_mitigation, npc_types.maxlevel, npc_types.scalerate, "
"npc_types.private_corpse, npc_types.unique_spawn_by_name, npc_types.underwater, "
"npc_types.emoteid, npc_types.spellscale, npc_types.healscale, npc_types.no_target_hotkey,"
"npc_types.raid_target FROM npc_types WHERE id = %d", id);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error loading NPCs from database. Bad query: " << results.ErrorMessage() << std::endl;
return nullptr;
}
for (auto row = results.begin(); row != results.end(); ++row) {
NPCType *tmpNPCType;
tmpNPCType = new NPCType;
memset (tmpNPCType, 0, sizeof *tmpNPCType);
tmpNPCType->npc_id = atoi(row[0]);
strn0cpy(tmpNPCType->name, row[1], 50);
tmpNPCType->level = atoi(row[2]);
tmpNPCType->race = atoi(row[3]);
tmpNPCType->class_ = atoi(row[4]);
tmpNPCType->max_hp = atoi(row[5]);
tmpNPCType->cur_hp = tmpNPCType->max_hp;
tmpNPCType->Mana = atoi(row[6]);
tmpNPCType->gender = atoi(row[7]);
tmpNPCType->texture = atoi(row[8]);
tmpNPCType->helmtexture = atoi(row[9]);
tmpNPCType->size = atof(row[10]);
tmpNPCType->loottable_id = atoi(row[11]);
tmpNPCType->merchanttype = atoi(row[12]);
tmpNPCType->alt_currency_type = atoi(row[13]);
tmpNPCType->adventure_template = atoi(row[14]);
tmpNPCType->trap_template = atoi(row[15]);
tmpNPCType->attack_speed = atof(row[16]);
tmpNPCType->STR = atoi(row[17]);
tmpNPCType->STA = atoi(row[18]);
tmpNPCType->DEX = atoi(row[19]);
tmpNPCType->AGI = atoi(row[20]);
tmpNPCType->INT = atoi(row[21]);
tmpNPCType->WIS = atoi(row[22]);
tmpNPCType->CHA = atoi(row[23]);
tmpNPCType->MR = atoi(row[24]);
tmpNPCType->CR = atoi(row[25]);
tmpNPCType->DR = atoi(row[26]);
tmpNPCType->FR = atoi(row[27]);
tmpNPCType->PR = atoi(row[28]);
tmpNPCType->Corrup = atoi(row[29]);
tmpNPCType->PhR = atoi(row[30]);
tmpNPCType->min_dmg = atoi(row[31]);
tmpNPCType->max_dmg = atoi(row[32]);
tmpNPCType->attack_count = atoi(row[33]);
strn0cpy(tmpNPCType->special_abilities, row[34], 512);
tmpNPCType->npc_spells_id = atoi(row[35]);
tmpNPCType->npc_spells_effects_id = atoi(row[36]);
tmpNPCType->d_meele_texture1 = atoi(row[37]);
tmpNPCType->d_meele_texture2 = atoi(row[38]);
strn0cpy(tmpNPCType->ammo_idfile, row[39], 30);
tmpNPCType->prim_melee_type = atoi(row[40]);
tmpNPCType->sec_melee_type = atoi(row[41]);
tmpNPCType->ranged_type = atoi(row[42]);
tmpNPCType->runspeed= atof(row[43]);
tmpNPCType->findable = atoi(row[44]) == 0? false : true;
tmpNPCType->trackable = atoi(row[45]) == 0? false : true;
tmpNPCType->hp_regen = atoi(row[46]);
tmpNPCType->mana_regen = atoi(row[47]);
// set defaultvalue for aggroradius
tmpNPCType->aggroradius = (int32)atoi(row[48]);
if (tmpNPCType->aggroradius <= 0)
tmpNPCType->aggroradius = 70;
tmpNPCType->assistradius = (int32)atoi(row[49]);
if (tmpNPCType->assistradius <= 0)
tmpNPCType->assistradius = tmpNPCType->aggroradius;
if (row[50] && strlen(row[50]))
tmpNPCType->bodytype = (uint8)atoi(row[50]);
else
tmpNPCType->bodytype = 0;
tmpNPCType->npc_faction_id = atoi(row[51]);
tmpNPCType->luclinface = atoi(row[52]);
tmpNPCType->hairstyle = atoi(row[53]);
tmpNPCType->haircolor = atoi(row[54]);
tmpNPCType->eyecolor1 = atoi(row[55]);
tmpNPCType->eyecolor2 = atoi(row[56]);
tmpNPCType->beardcolor = atoi(row[57]);
tmpNPCType->beard = atoi(row[58]);
tmpNPCType->drakkin_heritage = atoi(row[59]);
tmpNPCType->drakkin_tattoo = atoi(row[60]);
tmpNPCType->drakkin_details = atoi(row[61]);
uint32 armor_tint_id = atoi(row[62]);
tmpNPCType->armor_tint[0] = (atoi(row[63]) & 0xFF) << 16;
tmpNPCType->armor_tint[0] |= (atoi(row[64]) & 0xFF) << 8;
tmpNPCType->armor_tint[0] |= (atoi(row[65]) & 0xFF);
tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0;
if (armor_tint_id == 0)
for (int index = MaterialChest; index <= EmuConstants::MATERIAL_END; index++)
tmpNPCType->armor_tint[index] = tmpNPCType->armor_tint[0];
else if (tmpNPCType->armor_tint[0] == 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 = results.begin();
for (int index = EmuConstants::MATERIAL_BEGIN; index <= EmuConstants::MATERIAL_END; index++) {
tmpNPCType->armor_tint[index] = atoi(armorTint_row[index * 3]) << 16;
tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 1]) << 8;
tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 2]);
tmpNPCType->armor_tint[index] |= (tmpNPCType->armor_tint[index]) ? (0xFF << 24) : 0;
}
}
} else
armor_tint_id = 0;
tmpNPCType->see_invis = atoi(row[66]);
tmpNPCType->see_invis_undead = atoi(row[67]) == 0? false: true; // Set see_invis_undead flag
if (row[68] != nullptr)
strn0cpy(tmpNPCType->lastname, row[68], 32);
tmpNPCType->qglobal = atoi(row[69]) == 0? false: true; // qglobal
tmpNPCType->AC = atoi(row[70]);
tmpNPCType->npc_aggro = atoi(row[71]) == 0? false: true;
tmpNPCType->spawn_limit = atoi(row[72]);
tmpNPCType->see_hide = atoi(row[73]) == 0? false: true;
tmpNPCType->see_improved_hide = atoi(row[74]) == 0? false: true;
tmpNPCType->ATK = atoi(row[75]);
tmpNPCType->accuracy_rating = atoi(row[76]);
tmpNPCType->avoidance_rating = atoi(row[77]);
tmpNPCType->slow_mitigation = atoi(row[78]);
tmpNPCType->maxlevel = atoi(row[79]);
tmpNPCType->scalerate = atoi(row[80]);
tmpNPCType->private_corpse = atoi(row[81]) == 1 ? true: false;
tmpNPCType->unique_spawn_by_name = atoi(row[82]) == 1 ? true: false;
tmpNPCType->underwater = atoi(row[83]) == 1 ? true: false;
tmpNPCType->emoteid = atoi(row[84]);
tmpNPCType->spellscale = atoi(row[85]);
tmpNPCType->healscale = atoi(row[86]);
tmpNPCType->no_target_hotkey = atoi(row[87]) == 1 ? true: false;
tmpNPCType->raid_target = atoi(row[88]) == 0 ? false: true;
// If NPC with duplicate NPC id already in table,
// free item we attempted to add.
if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end()) {
std::cerr << "Error loading duplicate NPC " << tmpNPCType->npc_id << std::endl;
delete tmpNPCType;
return nullptr;
}
zone->npctable[tmpNPCType->npc_id]=tmpNPCType;
npc = tmpNPCType;
}
return npc;
}
const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 clientlevel) {
//need to save based on merc_npc_type & client level
uint32 merc_type_id = id * 100 + clientlevel;
// If NPC is already in tree, return it.
auto itr = zone->merctable.find(merc_type_id);
if(itr != zone->merctable.end())
return itr->second;
//If the NPC type is 0, return nullptr. (sanity check)
if(id == 0)
return nullptr;
// Otherwise, get NPCs from database.
// If id is 0, load all npc_types for the current zone,
// according to spawn2.
std::string query = StringFormat("SELECT vwMercNpcTypes.merc_npc_type_id, vwMercNpcTypes.name, "
"vwMercNpcTypes.level, vwMercNpcTypes.race_id, vwMercNpcTypes.class_id, "
"vwMercNpcTypes.hp, vwMercNpcTypes.mana, vwMercNpcTypes.gender, "
"vwMercNpcTypes.texture, vwMercNpcTypes.helmtexture, vwMercNpcTypes.attack_speed, "
"vwMercNpcTypes.STR, vwMercNpcTypes.STA, vwMercNpcTypes.DEX, vwMercNpcTypes.AGI, "
"vwMercNpcTypes._INT, vwMercNpcTypes.WIS, vwMercNpcTypes.CHA, vwMercNpcTypes.MR, "
"vwMercNpcTypes.CR, vwMercNpcTypes.DR, vwMercNpcTypes.FR, vwMercNpcTypes.PR, "
"vwMercNpcTypes.Corrup, vwMercNpcTypes.mindmg, vwMercNpcTypes.maxdmg, "
"vwMercNpcTypes.attack_count, vwMercNpcTypes.special_abilities, "
"vwMercNpcTypes.d_meele_texture1, vwMercNpcTypes.d_meele_texture2, "
"vwMercNpcTypes.prim_melee_type, vwMercNpcTypes.sec_melee_type, "
"vwMercNpcTypes.runspeed, vwMercNpcTypes.hp_regen_rate, vwMercNpcTypes.mana_regen_rate, "
"vwMercNpcTypes.bodytype, vwMercNpcTypes.armortint_id, "
"vwMercNpcTypes.armortint_red, vwMercNpcTypes.armortint_green, vwMercNpcTypes.armortint_blue, "
"vwMercNpcTypes.AC, vwMercNpcTypes.ATK, vwMercNpcTypes.Accuracy, vwMercNpcTypes.spellscale, "
"vwMercNpcTypes.healscale FROM vwMercNpcTypes "
"WHERE merc_npc_type_id = %d AND clientlevel = %d AND race_id = %d",
id, clientlevel, raceid); //dual primary keys. one is ID, one is level.
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error loading NPCs from database. Bad query: " << results.ErrorMessage() << std::endl;
return nullptr;
}
const NPCType *npc;
// Process each row returned.
for (auto row = results.begin(); row != results.end(); ++row) {
NPCType *tmpNPCType;
tmpNPCType = new NPCType;
memset (tmpNPCType, 0, sizeof *tmpNPCType);
tmpNPCType->npc_id = atoi(row[0]);
strn0cpy(tmpNPCType->name, row[1], 50);
tmpNPCType->level = atoi(row[2]);
tmpNPCType->race = atoi(row[3]);
tmpNPCType->class_ = atoi(row[4]);
tmpNPCType->max_hp = atoi(row[5]);
tmpNPCType->cur_hp = tmpNPCType->max_hp;
tmpNPCType->Mana = atoi(row[6]);
tmpNPCType->gender = atoi(row[7]);
tmpNPCType->texture = atoi(row[8]);
tmpNPCType->helmtexture = atoi(row[9]);
tmpNPCType->attack_speed = atof(row[10]);
tmpNPCType->STR = atoi(row[11]);
tmpNPCType->STA = atoi(row[12]);
tmpNPCType->DEX = atoi(row[13]);
tmpNPCType->AGI = atoi(row[14]);
tmpNPCType->INT = atoi(row[15]);
tmpNPCType->WIS = atoi(row[16]);
tmpNPCType->CHA = atoi(row[17]);
tmpNPCType->MR = atoi(row[18]);
tmpNPCType->CR = atoi(row[19]);
tmpNPCType->DR = atoi(row[20]);
tmpNPCType->FR = atoi(row[21]);
tmpNPCType->PR = atoi(row[22]);
tmpNPCType->Corrup = atoi(row[23]);
tmpNPCType->min_dmg = atoi(row[24]);
tmpNPCType->max_dmg = atoi(row[25]);
tmpNPCType->attack_count = atoi(row[26]);
strn0cpy(tmpNPCType->special_abilities, row[27], 512);
tmpNPCType->d_meele_texture1 = atoi(row[28]);
tmpNPCType->d_meele_texture2 = atoi(row[29]);
tmpNPCType->prim_melee_type = atoi(row[30]);
tmpNPCType->sec_melee_type = atoi(row[31]);
tmpNPCType->runspeed= atof(row[32]);
tmpNPCType->hp_regen = atoi(row[33]);
tmpNPCType->mana_regen = atoi(row[34]);
tmpNPCType->aggroradius = RuleI(Mercs, AggroRadius);
if (row[35] && strlen(row[35]))
tmpNPCType->bodytype = (uint8)atoi(row[35]);
else
tmpNPCType->bodytype = 1;
uint32 armor_tint_id = atoi(row[36]);
tmpNPCType->armor_tint[0] = (atoi(row[37]) & 0xFF) << 16;
tmpNPCType->armor_tint[0] |= (atoi(row[38]) & 0xFF) << 8;
tmpNPCType->armor_tint[0] |= (atoi(row[39]) & 0xFF);
tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0;
if (armor_tint_id == 0)
for (int index = MaterialChest; index <= EmuConstants::MATERIAL_END; index++)
tmpNPCType->armor_tint[index] = tmpNPCType->armor_tint[0];
else if (tmpNPCType->armor_tint[0] == 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 (!results.Success() || results.RowCount() == 0)
armor_tint_id = 0;
else {
auto armorTint_row = results.begin();
for (int index = EmuConstants::MATERIAL_BEGIN; index <= EmuConstants::MATERIAL_END; index++) {
tmpNPCType->armor_tint[index] = atoi(armorTint_row[index * 3]) << 16;
tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 1]) << 8;
tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 2]);
tmpNPCType->armor_tint[index] |= (tmpNPCType->armor_tint[index]) ? (0xFF << 24) : 0;
}
}
} else
armor_tint_id = 0;
tmpNPCType->AC = atoi(row[40]);
tmpNPCType->ATK = atoi(row[41]);
tmpNPCType->accuracy_rating = atoi(row[42]);
tmpNPCType->scalerate = RuleI(Mercs, ScaleRate);
tmpNPCType->spellscale = atoi(row[43]);
tmpNPCType->healscale = atoi(row[4]);
// If NPC with duplicate NPC id already in table,
// free item we attempted to add.
if (zone->merctable.find(tmpNPCType->npc_id * 100 + clientlevel) != zone->merctable.end()) {
delete tmpNPCType;
return nullptr;
}
zone->merctable[tmpNPCType->npc_id * 100 + clientlevel]=tmpNPCType;
npc = tmpNPCType;
}
return npc;
}
bool ZoneDatabase::LoadMercInfo(Client *client) {
if(client->GetEPP().merc_name[0] == 0)
return false;
std::string query = StringFormat("SELECT MercID, Slot, Name, TemplateID, SuspendedTime, "
"IsSuspended, TimerRemaining, Gender, StanceID, HP, Mana, "
"Endurance, Face, LuclinHairStyle, LuclinHairColor, "
"LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, "
"DrakkinHeritage, DrakkinTattoo, DrakkinDetails "
"FROM mercs WHERE OwnerCharacterID = '%i' ORDER BY Slot", client->CharacterID());
auto results = QueryDatabase(query);
if (!results.Success())
return false;
for (auto row = results.begin(); row != results.end(); ++row) {
uint8 slot = atoi(row[1]);
if(slot >= MAXMERCS)
continue;
client->GetMercInfo(slot).mercid = atoi(row[0]);
client->GetMercInfo(slot).slot = slot;
snprintf(client->GetMercInfo(slot).merc_name, 64, "%s", row[2]);
client->GetMercInfo(slot).MercTemplateID = atoi(row[3]);
client->GetMercInfo(slot).SuspendedTime = atoi(row[4]);
client->GetMercInfo(slot).IsSuspended = atoi(row[5]) == 1 ? true : false;
client->GetMercInfo(slot).MercTimerRemaining = atoi(row[6]);
client->GetMercInfo(slot).Gender = atoi(row[7]);
client->GetMercInfo(slot).State = 5;
client->GetMercInfo(slot).Stance = atoi(row[8]);
client->GetMercInfo(slot).hp = atoi(row[9]);
client->GetMercInfo(slot).mana = atoi(row[10]);
client->GetMercInfo(slot).endurance = atoi(row[11]);
client->GetMercInfo(slot).face = atoi(row[12]);
client->GetMercInfo(slot).luclinHairStyle = atoi(row[13]);
client->GetMercInfo(slot).luclinHairColor = atoi(row[14]);
client->GetMercInfo(slot).luclinEyeColor = atoi(row[15]);
client->GetMercInfo(slot).luclinEyeColor2 = atoi(row[16]);
client->GetMercInfo(slot).luclinBeardColor = atoi(row[17]);
client->GetMercInfo(slot).luclinBeard = atoi(row[18]);
client->GetMercInfo(slot).drakkinHeritage = atoi(row[19]);
client->GetMercInfo(slot).drakkinTattoo = atoi(row[20]);
client->GetMercInfo(slot).drakkinDetails = atoi(row[21]);
}
return true;
}
bool ZoneDatabase::LoadCurrentMerc(Client *client) {
if(client->GetEPP().merc_name[0] == 0)
return false;
uint8 slot = client->GetMercSlot();
if(slot > MAXMERCS)
return false;
std::string query = StringFormat("SELECT MercID, Name, TemplateID, SuspendedTime, "
"IsSuspended, TimerRemaining, Gender, StanceID, HP, "
"Mana, Endurance, Face, LuclinHairStyle, LuclinHairColor, "
"LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, "
"LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails "
"FROM mercs WHERE OwnerCharacterID = '%i' AND Slot = '%u'",
client->CharacterID(), slot);
auto results = database.QueryDatabase(query);
if(!results.Success())
return false;
for (auto row = results.begin(); row != results.end(); ++row) {
client->GetMercInfo(slot).mercid = atoi(row[0]);
client->GetMercInfo(slot).slot = slot;
snprintf(client->GetMercInfo(slot).merc_name, 64, "%s", row[1]);
client->GetMercInfo(slot).MercTemplateID = atoi(row[2]);
client->GetMercInfo(slot).SuspendedTime = atoi(row[3]);
client->GetMercInfo(slot).IsSuspended = atoi(row[4]) == 1? true: false;
client->GetMercInfo(slot).MercTimerRemaining = atoi(row[5]);
client->GetMercInfo(slot).Gender = atoi(row[6]);
client->GetMercInfo(slot).State = atoi(row[7]);
client->GetMercInfo(slot).hp = atoi(row[8]);
client->GetMercInfo(slot).mana = atoi(row[9]);
client->GetMercInfo(slot).endurance = atoi(row[10]);
client->GetMercInfo(slot).face = atoi(row[11]);
client->GetMercInfo(slot).luclinHairStyle = atoi(row[12]);
client->GetMercInfo(slot).luclinHairColor = atoi(row[13]);
client->GetMercInfo(slot).luclinEyeColor = atoi(row[14]);
client->GetMercInfo(slot).luclinEyeColor2 = atoi(row[15]);
client->GetMercInfo(slot).luclinBeardColor = atoi(row[16]);
client->GetMercInfo(slot).luclinBeard = atoi(row[17]);
client->GetMercInfo(slot).drakkinHeritage = atoi(row[18]);
client->GetMercInfo(slot).drakkinTattoo = atoi(row[19]);
client->GetMercInfo(slot).drakkinDetails = atoi(row[20]);
}
return true;
}
bool ZoneDatabase::SaveMerc(Merc *merc) {
Client *owner = merc->GetMercOwner();
if(!owner)
return false;
if(merc->GetMercID() == 0) {
// New merc record
uint32 TempNewMercID = 0;
std::string query = StringFormat("INSERT INTO mercs "
"(OwnerCharacterID, Slot, Name, TemplateID, "
"SuspendedTime, IsSuspended, TimerRemaining, "
"Gender, StanceID, HP, Mana, Endurance, Face, "
"LuclinHairStyle, LuclinHairColor, LuclinEyeColor, "
"LuclinEyeColor2, LuclinBeardColor, LuclinBeard, "
"DrakkinHeritage, DrakkinTattoo, DrakkinDetails) "
"VALUES('%u', '%u', '%s', '%u', '%u', '%u', '%u', "
"'%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', "
"'%i', '%i', '%i', '%i', '%i', '%i', '%i')",
merc->GetMercCharacterID(), owner->GetNumMercs(),
merc->GetCleanName(), merc->GetMercTemplateID(),
owner->GetMercInfo().SuspendedTime, merc->IsSuspended(),
owner->GetMercInfo().MercTimerRemaining, merc->GetGender(),
merc->GetStance(), merc->GetHP(), merc->GetMana(),
merc->GetEndurance(), merc->GetLuclinFace(),
merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(),
merc->GetEyeColor2(), merc->GetBeardColor(),
merc->GetBeard(), merc->GetDrakkinHeritage(),
merc->GetDrakkinTattoo(), merc->GetDrakkinDetails());
auto results = database.QueryDatabase(query);
if(!results.Success()) {
owner->Message(13, results.ErrorMessage().c_str());
return false;
} else if (results.RowsAffected() != 1) {
owner->Message(13, "Unable to save merc to the database.");
return false;
}
merc->SetMercID(TempNewMercID);
merc->UpdateMercInfo(owner);
database.SaveMercBuffs(merc);
return true;
}
// Update existing merc record
std::string query = StringFormat("UPDATE mercs SET OwnerCharacterID = '%u', Slot = '%u', "
"Name = '%s', TemplateID = '%u', SuspendedTime = '%u', "
"IsSuspended = '%u', TimerRemaining = '%u', Gender = '%u', "
"StanceID = '%u', HP = '%u', Mana = '%u', Endurance = '%u', "
"Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', "
"LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', "
"LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', "
"DrakkinDetails = '%i' WHERE MercID = '%u'",
merc->GetMercCharacterID(), owner->GetMercSlot(), merc->GetCleanName(),
merc->GetMercTemplateID(), owner->GetMercInfo().SuspendedTime,
merc->IsSuspended(), owner->GetMercInfo().MercTimerRemaining,
merc->GetGender(), merc->GetStance(), merc->GetHP(), merc->GetMana(),
merc->GetEndurance(), merc->GetLuclinFace(), merc->GetHairStyle(),
merc->GetHairColor(), merc->GetEyeColor1(), merc->GetEyeColor2(),
merc->GetBeardColor(), merc->GetBeard(), merc->GetDrakkinHeritage(),
merc->GetDrakkinTattoo(), merc->GetDrakkinDetails(), merc->GetMercID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
owner->Message(13, results.ErrorMessage().c_str());
return false;
} else if (results.RowsAffected() != 1) {
owner->Message(13, "Unable to save merc to the database.");
return false;
}
merc->UpdateMercInfo(owner);
database.SaveMercBuffs(merc);
return true;
}
void ZoneDatabase::SaveMercBuffs(Merc *merc) {
Buffs_Struct *buffs = merc->GetBuffs();
// Remove any existing buff saves
std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID());
auto results = database.QueryDatabase(query);
if(!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error While Deleting Merc Buffs before save: %s", results.ErrorMessage().c_str());
return;
}
for (int buffCount = 0; buffCount <= BUFF_COUNT; buffCount++) {
if(buffs[buffCount].spellid == 0 || buffs[buffCount].spellid == SPELL_UNKNOWN)
continue;
int IsPersistent = buffs[buffCount].persistant_buff? 1: 0;
query = StringFormat("INSERT INTO merc_buffs (MercId, SpellId, CasterLevel, DurationFormula, "
"TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, "
"CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, "
"caston_x, Persistent, caston_y, caston_z, ExtraDIChance) "
"VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);",
merc->GetMercID(), buffs[buffCount].spellid, buffs[buffCount].casterlevel,
spells[buffs[buffCount].spellid].buffdurationformula, buffs[buffCount].ticsremaining,
CalculatePoisonCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0,
CalculateDiseaseCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0,
CalculateCurseCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0,
CalculateCorruptionCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0,
buffs[buffCount].numhits, buffs[buffCount].melee_rune, buffs[buffCount].magic_rune,
buffs[buffCount].dot_rune, buffs[buffCount].caston_x, IsPersistent, buffs[buffCount].caston_y,
buffs[buffCount].caston_z, buffs[buffCount].ExtraDIChance);
results = database.QueryDatabase(query);
if(!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error Saving Merc Buffs: %s", results.ErrorMessage().c_str());
break;
}
}
}
void ZoneDatabase::LoadMercBuffs(Merc *merc) {
Buffs_Struct *buffs = merc->GetBuffs();
uint32 max_slots = merc->GetMaxBuffSlots();
bool BuffsLoaded = false;
std::string query = StringFormat("SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, "
"PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, "
"HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, "
"caston_y, caston_z, ExtraDIChance FROM merc_buffs WHERE MercId = %u",
merc->GetMercID());
auto results = database.QueryDatabase(query);
if(!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error Loading Merc Buffs: %s", results.ErrorMessage().c_str());
return;
}
int buffCount = 0;
for (auto row = results.begin(); row != results.end(); ++row, ++buffCount) {
if(buffCount == BUFF_COUNT)
break;
buffs[buffCount].spellid = atoi(row[0]);
buffs[buffCount].casterlevel = atoi(row[1]);
buffs[buffCount].ticsremaining = atoi(row[3]);
if(CalculatePoisonCounters(buffs[buffCount].spellid) > 0)
buffs[buffCount].counters = atoi(row[4]);
if(CalculateDiseaseCounters(buffs[buffCount].spellid) > 0)
buffs[buffCount].counters = atoi(row[5]);
if(CalculateCurseCounters(buffs[buffCount].spellid) > 0)
buffs[buffCount].counters = atoi(row[6]);
if(CalculateCorruptionCounters(buffs[buffCount].spellid) > 0)
buffs[buffCount].counters = atoi(row[7]);
buffs[buffCount].numhits = atoi(row[8]);
buffs[buffCount].melee_rune = atoi(row[9]);
buffs[buffCount].magic_rune = atoi(row[10]);
buffs[buffCount].dot_rune = atoi(row[11]);
buffs[buffCount].caston_x = atoi(row[12]);
buffs[buffCount].casterid = 0;
bool IsPersistent = atoi(row[13])? true: false;
buffs[buffCount].caston_y = atoi(row[13]);
buffs[buffCount].caston_z = atoi(row[14]);
buffs[buffCount].ExtraDIChance = atoi(row[15]);
buffs[buffCount].persistant_buff = IsPersistent;
}
query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID());
results = database.QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error Loading Merc Buffs: %s", results.ErrorMessage().c_str());
}
bool ZoneDatabase::DeleteMerc(uint32 merc_id) {
if(merc_id == 0)
return false;
bool firstQueryWorked = false;
// TODO: These queries need to be ran together as a transaction.. ie,
// if one or more fail then they all will fail to commit to the database.
std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercID = '%u'", merc_id);
auto results = database.QueryDatabase(query);
if(!results.Success())
LogFile->write(EQEMuLog::Error, "Error Deleting Merc: %s", results.ErrorMessage().c_str());
else
firstQueryWorked = true;
query = StringFormat("DELETE FROM mercs WHERE MercID = '%u'", merc_id);
if(!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error Deleting Merc: %s", results.ErrorMessage().c_str());
return false;
}
return firstQueryWorked;
}
void ZoneDatabase::LoadMercEquipment(Merc *merc) {
std::string query = StringFormat("SELECT item_id FROM merc_inventory "
"WHERE merc_subtype_id = ("
"SELECT merc_subtype_id FROM merc_subtypes "
"WHERE class_id = '%u' AND tier_id = '%u') "
"AND min_level <= %u AND max_level >= %u",
merc->GetClass(), merc->GetTierID(),
merc->GetLevel(), merc->GetLevel());
auto results = database.QueryDatabase(query);
if(!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error Loading Merc Inventory: %s", results.ErrorMessage().c_str());
return;
}
int itemCount = 0;
for(auto row = results.begin(); row != results.end(); ++row) {
if (itemCount == EmuConstants::EQUIPMENT_SIZE)
break;
if(atoi(row[0]) == 0)
continue;
merc->AddItem(itemCount, atoi(row[0]));
itemCount++;
}
}
uint8 ZoneDatabase::GetGridType(uint32 grid, uint32 zoneid ) {
std::string query = StringFormat("SELECT type FROM grid WHERE id = %i AND zoneid = %i", grid, zoneid);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in GetGridType query '" << query << "' " << results.ErrorMessage() << std::endl;
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
void ZoneDatabase::SaveMerchantTemp(uint32 npcid, uint32 slot, uint32 item, uint32 charges){
std::string query = StringFormat("REPLACE INTO merchantlist_temp (npcid, slot, itemid, charges) "
"VALUES(%d, %d, %d, %d)", npcid, slot, item, charges);
auto results = QueryDatabase(query);
if (!results.Success())
std::cerr << "Error in SaveMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl;
}
void ZoneDatabase::DeleteMerchantTemp(uint32 npcid, uint32 slot){
std::string query = StringFormat("DELETE FROM merchantlist_temp WHERE npcid=%d AND slot=%d", npcid, slot);
auto results = QueryDatabase(query);
if (!results.Success())
std::cerr << "Error in DeleteMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl;
}
bool ZoneDatabase::UpdateZoneSafeCoords(const char* zonename, float x=0, float y=0, float z=0) {
std::string query = StringFormat("UPDATE zone SET safe_x='%f', safe_y='%f', safe_z='%f' "
"WHERE short_name='%s';", x, y, z, zonename);
auto results = QueryDatabase(query);
if (!results.Success() || results.RowsAffected() == 0)
return false;
return true;
}
uint8 ZoneDatabase::GetUseCFGSafeCoords()
{
const std::string query = "SELECT value FROM variables WHERE varname='UseCFGSafeCoords'";
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in GetUseCFGSafeCoords query '" << query << "' " << results.ErrorMessage() << std::endl;
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
//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()) {
std::cerr << "Error in GetZoneTZ query '" << query << "' " << results.ErrorMessage() << std::endl;
return 0;
}
if (results.RowCount() == 0)
return 0;
auto row = results.begin();
return atoi(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()) {
std::cerr << "Error in SetZoneTZ query '" << query << "' " << results.ErrorMessage() << std::endl;
return false;
}
return results.RowsAffected() == 1;
}
void ZoneDatabase::RefreshGroupFromDB(Client *client){
if(!client)
return;
Group *group = client->GetGroup();
if(!group)
return;
EQApplicationPacket* 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, &gu->leader_aas);
gu->NPCMarkerID = group->GetNPCMarkerID();
int index = 0;
std::string query = StringFormat("SELECT name FROM group_id WHERE groupid = %d", group->GetID());
auto results = QueryDatabase(query);
if (!results.Success())
printf("Error in group update query: %s\n", results.ErrorMessage().c_str());
else
for (auto row = results.begin(); row != results.end(); ++row) {
if(index >= 6)
continue;
if(strcmp(client->GetName(), row[0]) == 0)
continue;
strcpy(gu->membername[index], row[0]);
index++;
}
client->QueuePacket(outapp);
safe_delete(outapp);
if(client->GetClientVersion() >= EQClientSoD) {
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);
}
uint8 ZoneDatabase::GroupCount(uint32 groupid) {
std::string query = StringFormat("SELECT count(charid) FROM group_id WHERE groupid = %d", groupid);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::GroupCount query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
if (results.RowCount() == 0)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
uint8 ZoneDatabase::RaidGroupCount(uint32 raidid, uint32 groupid) {
std::string query = StringFormat("SELECT count(charid) FROM raid_members "
"WHERE raidid = %d AND groupid = %d;", raidid, groupid);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::RaidGroupCount query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return 0;
}
if (results.RowCount() == 0)
return 0;
auto row = results.begin();
return atoi(row[0]);
}
int32 ZoneDatabase::GetBlockedSpellsCount(uint32 zoneid)
{
std::string query = StringFormat("SELECT count(*) FROM blocked_spells WHERE zoneid = %d", zoneid);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in GetBlockedSpellsCount query '" << query << "' " << results.ErrorMessage() << std::endl;
return -1;
}
if (results.RowCount() == 0)
return -1;
auto row = results.begin();
return atoi(row[0]);
}
bool ZoneDatabase::LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid)
{
LogFile->write(EQEMuLog::Status, "Loading Blocked Spells from database...");
std::string query = StringFormat("SELECT id, spellid, type, x, y, z, x_diff, y_diff, z_diff, message "
"FROM blocked_spells WHERE zoneid = %d ORDER BY id ASC", zoneid);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in LoadBlockedSpells query '" << query << "' " << results.ErrorMessage() << std::endl;
return false;
}
if (results.RowCount() == 0)
return true;
int32 index = 0;
for(auto row = results.begin(); row != results.end(); ++row, ++index) {
if(index >= blockedSpellsCount) {
std::cerr << "Error, Blocked Spells Count of " << blockedSpellsCount << " exceeded." << std::endl;
break;
}
memset(&into[index], 0, sizeof(ZoneSpellsBlocked));
into[index].spellid = atoi(row[1]);
into[index].type = atoi(row[2]);
into[index].x = atof(row[3]);
into[index].y = atof(row[4]);
into[index].z = atof(row[5]);
into[index].xdiff = atof(row[6]);
into[index].ydiff = atof(row[7]);
into[index].zdiff = atof(row[8]);
strn0cpy(into[index].message, row[9], 255);
}
return true;
}
int ZoneDatabase::getZoneShutDownDelay(uint32 zoneID, uint32 version)
{
std::string query = StringFormat("SELECT shutdowndelay FROM zone "
"WHERE zoneidnumber = %i AND (version=%i OR version=0) "
"ORDER BY version DESC", zoneID, version);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::cerr << "Error in getZoneShutDownDelay query '" << query << "' " << results.ErrorMessage().c_str() << std::endl;
return (RuleI(Zone, AutoShutdownDelay));
}
if (results.RowCount() == 0) {
std::cerr << "Error in getZoneShutDownDelay no result '" << query << "' " << std::endl;
return (RuleI(Zone, AutoShutdownDelay));
}
auto row = results.begin();
return atoi(row[0]);
}
uint32 ZoneDatabase::GetKarma(uint32 acct_id)
{
std::string query = StringFormat("SELECT `karma` FROM `account` WHERE `id` = '%i' LIMIT 1", acct_id);
auto results = QueryDatabase(query);
if (!results.Success())
return 0;
auto row = results.begin();
return atoi(row[0]);
}
void ZoneDatabase::UpdateKarma(uint32 acct_id, uint32 amount)
{
std::string query = StringFormat("UPDATE account SET karma = %i WHERE id = %i", amount, acct_id);
auto results = QueryDatabase(query);
if (!results.Success())
std::cerr << "Error in UpdateKarma query '" << query << "' " << results.ErrorMessage().c_str() << std::endl;
}
void ZoneDatabase::ListAllInstances(Client* client, uint32 charid)
{
if(!client)
return;
std::string query = StringFormat("SELECT instance_list.id, zone, version "
"FROM instance_list JOIN instance_list_player "
"ON instance_list.id = instance_list_player.id "
"WHERE instance_list_player.charid = %lu",
(unsigned long)charid);
auto results = QueryDatabase(query);
if (!results.Success())
return;
char name[64];
database.GetCharName(charid, name);
client->Message(0, "%s is part of the following instances:", name);
for (auto row = results.begin(); row != results.end(); ++row) {
client->Message(0, "%s - id: %lu, version: %lu", database.GetZoneName(atoi(row[1])),
(unsigned long)atoi(row[0]), (unsigned long)atoi(row[2]));
}
}
void ZoneDatabase::QGlobalPurge()
{
const std::string query = "DELETE FROM quest_globals WHERE expdate < UNIX_TIMESTAMP()";
database.QueryDatabase(query);
}
void ZoneDatabase::InsertDoor(uint32 ddoordbid, uint16 ddoorid, const char* ddoor_name, float dxpos, float dypos, float dzpos, float dheading, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize){
std::string query = StringFormat("REPLACE INTO doors (id, doorid, zone, version, name, "
"pos_x, pos_y, pos_z, heading, opentype, guild, lockpick, "
"keyitem, door_param, invert_state, incline, size) "
"VALUES('%i', '%i', '%s', '%i', '%s', '%f', '%f', "
"'%f', '%f', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i')",
ddoordbid, ddoorid, zone->GetShortName(), zone->GetInstanceVersion(),
ddoor_name, dxpos, dypos, dzpos, dheading, dopentype, dguildid,
dlockpick, dkeyitem, ddoor_param, dinvert, dincline, dsize);
auto results = QueryDatabase(query);
if (!results.Success())
std::cerr << "Error in InsertDoor" << query << "' " << results.ErrorMessage() << std::endl;
}
void ZoneDatabase::LoadAltCurrencyValues(uint32 char_id, std::map<uint32, uint32> &currency) {
std::string query = StringFormat("SELECT currency_id, amount "
"FROM character_alt_currency "
"WHERE char_id = '%u'", char_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadAltCurrencyValues query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
for (auto row = results.begin(); row != results.end(); ++row)
currency[atoi(row[0])] = atoi(row[1]);
}
void ZoneDatabase::UpdateAltCurrencyValue(uint32 char_id, uint32 currency_id, uint32 value) {
std::string query = StringFormat("REPLACE INTO character_alt_currency (char_id, currency_id, amount) "
"VALUES('%u', '%u', '%u')", char_id, currency_id, value);
database.QueryDatabase(query);
}
void ZoneDatabase::SaveBuffs(Client *client) {
std::string query = StringFormat("DELETE FROM `character_buffs` WHERE `character_id` = '%u'", client->CharacterID());
database.QueryDatabase(query);
uint32 buff_count = client->GetMaxBuffSlots();
Buffs_Struct *buffs = client->GetBuffs();
for (int index = 0; index < buff_count; index++) {
if(buffs[index].spellid == SPELL_UNKNOWN)
continue;
query = StringFormat("INSERT INTO `character_buffs` (character_id, slot_id, spell_id, "
"caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, "
"magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance) "
"VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', "
"'%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid,
buffs[index].casterlevel, buffs[index].caster_name, buffs[index].ticsremaining,
buffs[index].counters, buffs[index].numhits, buffs[index].melee_rune,
buffs[index].magic_rune, buffs[index].persistant_buff, buffs[index].dot_rune,
buffs[index].caston_x, buffs[index].caston_y, buffs[index].caston_z,
buffs[index].ExtraDIChance);
auto results = QueryDatabase(query);
if (!results.Success())
LogFile->write(EQEMuLog::Error, "Error in SaveBuffs query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
}
}
void ZoneDatabase::LoadBuffs(Client *client) {
Buffs_Struct *buffs = client->GetBuffs();
uint32 max_slots = client->GetMaxBuffSlots();
for(int index = 0; index < max_slots; ++index)
buffs[index].spellid = SPELL_UNKNOWN;
std::string query = StringFormat("SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, "
"counters, numhits, melee_rune, magic_rune, persistent, dot_rune, "
"caston_x, caston_y, caston_z, ExtraDIChance "
"FROM `character_buffs` WHERE `character_id` = '%u'", client->CharacterID());
auto results = QueryDatabase(query);
if (!results.Success()) {
LogFile->write(EQEMuLog::Error, "Error in LoadBuffs query '%s': %s", query.c_str(), results.ErrorMessage().c_str());
return;
}
for (auto row = results.begin(); row != results.end(); ++row) {
uint32 slot_id = atoul(row[1]);
if(slot_id >= client->GetMaxBuffSlots())
continue;
uint32 spell_id = atoul(row[0]);
if(!IsValidSpell(spell_id))
continue;
Client *caster = entity_list.GetClientByName(row[3]);
uint32 caster_level = atoi(row[2]);
uint32 ticsremaining = atoul(row[4]);
uint32 counters = atoul(row[5]);
uint32 numhits = atoul(row[6]);
uint32 melee_rune = atoul(row[7]);
uint32 magic_rune = atoul(row[8]);
uint8 persistent = atoul(row[9]);
uint32 dot_rune = atoul(row[10]);
int32 caston_x = atoul(row[11]);
int32 caston_y = atoul(row[12]);
int32 caston_z = atoul(row[13]);
int32 ExtraDIChance = atoul(row[14]);
buffs[slot_id].spellid = spell_id;
buffs[slot_id].casterlevel = caster_level;
if(caster) {
buffs[slot_id].casterid = caster->GetID();
strcpy(buffs[slot_id].caster_name, caster->GetName());
buffs[slot_id].client = true;
} else {
buffs[slot_id].casterid = 0;
strcpy(buffs[slot_id].caster_name, "");
buffs[slot_id].client = false;
}
buffs[slot_id].ticsremaining = ticsremaining;
buffs[slot_id].counters = counters;
buffs[slot_id].numhits = numhits;
buffs[slot_id].melee_rune = melee_rune;
buffs[slot_id].magic_rune = magic_rune;
buffs[slot_id].persistant_buff = persistent? true: false;
buffs[slot_id].dot_rune = dot_rune;
buffs[slot_id].caston_x = caston_x;
buffs[slot_id].caston_y = caston_y;
buffs[slot_id].caston_z = caston_z;
buffs[slot_id].ExtraDIChance = ExtraDIChance;
buffs[slot_id].RootBreakChance = 0;
buffs[slot_id].UpdateClient = false;
}
max_slots = client->GetMaxBuffSlots();
for(int index = 0; index < max_slots; ++index) {
if(!IsValidSpell(buffs[index].spellid))
continue;
for(int effectIndex = 0; effectIndex < 12; ++effectIndex) {
if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Charm) {
buffs[index].spellid = SPELL_UNKNOWN;
break;
}
if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) {
if(buffs[index].persistant_buff)
break;
buffs[index].spellid = SPELL_UNKNOWN;
}
}
}
}
void ZoneDatabase::SavePetInfo(Client *client) {
PetInfo *petinfo = client->GetPetInfo(0);
PetInfo *suspended = client->GetPetInfo(1);
std::string query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID());
auto results = database.QueryDatabase(query);
if(!results.Success())
return;
query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID());
results = database.QueryDatabase(query);
if(!results.Success())
return;
query = StringFormat("INSERT INTO `character_pet_info` "
"(`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) "
"VALUES (%u, 0, '%s', %i, %u, %u, %u, %f) "
"ON DUPLICATE KEY UPDATE `petname` = '%s', `petpower` = %i, `spell_id` = %u, "
"`hp` = %u, `mana` = %u, `size` = %f",
client->CharacterID(), petinfo->Name, petinfo->petpower, petinfo->SpellID,
petinfo->HP, petinfo->Mana, petinfo->size, petinfo->Name, petinfo->petpower,
petinfo->SpellID, petinfo->HP, petinfo->Mana, petinfo->size);
results = database.QueryDatabase(query);
if(!results.Success())
return;
for(int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) {
if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0)
continue;
query = StringFormat("INSERT INTO `character_pet_buffs` "
"(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, "
"`ticsremaining`, `counters`) "
"VALUES (%u, 0, %u, %u, %u, %u, %d)",
client->CharacterID(), index, petinfo->Buffs[index].spellid,
petinfo->Buffs[index].level, petinfo->Buffs[index].duration,
petinfo->Buffs[index].counters);
database.QueryDatabase(query);
}
for(int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) {
if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0)
continue;
query = StringFormat("INSERT INTO `character_pet_buffs` "
"(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, "
"`ticsremaining`, `counters`) "
"VALUES (%u, 1, %u, %u, %u, %u, %d)",
client->CharacterID(), index, suspended->Buffs[index].spellid,
suspended->Buffs[index].level, suspended->Buffs[index].duration,
suspended->Buffs[index].counters);
database.QueryDatabase(query);
}
for (int index = EmuConstants::EQUIPMENT_BEGIN; index <= EmuConstants::EQUIPMENT_END; index++) {
if(!petinfo->Items[index])
continue;
query = StringFormat("INSERT INTO `character_pet_inventory` "
"(`char_id`, `pet`, `slot`, `item_id`) "
"VALUES (%u, 0, %u, %u)",
client->CharacterID(), index, petinfo->Items[index]);
database.QueryDatabase(query);
}
query = StringFormat("INSERT INTO `character_pet_info` "
"(`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) "
"VALUES (%u, 1, '%s', %u, %u, %u, %u, %f) "
"ON DUPLICATE KEY UPDATE "
"`petname`='%s', `petpower`=%i, `spell_id`=%u, `hp`=%u, `mana`=%u, `size`=%f",
client->CharacterID(), suspended->Name, suspended->petpower, suspended->SpellID,
suspended->HP, suspended->Mana, suspended->size, suspended->Name, suspended->petpower,
suspended->SpellID, suspended->HP, suspended->Mana, suspended->size);
results = database.QueryDatabase(query);
if(!results.Success())
return;
for (int index = EmuConstants::EQUIPMENT_BEGIN; index <= EmuConstants::EQUIPMENT_END; index++) {
if(!suspended->Items[index])
continue;
query = StringFormat("INSERT INTO `character_pet_inventory` "
"(`char_id`, `pet`, `slot`, `item_id`) "
"VALUES (%u, 1, %u, %u)",
client->CharacterID(), index, suspended->Items[index]);
database.QueryDatabase(query);
}
}
void ZoneDatabase::RemoveTempFactions(Client *client) {
std::string query = StringFormat("DELETE FROM faction_values "
"WHERE temp = 1 AND char_id = %u",
client->CharacterID());
auto results = QueryDatabase(query);
if (!results.Success())
std::cerr << "Error in RemoveTempFactions query '" << query << "' " << results.ErrorMessage() << std::endl;
}
void ZoneDatabase::LoadPetInfo(Client *c) {
// Load current pet and suspended pet
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
PetInfo *petinfo = c->GetPetInfo(0);
PetInfo *suspended = c->GetPetInfo(1);
PetInfo *pi;
uint16 pet;
memset(petinfo, 0, sizeof(PetInfo));
memset(suspended, 0, sizeof(PetInfo));
if(database.RunQuery(query, MakeAnyLenString(&query,
"SELECT `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size` from `character_pet_info` where `char_id`=%u",
c->CharacterID()), errbuf, &result))
{
safe_delete_array(query);
while ((row = mysql_fetch_row(result))) {
pet = atoi(row[0]);
if (pet == 0)
pi = petinfo;
else if (pet == 1)
pi = suspended;
else
continue;
strncpy(pi->Name,row[1],64);
pi->petpower = atoi(row[2]);
pi->SpellID = atoi(row[3]);
pi->HP = atoul(row[4]);
pi->Mana = atoul(row[5]);
pi->size = atof(row[6]);
}
mysql_free_result(result);
}
else
{
LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf);
safe_delete_array(query);
return;
}
if (RunQuery(query, MakeAnyLenString(&query,
"SELECT `pet`, `slot`, `spell_id`, `caster_level`, `castername`, "
"`ticsremaining`, `counters` FROM `character_pet_buffs` "
"WHERE `char_id`=%u",
c->CharacterID()), errbuf, &result))
{
safe_delete_array(query);
while ((row = mysql_fetch_row(result)))
{
pet = atoi(row[0]);
if (pet == 0)
pi = petinfo;
else if (pet == 1)
pi = suspended;
else
continue;
uint32 slot_id = atoul(row[1]);
if(slot_id >= RuleI(Spells, MaxTotalSlotsPET)) {
continue;
}
uint32 spell_id = atoul(row[2]);
if(!IsValidSpell(spell_id)) {
continue;
}
uint32 caster_level = atoi(row[3]);
int caster_id = 0;
// The castername field is currently unused
//Client *caster = entity_list.GetClientByName(row[4]);
//if (caster) { caster_id = caster->GetID(); }
uint32 ticsremaining = atoul(row[5]);
uint32 counters = atoul(row[6]);
pi->Buffs[slot_id].spellid = spell_id;
pi->Buffs[slot_id].level = caster_level;
pi->Buffs[slot_id].player_id = caster_id;
pi->Buffs[slot_id].slotid = 2; // Always 2 in buffs struct for real buffs
pi->Buffs[slot_id].duration = ticsremaining;
pi->Buffs[slot_id].counters = counters;
}
mysql_free_result(result);
}
else {
LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf);
safe_delete_array(query);
return;
}
if (database.RunQuery(query, MakeAnyLenString(&query,
"SELECT `pet`, `slot`, `item_id` FROM `character_pet_inventory` WHERE `char_id`=%u",
c->CharacterID()), errbuf, &result))
{
safe_delete_array(query);
while((row = mysql_fetch_row(result))) {
pet = atoi(row[0]);
if (pet == 0)
pi = petinfo;
else if (pet == 1)
pi = suspended;
else
continue;
int slot = atoi(row[1]);
if (slot < EmuConstants::EQUIPMENT_BEGIN || slot > EmuConstants::EQUIPMENT_END)
continue;
pi->Items[slot] = atoul(row[2]);
}
mysql_free_result(result);
}
else {
LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf);
safe_delete_array(query);
return;
}
}
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;
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);
std::map<std::string, int16>::iterator 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);
std::map<std::string, int16>::iterator 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;
}
bool ZoneDatabase::LoadFactionValues(uint32 char_id, faction_map & val_list) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT faction_id,current_value FROM faction_values WHERE char_id = %i",char_id), errbuf, &result)) {
safe_delete_array(query);
bool ret = LoadFactionValues_result(result, val_list);
mysql_free_result(result);
return ret;
}
else {
std::cerr << "Error in LoadFactionValues query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
}
return false;
}
bool ZoneDatabase::LoadFactionValues_result(MYSQL_RES* result, faction_map & val_list) {
MYSQL_ROW row;
while((row = mysql_fetch_row(result))) {
val_list[atoi(row[0])] = atoi(row[1]);
}
return true;
}
//o--------------------------------------------------------------
//| Name: GetFactionName; rembrant, 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;
}
//o--------------------------------------------------------------
//| Name: GetNPCFactionList; rembrant, 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; rembrant, 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)
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
uint32 affected_rows = 0;
if (!RunQuery(query, MakeAnyLenString(&query,
"DELETE FROM faction_values WHERE char_id=%i AND faction_id = %i",
char_id, faction_id), errbuf)) {
std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return false;
}
if(value == 0)
{
safe_delete_array(query);
return true;
}
if(temp == 2)
temp = 0;
if(temp == 3)
temp = 1;
if (!RunQuery(query, MakeAnyLenString(&query,
"INSERT INTO faction_values (char_id,faction_id,current_value,temp) VALUES (%i,%i,%i,%i)",
char_id, faction_id,value,temp), errbuf, 0, &affected_rows)) {
std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return false;
}
safe_delete_array(query);
if (affected_rows == 0)
{
return false;
}
val_list[faction_id] = value;
return(true);
}
bool ZoneDatabase::LoadFactionData()
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
query = new char[256];
strcpy(query, "SELECT MAX(id) FROM faction_list");
if (RunQuery(query, strlen(query), errbuf, &result)) {
safe_delete_array(query);
row = mysql_fetch_row(result);
if (row && row[0])
{
max_faction = atoi(row[0]);
faction_array = new Faction*[max_faction+1];
for(unsigned int i=0; i<max_faction; i++)
{
faction_array[i] = nullptr;
}
mysql_free_result(result);
MakeAnyLenString(&query, "SELECT id,name,base FROM faction_list");
if (RunQuery(query, strlen(query), errbuf, &result))
{
safe_delete_array(query);
while((row = mysql_fetch_row(result)))
{
uint32 index = atoi(row[0]);
faction_array[index] = new Faction;
strn0cpy(faction_array[index]->name, row[1], 50);
faction_array[index]->base = atoi(row[2]);
char sec_errbuf[MYSQL_ERRMSG_SIZE];
MYSQL_RES *sec_result;
MYSQL_ROW sec_row;
MakeAnyLenString(&query, "SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id=%u", index);
if (RunQuery(query, strlen(query), sec_errbuf, &sec_result)) {
while((sec_row = mysql_fetch_row(sec_result)))
{
faction_array[index]->mods[sec_row[1]] = atoi(sec_row[0]);
}
mysql_free_result(sec_result);
}
safe_delete_array(query);
}
mysql_free_result(result);
}
else {
std::cerr << "Error in LoadFactionData '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return false;
}
}
else {
mysql_free_result(result);
}
}
else {
std::cerr << "Error in LoadFactionData '" << query << "' " << errbuf << std::endl;
safe_delete_array(query);
return false;
}
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;
}