eqemu-server/world/worlddb.cpp

909 lines
26 KiB
C++

/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "worlddb.h"
#include "../common/strings.h"
#include "../common/eq_packet_structs.h"
#include "../common/inventory_profile.h"
#include "../common/rulesys.h"
#include <iostream>
#include <cstdlib>
#include <vector>
#include "sof_char_create_data.h"
#include "../common/repositories/character_instance_safereturns_repository.h"
#include "../common/repositories/inventory_repository.h"
#include "../common/repositories/criteria/content_filter_criteria.h"
#include "../common/zone_store.h"
#include "../common/repositories/character_data_repository.h"
#include "../common/repositories/character_bind_repository.h"
#include "../common/repositories/character_material_repository.h"
#include "../common/repositories/start_zones_repository.h"
WorldDatabase database;
WorldDatabase content_db;
extern std::vector<RaceClassAllocation> character_create_allocations;
extern std::vector<RaceClassCombos> character_create_race_class_combos;
void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit)
{
EQ::versions::ClientVersion
client_version = EQ::versions::ConvertClientVersionBitToClientVersion(client_version_bit);
size_t character_limit = EQ::constants::StaticLookup(client_version)->CharacterCreationLimit;
if (character_limit > EQ::constants::CHARACTER_CREATION_LIMIT) {
character_limit = EQ::constants::CHARACTER_CREATION_LIMIT;
}
// Force Titanium clients to use '8'
if (client_version == EQ::versions::ClientVersion::Titanium) {
character_limit = 8;
}
auto characters = CharacterDataRepository::GetWhere(
database,
fmt::format(
"`account_id` = {} AND `deleted_at` IS NULL ORDER BY `name` LIMIT {}",
account_id,
character_limit
)
);
size_t character_count = characters.size();
if (characters.empty()) {
*out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
auto *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer;
cs->CharCount = 0;
cs->TotalChars = character_limit;
return;
}
std::vector<uint32_t> character_ids;
for (auto &e: characters) {
character_ids.push_back(e.id);
}
const auto& inventories = InventoryRepository::GetWhere(
*this,
fmt::format(
"`character_id` IN ({}) AND `slot_id` BETWEEN {} AND {}",
Strings::Join(character_ids, ","),
EQ::invslot::slotHead,
EQ::invslot::slotFeet
)
);
const auto& character_binds = CharacterBindRepository::GetWhere(
*this,
fmt::format(
"`id` IN ({}) ORDER BY id, slot",
Strings::Join(character_ids, ",")
)
);
const auto& character_materials = CharacterMaterialRepository::GetWhere(
*this,
fmt::format(
"`id` IN ({}) ORDER BY id, slot",
Strings::Join(character_ids, ",")
)
);
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count);
*out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size);
unsigned char *buff_ptr = (*out_app)->pBuffer;
auto *cs = (CharacterSelect_Struct *) buff_ptr;
cs->CharCount = character_count;
cs->TotalChars = character_limit;
buff_ptr += sizeof(CharacterSelect_Struct);
for (auto &e: characters) {
auto *cse = (CharacterSelectEntry_Struct *) buff_ptr;
PlayerProfile_Struct pp;
EQ::InventoryProfile inv;
pp.SetPlayerProfileVersion(EQ::versions::ConvertClientVersionToMobVersion(client_version));
inv.SetInventoryVersion(client_version);
inv.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
uint32 character_id = e.id;
uint8 has_home = 0;
uint8 has_bind = 0;
memset(&pp, 0, sizeof(PlayerProfile_Struct));
memset(cse->Name, 0, sizeof(cse->Name));
strcpy(cse->Name, e.name.c_str());
cse->Class = e.class_;
cse->Race = e.race;
cse->Level = e.level;
cse->ShroudClass = cse->Class;
cse->ShroudRace = cse->Race;
cse->Zone = e.zone_id;
cse->Instance = 0;
cse->Gender = e.gender;
cse->Face = e.face;
for (auto &s: cse->Equip) {
s.Material = 0;
s.Unknown1 = 0;
s.EliteModel = 0;
s.HerosForgeModel = 0;
s.Unknown2 = 0;
s.Color = 0;
}
cse->Unknown15 = 0xFF;
cse->Unknown19 = 0xFF;
cse->DrakkinTattoo = e.drakkin_tattoo;
cse->DrakkinDetails = e.drakkin_details;
cse->Deity = e.deity;
cse->PrimaryIDFile = 0; // Processed Below
cse->SecondaryIDFile = 0; // Processed Below
cse->HairColor = e.hair_color;
cse->BeardColor = e.beard_color;
cse->EyeColor1 = e.eye_color_1;
cse->EyeColor2 = e.eye_color_2;
cse->HairStyle = e.hair_style;
cse->Beard = e.beard;
cse->GoHome = 0; // Processed Below
cse->Tutorial = 0; // Processed Below
cse->DrakkinHeritage = e.drakkin_heritage;
cse->Unknown1 = 0;
cse->Enabled = 1;
cse->LastLogin = e.last_login; // RoF2 value: 1212696584
cse->Unknown2 = 0;
if (RuleB(World, EnableReturnHomeButton)) {
int now = time(nullptr);
if (now - e.last_login >= RuleI(World, MinOfflineTimeToReturnHome)) {
cse->GoHome = 1;
}
}
if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) {
cse->Tutorial = 1;
}
// binds
int bind_count = 0;
for (auto &bind : character_binds) {
if (bind.id != e.id) {
continue;
}
if (bind.slot == 4) {
has_home = 1;
pp.binds[4].zone_id = bind.zone_id;
pp.binds[4].instance_id = bind.instance_id;
pp.binds[4].x = bind.x;
pp.binds[4].y = bind.y;
pp.binds[4].z = bind.z;
pp.binds[4].heading = bind.heading;
}
if (bind.slot == 0) {
has_bind = 1;
}
bind_count++;
}
if (has_home == 0 || has_bind == 0) {
const auto &start_zones = StartZonesRepository::GetWhere(
content_db,
fmt::format(
"`player_class` = {} AND `player_deity` = {} AND `player_race` = {} {}",
cse->Class,
cse->Deity,
cse->Race,
ContentFilterCriteria::apply().c_str()
)
);
if (!start_zones.empty()) {
pp.binds[4].zone_id = start_zones[0].zone_id;
pp.binds[4].x = start_zones[0].x;
pp.binds[4].y = start_zones[0].y;
pp.binds[4].z = start_zones[0].z;
pp.binds[4].heading = start_zones[0].heading;
if (start_zones[0].bind_id != 0) {
pp.binds[4].zone_id = start_zones[0].bind_id;
auto z = GetZone(pp.binds[4].zone_id);
if (z) {
pp.binds[4].x = z->safe_x;
pp.binds[4].y = z->safe_y;
pp.binds[4].z = z->safe_z;
pp.binds[4].heading = z->safe_heading;
}
} else {
pp.binds[4].zone_id = start_zones[0].zone_id;
pp.binds[4].x = start_zones[0].x;
pp.binds[4].y = start_zones[0].y;
pp.binds[4].z = start_zones[0].z;
pp.binds[4].heading = start_zones[0].heading;
if (pp.binds[4].x == 0 && pp.binds[4].y == 0 && pp.binds[4].z == 0 && pp.binds[4].heading == 0) {
auto zone = GetZone(pp.binds[4].zone_id);
if (zone) {
pp.binds[4].x = zone->safe_x;
pp.binds[4].y = zone->safe_y;
pp.binds[4].z = zone->safe_z;
pp.binds[4].heading = zone->safe_heading;
}
}
}
}
else {
LogError("No start zone found for class [{}] deity [{}] race [{}]", cse->Class, cse->Deity, cse->Race);
}
pp.binds[0] = pp.binds[4];
// If we don't have home set, set it
if (has_home == 0) {
auto bind = CharacterBindRepository::NewEntity();
bind.id = character_id;
bind.zone_id = pp.binds[4].zone_id;
bind.instance_id = 0;
bind.x = pp.binds[4].x;
bind.y = pp.binds[4].y;
bind.z = pp.binds[4].z;
bind.heading = pp.binds[4].heading;
bind.slot = 4;
CharacterBindRepository::ReplaceOne(*this, bind);
}
// If we don't have regular bind set, set it
if (has_bind == 0) {
auto bind = CharacterBindRepository::NewEntity();
bind.id = character_id;
bind.zone_id = pp.binds[0].zone_id;
bind.instance_id = 0;
bind.x = pp.binds[0].x;
bind.y = pp.binds[0].y;
bind.z = pp.binds[0].z;
bind.heading = pp.binds[0].heading;
bind.slot = 0;
CharacterBindRepository::ReplaceOne(*this, bind);
}
}
// If our bind count is less than 5, then we have null data that needs to be filled in
if (bind_count < 5) {
// we know that home and main bind must be valid here, so we don't check those
// we also use home to fill in the null data like live does.
std::vector<CharacterBindRepository::CharacterBind> binds;
for (int i = 1; i < 4; i++) {
if (pp.binds[i].zone_id != 0) { // we assume 0 is the only invalid one ...
continue;
}
auto bind = CharacterBindRepository::NewEntity();
bind.slot = i;
bind.id = character_id;
bind.zone_id = pp.binds[4].zone_id;
bind.instance_id = 0;
bind.x = pp.binds[4].x;
bind.y = pp.binds[4].y;
bind.z = pp.binds[4].z;
bind.heading = pp.binds[4].heading;
binds.emplace_back(bind);
}
CharacterBindRepository::ReplaceMany(*this, binds);
}
for (auto &cm : character_materials) {
pp.item_tint.Slot[cm.slot].Red = cm.red;
pp.item_tint.Slot[cm.slot].Green = cm.green;
pp.item_tint.Slot[cm.slot].Blue = cm.blue;
pp.item_tint.Slot[cm.slot].UseTint = cm.use_tint;
pp.item_tint.Slot[cm.slot].Color = cm.color;
}
if (GetCharSelInventory(inventories, e, &inv)) {
const EQ::ItemData *item = nullptr;
const EQ::ItemInstance *inst = nullptr;
int16 inventory_slot = 0;
for (uint32 matslot = EQ::textures::textureBegin; matslot < EQ::textures::materialCount; matslot++) {
inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(matslot);
if (inventory_slot == INVALID_INDEX) { continue; }
inst = inv.GetItem(inventory_slot);
if (inst == nullptr) {
continue;
}
item = inst->GetItem();
if (item == nullptr) {
continue;
}
if (matslot > 6) {
uint32 item_id_file = 0;
// Weapon Models
if (inst->GetOrnamentationIDFile() != 0) {
item_id_file = inst->GetOrnamentationIDFile();
cse->Equip[matslot].Material = item_id_file;
}
else {
if (strlen(item->IDFile) > 2) {
item_id_file = Strings::ToInt(&item->IDFile[2]);
cse->Equip[matslot].Material = item_id_file;
}
}
if (matslot == EQ::textures::weaponPrimary) {
cse->PrimaryIDFile = item_id_file;
}
else {
cse->SecondaryIDFile = item_id_file;
}
}
else {
// Armor Materials/Models
uint32 color = (
pp.item_tint.Slot[matslot].UseTint ?
pp.item_tint.Slot[matslot].Color :
inst->GetColor()
);
cse->Equip[matslot].Material = item->Material;
cse->Equip[matslot].EliteModel = item->EliteMaterial;
cse->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
cse->Equip[matslot].Color = color;
}
}
}
else {
printf("Error loading inventory for %s\n", cse->Name);
}
buff_ptr += sizeof(CharacterSelectEntry_Struct);
}
}
int WorldDatabase::MoveCharacterToBind(int character_id, uint8 bind_number)
{
/* if an invalid bind point is specified, use the primary bind */
if (bind_number > 4)
bind_number = 0;
std::string query = fmt::format(
SQL(
SELECT
zone_id, instance_id, x, y, z, heading
FROM
character_bind
WHERE
id = {}
AND
slot = {}
LIMIT 1
),
character_id,
bind_number
);
auto results = database.QueryDatabase(query);
if(!results.Success() || results.RowCount() == 0) {
return 0;
}
int zone_id, instance_id;
double x, y, z, heading;
for (auto row = results.begin(); row != results.end(); ++row) {
zone_id = Strings::ToInt(row[0]);
instance_id = Strings::ToInt(row[1]);
x = Strings::ToFloat(row[2]);
y = Strings::ToFloat(row[3]);
z = Strings::ToFloat(row[4]);
heading = Strings::ToFloat(row[5]);
}
query = fmt::format(
SQL(
UPDATE
`character_data`
SET
`zone_id` = {},
`zone_instance` = {},
`x` = {},
`y` = {},
`z` = {},
`heading` = {}
WHERE `id` = {}
),
zone_id,
instance_id,
x,
y,
z,
heading,
character_id
);
results = database.QueryDatabase(query);
if(!results.Success()) {
return 0;
}
return zone_id;
}
int WorldDatabase::MoveCharacterToInstanceSafeReturn(
int character_id,
int instance_zone_id,
int instance_id
)
{
int zone_id = 0;
// only moves if safe return is for specified zone instance
auto entries = CharacterInstanceSafereturnsRepository::GetWhere(
database,
fmt::format(
"character_id = {} AND instance_zone_id = {} AND instance_id = {} AND safe_zone_id > 0",
character_id, instance_zone_id, instance_id
)
);
if (!entries.empty()) {
auto entry = entries.front();
auto results = QueryDatabase(
fmt::format(
SQL(
UPDATE character_data
SET zone_id = {}, zone_instance = 0, x = {}, y = {}, z = {}, heading = {}
WHERE id = {};
),
entry.safe_zone_id,
entry.safe_x,
entry.safe_y,
entry.safe_z,
entry.safe_heading,
character_id
)
);
if (results.Success() && results.RowsAffected() > 0) {
zone_id = entry.safe_zone_id;
}
}
if (zone_id == 0) {
zone_id = MoveCharacterToBind(character_id);
}
return zone_id;
}
bool WorldDatabase::GetStartZone(
PlayerProfile_Struct *pp,
CharCreate_Struct *p_char_create_struct,
bool is_titanium
)
{
// SoF doesn't send the player_choice field in character creation, it now sends the real zoneID instead.
//
// For SoF, search for an entry in start_zones with a matching zone_id, class, race and deity.
//
// For now, if no row matching row is found, send them to Crescent Reach, as that is probably the most likely
// reason for no match being found.
//
if (!pp || !p_char_create_struct) {
return false;
}
pp->x = 0;
pp->y = 0;
pp->z = 0;
pp->heading = 0;
pp->zone_id = 0;
pp->binds[0].x = 0;
pp->binds[0].y = 0;
pp->binds[0].z = 0;
pp->binds[0].zone_id = 0;
pp->binds[0].instance_id = 0;
// see if we have an entry for start_zone. We can support both titanium & SOF+ by having two entries per class/race/deity combo with different zone_ids
std::string query;
if (is_titanium) {
// Titanium sends player choice (starting city) instead of a zone id
query = StringFormat(
"SELECT x, y, z, heading, start_zone, bind_id, bind_x, bind_y, bind_z FROM start_zones WHERE player_choice = %i "
"AND player_class = %i AND player_deity = %i AND player_race = %i %s",
p_char_create_struct->start_zone,
p_char_create_struct->class_,
p_char_create_struct->deity,
p_char_create_struct->race,
ContentFilterCriteria::apply().c_str()
);
LogInfo("Titanium Start zone query: [{}]\n", query.c_str());
}
else {
query = StringFormat(
"SELECT x, y, z, heading, start_zone, bind_id, bind_x, bind_y, bind_z FROM start_zones WHERE zone_id = %i "
"AND player_class = %i AND player_deity = %i AND player_race = %i %s",
p_char_create_struct->start_zone,
p_char_create_struct->class_,
p_char_create_struct->deity,
p_char_create_struct->race,
ContentFilterCriteria::apply().c_str()
);
LogInfo("SoF Start zone query: [{}]\n", query.c_str());
}
auto results = QueryDatabase(query);
if(!results.Success()) {
return false;
}
LogInfo("SoF Start zone query: [{}]\n", query.c_str());
if (results.RowCount() == 0) {
printf("No start_zones entry in database, using defaults\n");
is_titanium ? SetTitaniumDefaultStartZone(pp, p_char_create_struct) : SetSoFDefaultStartZone(pp, p_char_create_struct);
}
else {
LogInfo("Found starting location in start_zones");
auto row = results.begin();
pp->x = Strings::ToFloat(row[0]);
pp->y = Strings::ToFloat(row[1]);
pp->z = Strings::ToFloat(row[2]);
pp->heading = Strings::ToFloat(row[3]);
pp->zone_id = Strings::ToInt(row[4]);
pp->binds[0].zone_id = Strings::ToInt(row[5]);
pp->binds[0].x = Strings::ToFloat(row[6]);
pp->binds[0].y = Strings::ToFloat(row[7]);
pp->binds[0].z = Strings::ToFloat(row[8]);
pp->binds[0].heading = Strings::ToFloat(row[3]);
}
if (
pp->x == 0 &&
pp->y == 0 &&
pp->z == 0 &&
pp->heading == 0
) {
auto zone = GetZone(pp->zone_id);
if (zone) {
pp->x = zone->safe_x;
pp->y = zone->safe_y;
pp->z = zone->safe_z;
pp->heading = zone->safe_heading;
}
}
if (
pp->binds[0].x == 0 &&
pp->binds[0].y == 0 &&
pp->binds[0].z == 0 &&
pp->binds[0].heading == 0
) {
auto zone = GetZone(pp->binds[0].zone_id);
if (zone) {
pp->binds[0].x = zone->safe_x;
pp->binds[0].y = zone->safe_y;
pp->binds[0].z = zone->safe_z;
pp->binds[0].heading = zone->safe_heading;
}
}
return true;
}
void WorldDatabase::SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc){
int sof_start_zone_id = RuleI(World, SoFStartZoneID);
if (sof_start_zone_id > 0) {
in_pp->zone_id = sof_start_zone_id;
in_cc->start_zone = in_pp->zone_id;
}
else if (in_cc->start_zone == RuleI(World, TutorialZoneID)) {
in_pp->zone_id = in_cc->start_zone;
}
else {
in_pp->x = in_pp->binds[0].x = -51.0f;
in_pp->y = in_pp->binds[0].y = -20.0f;
in_pp->z = in_pp->binds[0].z = 0.79f;
in_pp->heading = in_pp->binds[0].heading = 0.0f;
in_pp->zone_id = in_pp->binds[0].zone_id = Zones::CRESCENT; // Crescent Reach.
}
}
void WorldDatabase::SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc)
{
int titanium_start_zone_id = RuleI(World, TitaniumStartZoneID);
if (titanium_start_zone_id > 0) {
in_pp->zone_id = titanium_start_zone_id;
in_pp->binds[0].zone_id = titanium_start_zone_id;
} else {
switch (in_cc->start_zone)
{
case StartZoneIndex::Odus:
{
if (in_cc->deity == Deity::CazicThule) // Cazic-Thule Erudites go to Paineel
{
in_pp->zone_id = Zones::PAINEEL; // paineel
in_pp->binds[0].zone_id = Zones::PAINEEL;
}
else
{
in_pp->zone_id = Zones::ERUDNEXT; // erudnext
in_pp->binds[0].zone_id = Zones::TOX; // tox
}
break;
}
case StartZoneIndex::Qeynos:
{
in_pp->zone_id = Zones::QEYNOS2; // qeynos2
in_pp->binds[0].zone_id = Zones::QEYNOS2; // qeynos2
break;
}
case StartZoneIndex::Halas:
{
in_pp->zone_id = Zones::HALAS; // halas
in_pp->binds[0].zone_id = Zones::EVERFROST; // everfrost
break;
}
case StartZoneIndex::Rivervale:
{
in_pp->zone_id = Zones::RIVERVALE; // rivervale
in_pp->binds[0].zone_id = Zones::KITHICOR; // kithicor
break;
}
case StartZoneIndex::Freeport:
{
in_pp->zone_id = Zones::FREPORTW; // freportw
in_pp->binds[0].zone_id = Zones::FREPORTW; // freportw
break;
}
case StartZoneIndex::Neriak:
{
in_pp->zone_id = Zones::NERIAKA; // neriaka
in_pp->binds[0].zone_id = Zones::NEKTULOS; // nektulos
break;
}
case StartZoneIndex::Grobb:
{
in_pp->zone_id = Zones::GROBB; // grobb
in_pp->binds[0].zone_id = Zones::INNOTHULE; // innothule
break;
}
case StartZoneIndex::Oggok:
{
in_pp->zone_id = Zones::OGGOK; // oggok
in_pp->binds[0].zone_id = Zones::FEERROTT; // feerrott
break;
}
case StartZoneIndex::Kaladim:
{
in_pp->zone_id = Zones::KALADIMA; // kaladima
in_pp->binds[0].zone_id = Zones::BUTCHER; // butcher
break;
}
case StartZoneIndex::GreaterFaydark:
{
in_pp->zone_id = Zones::GFAYDARK; // gfaydark
in_pp->binds[0].zone_id = Zones::GFAYDARK; // gfaydark
break;
}
case StartZoneIndex::Felwithe:
{
in_pp->zone_id = Zones::FELWITHEA; // felwithea
in_pp->binds[0].zone_id = Zones::GFAYDARK; // gfaydark
break;
}
case StartZoneIndex::Akanon:
{
in_pp->zone_id = Zones::AKANON; // akanon
in_pp->binds[0].zone_id = Zones::STEAMFONT; // steamfont
break;
}
case StartZoneIndex::Cabilis:
{
in_pp->zone_id = Zones::CABWEST; // cabwest
in_pp->binds[0].zone_id = Zones::FIELDOFBONE; // fieldofbone
break;
}
case StartZoneIndex::SharVahl:
{
in_pp->zone_id = Zones::SHARVAHL; // sharvahl
in_pp->binds[0].zone_id = Zones::SHARVAHL; // sharvahl
break;
}
}
}
}
void WorldDatabase::GetLauncherList(std::vector<std::string> &rl) {
rl.clear();
const std::string query = "SELECT name FROM launcher";
auto results = QueryDatabase(query);
if (!results.Success()) {
LogError("WorldDatabase::GetLauncherList: {}", results.ErrorMessage().c_str());
return;
}
for (auto row = results.begin(); row != results.end(); ++row)
rl.push_back(row[0]);
}
bool WorldDatabase::GetCharacterLevel(const char *name, int &level)
{
std::string query = StringFormat("SELECT level FROM character_data WHERE name = '%s'", name);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogError("WorldDatabase::GetCharacterLevel: {}", results.ErrorMessage().c_str());
return false;
}
if (results.RowCount() == 0)
return false;
auto row = results.begin();
level = Strings::ToInt(row[0]);
return true;
}
bool WorldDatabase::LoadCharacterCreateAllocations()
{
character_create_allocations.clear();
std::string query = "SELECT * FROM char_create_point_allocations ORDER BY id";
auto results = QueryDatabase(query);
if (!results.Success())
return false;
for (auto row = results.begin(); row != results.end(); ++row) {
RaceClassAllocation allocate;
allocate.Index = Strings::ToInt(row[0]);
allocate.BaseStats[0] = Strings::ToInt(row[1]);
allocate.BaseStats[3] = Strings::ToInt(row[2]);
allocate.BaseStats[1] = Strings::ToInt(row[3]);
allocate.BaseStats[2] = Strings::ToInt(row[4]);
allocate.BaseStats[4] = Strings::ToInt(row[5]);
allocate.BaseStats[5] = Strings::ToInt(row[6]);
allocate.BaseStats[6] = Strings::ToInt(row[7]);
allocate.DefaultPointAllocation[0] = Strings::ToInt(row[8]);
allocate.DefaultPointAllocation[3] = Strings::ToInt(row[9]);
allocate.DefaultPointAllocation[1] = Strings::ToInt(row[10]);
allocate.DefaultPointAllocation[2] = Strings::ToInt(row[11]);
allocate.DefaultPointAllocation[4] = Strings::ToInt(row[12]);
allocate.DefaultPointAllocation[5] = Strings::ToInt(row[13]);
allocate.DefaultPointAllocation[6] = Strings::ToInt(row[14]);
character_create_allocations.push_back(allocate);
}
return true;
}
bool WorldDatabase::LoadCharacterCreateCombos()
{
character_create_race_class_combos.clear();
std::string query = "SELECT * FROM char_create_combinations ORDER BY race, class, deity, start_zone";
auto results = QueryDatabase(query);
if (!results.Success())
return false;
for (auto row = results.begin(); row != results.end(); ++row) {
RaceClassCombos combo;
combo.AllocationIndex = Strings::ToInt(row[0]);
combo.Race = Strings::ToInt(row[1]);
combo.Class = Strings::ToInt(row[2]);
combo.Deity = Strings::ToInt(row[3]);
combo.Zone = Strings::ToInt(row[4]);
combo.ExpansionRequired = Strings::ToInt(row[5]);
character_create_race_class_combos.push_back(combo);
}
return true;
}
bool WorldDatabase::GetCharSelInventory(
const std::vector<InventoryRepository::Inventory> &inventories,
const CharacterDataRepository::CharacterData &character,
EQ::InventoryProfile *inv
)
{
if (inventories.empty() || !character.id || !inv) {
return false;
}
for (const auto& e : inventories) {
if (e.character_id != character.id) {
continue;
}
switch (e.slot_id) {
case EQ::invslot::slotFace:
case EQ::invslot::slotEar2:
case EQ::invslot::slotNeck:
case EQ::invslot::slotShoulders:
case EQ::invslot::slotBack:
case EQ::invslot::slotFinger1:
case EQ::invslot::slotFinger2:
continue;
default:
break;
}
uint32 augment_ids[EQ::invaug::SOCKET_COUNT] = {
e.augment_one,
e.augment_two,
e.augment_three,
e.augment_four,
e.augment_five,
e.augment_six
};
const EQ::ItemData* item = content_db.GetItem(e.item_id);
if (!item) {
continue;
}
EQ::ItemInstance *inst = content_db.CreateBaseItem(item, e.charges);
if (!inst) {
continue;
}
inst->SetCharges(e.charges);
if (e.color > 0) {
inst->SetColor(e.color);
}
if (item->IsClassCommon()) {
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
if (augment_ids[i]) {
inst->PutAugment(this, i, augment_ids[i]);
}
}
}
inst->SetAttuned(e.instnodrop);
if (!e.custom_data.empty()) {
inst->SetCustomDataString(e.custom_data);
}
inst->SetOrnamentIcon(e.ornament_icon);
inst->SetOrnamentationIDFile(e.ornament_idfile);
inst->SetOrnamentHeroModel(e.ornament_hero_model);
inv->PutItem(e.slot_id, *inst);
safe_delete(inst);
}
return true;
}