gdle/Source/Client.cpp
LikeableLime 40f21f9a0f Fix for gearknight armor model updating on unequip.
Fixed Sanamar new character start location.

Crash fix for void foci in player logon.

nullptr check in magic absorb check.

Added support for Award Luminance emote type as well as on kill and fellow sharing.

Fixed players able to leave and come back to training academy.
2018-12-18 17:02:31 -06:00

1818 lines
67 KiB
C++

#include "StdAfx.h"
#include "easylogging++.h"
#include "InferredPortalData.h"
#include "Client.h"
#include "ClientEvents.h"
#include "Config.h"
#include <chrono>
// Network access
#include "Network.h"
#include "PacketController.h"
#include "BinaryReader.h"
#include "BinaryWriter.h"
// Database access
#include "Database.h"
#include "Database2.h"
#include "DatabaseIO.h"
#include "WeenieFactory.h"
// World access
#include "World.h"
// Player access
#include "WeenieObject.h"
#include "Monster.h"
#include "Player.h"
// Command access
#include "ClientCommands.h"
#include "ChatMsgs.h"
#include "AllegianceManager.h"
#include "Util.h"
// CClient - for client/server interaction
CClient::CClient(SOCKADDR_IN *peer, WORD slot, AccountInformation_t &accountInfo)
{
memcpy(&m_vars.addr, peer, sizeof(SOCKADDR_IN));
m_AccountInfo = accountInfo;
m_vars.slot = slot;
m_vars.account = accountInfo.username;
m_pPC = new CPacketController(this);
m_pEvents = new CClientEvents(this);
}
CClient::~CClient()
{
SafeDelete(m_pEvents);
SafeDelete(m_pPC);
}
void CClient::IncomingBlob(BlobPacket_s *blob, double recvTime)
{
if (!IsAlive())
return;
m_pPC->IncomingBlob(blob, recvTime);
}
void CClient::Think()
{
if (!IsAlive())
return;
WorldThink();
m_pPC->Think();
// packet control can signal to kill
if (!m_pPC->IsAlive())
Kill(__FILE__, __LINE__);
}
void CClient::ThinkOutbound()
{
if (!IsAlive())
return;
if (m_pPC && m_pPC - IsAlive())
{
m_pPC->ThinkOutbound();
}
}
void CClient::WorldThink()
{
if (!m_vars.bInWorld)
{
if (m_vars.bNeedChars)
{
UpdateLoginScreen();
m_vars.bNeedChars = false;
}
}
m_pEvents->Think();
}
const AccountInformation_t &CClient::GetAccountInfo()
{
return m_AccountInfo;
}
const std::list<CharacterDesc_t> &CClient::GetCharacters()
{
return m_Characters;
}
bool CClient::HasCharacter(DWORD character_weenie_id)
{
for (auto &character : m_Characters)
{
if (character.weenie_id == character_weenie_id)
return true;
}
return false;
}
DWORD CClient::IncCharacterInstanceTS(DWORD character_weenie_id)
{
WORD newInstanceTS = 0;
// just make this work
m_Characters = g_pDBIO->GetCharacterList(m_AccountInfo.id);
for (auto &character : m_Characters)
{
if (character.weenie_id == character_weenie_id)
{
newInstanceTS = character.instance_ts + 1;
g_pDBIO->SetCharacterInstanceTS(character.weenie_id, newInstanceTS);
}
}
// just make this work
m_Characters = g_pDBIO->GetCharacterList(m_AccountInfo.id);
return newInstanceTS;
}
void CClient::UpdateLoginScreen()
{
m_CharactersSent = m_Characters = g_pDBIO->GetCharacterList(m_AccountInfo.id);
BinaryWriter CharacterList;
CharacterList.Write<DWORD>(0xF658);
CharacterList.Write<DWORD>(0); // check what this is
CharacterList.Write<DWORD>(m_Characters.size());
for (auto &character : m_Characters)
{
CharacterList.Write<DWORD>(character.weenie_id);
CharacterList.WriteString(character.name);
CharacterList.Write<DWORD>(0); // delete period, TODO
}
CharacterList.Write<DWORD>(0);
CharacterList.Write<DWORD>(11); // max characters I'm assuming
CharacterList.WriteString(m_AccountInfo.username);
CharacterList.Write<DWORD>(1); // what are these
CharacterList.Write<DWORD>(1); // what are these
SendNetMessage(CharacterList.GetData(), CharacterList.GetSize(), PRIVATE_MSG);
BinaryWriter ServerName;
ServerName.Write<DWORD>(0xF7E1);
ServerName.Write<DWORD>(g_pWorld->GetNumPlayers()); // num connections
ServerName.Write<DWORD>(-1); // Max connections
ServerName.WriteString(g_pConfig->WorldName());
SendNetMessage(ServerName.GetData(), ServerName.GetSize(), PRIVATE_MSG);
BinaryWriter DDD_InterrogationMessage;
DDD_InterrogationMessage.Write<DWORD>(0xF7E5);
DDD_InterrogationMessage.Write<DWORD>(1); // servers region
DDD_InterrogationMessage.Write<DWORD>(1); // name rule language
DDD_InterrogationMessage.Write<DWORD>(1); // product id
DDD_InterrogationMessage.Write<DWORD>(2); // supports languages (2)
DDD_InterrogationMessage.Write<DWORD>(0); // language #1
DDD_InterrogationMessage.Write<DWORD>(1); // language #2
SendNetMessage(DDD_InterrogationMessage.GetData(), DDD_InterrogationMessage.GetSize(), EVENT_MSG);
}
void CClient::EnterWorld()
{
DWORD EnterWorld = 0xF7DF; // 0xF7C7;
SendNetMessage(&EnterWorld, sizeof(DWORD), 9);
SERVER_INFO << "Client" << m_vars.slot << "is entering the world.";
m_vars.bInWorld = TRUE;
BinaryWriter setChatChannels;
setChatChannels.Write<DWORD>(0x295);
setChatChannels.Write<DWORD>(Allegiance_ChatChannel);
setChatChannels.Write<DWORD>(General_ChatChannel);
setChatChannels.Write<DWORD>(Trade_ChatChannel);
setChatChannels.Write<DWORD>(LFG_ChatChannel);
setChatChannels.Write<DWORD>(Roleplay_ChatChannel);
setChatChannels.Write<DWORD>(Olthoi_ChatChannel);
setChatChannels.Write<DWORD>(Society_ChatChannel);
setChatChannels.Write<DWORD>(SocietyCelHan_ChatChannel);
setChatChannels.Write<DWORD>(SocietyEldWeb_ChatChannel);
setChatChannels.Write<DWORD>(SocietyRadBlo_ChatChannel);
SendNetMessage(&setChatChannels, PRIVATE_MSG, FALSE, FALSE);
}
void CClient::ExitWorld()
{
DWORD ExitWorld = 0xF653;
SendNetMessage(&ExitWorld, sizeof(DWORD), PRIVATE_MSG);
SERVER_INFO << "Client" << m_vars.slot << "is exiting the world.";
m_pPC->ResetEvent();
UpdateLoginScreen();
m_vars.bInWorld = FALSE;
}
void CClient::SendNetMessage(BinaryWriter* pMessage, WORD group, BOOL event, BOOL del)
{
if (!pMessage)
return;
SendNetMessage(pMessage->GetData(), pMessage->GetSize(), group, event);
if (del)
delete pMessage;
}
void CClient::SendNetMessage(void *data, DWORD length, WORD group, BOOL game_event)
{
if (!IsAlive())
return;
if (!data || !length)
return;
m_pPC->QueueNetMessage(data, length, group, game_event ? GetEvents()->GetPlayerID() : 0);
}
BOOL CClient::CheckBadName(const std::string name)
{
string ps = name;
std::transform(ps.begin(), ps.end(), ps.begin(), ::tolower);
ps = ReplaceInString(ps, " ", "");
ps = ReplaceInString(ps, "-", "");
ps = ReplaceInString(ps, "'", "");
ps = ReplaceInString(ps, "\"", "");
for (auto const& value : g_pPortalDataEx->GetBannedWords())
{
if (ps.find(value) != std::string::npos)
return false;
}
return true;
}
BOOL CClient::CheckNameValidity(const char *name, int access, std::string &resultName)
{
resultName = "";
if (!strncmp(name, "Contributor ", strlen("Contributor ")))
{
if (access >= ADVOCATE_ACCESS)
{
resultName = "+Contributor ";
name += strlen("Contributor ");
}
else
return FALSE;
}
int len = (int)strlen(name);
if ((len < 3) || (len > 32))
return FALSE;
int i = 0;
while (i < len)
{
char letter = name[i];
if (!(letter >= 'A' && letter <= 'Z') && !(letter >= 'a' || letter <= 'z') && !(letter == '\'') && !(letter == ' ') && !(letter == '-'))
break;
i++;
}
if (i == len)
{
resultName += name;
return TRUE;
}
return FALSE;
}
void CClient::DeleteCharacter(BinaryReader *pReader)
{
std::string account_name = pReader->ReadString(); // account
if (strcmp(account_name.c_str(), m_vars.account.c_str()))
return;
int slot = pReader->Read<int>();
if (slot < 0 || slot >= m_Characters.size() || slot >= m_CharactersSent.size())
return; // out of range
auto entry = m_CharactersSent.begin();
std::advance(entry, slot);
g_pAllegianceManager->BreakAllAllegiance(entry->weenie_id);
g_pDBIO->DeleteCharacter(entry->weenie_id);
g_pDBIO->DeleteWeenie(entry->weenie_id);
m_CharactersSent.erase(entry);
BinaryWriter response;
response.Write<DWORD>(0xF655);
SendNetMessage(response.GetData(), response.GetSize(), PRIVATE_MSG);
}
void CClient::CreateCharacter(BinaryReader *pReader)
{
DWORD errorCode = CG_VERIFICATION_RESPONSE_CORRUPT;
std::string account_name = pReader->ReadString();
if (strcmp(account_name.c_str(), m_vars.account.c_str()))
return;
ACCharGenResult cg;
cg.UnPack(pReader);
std::string resultName;
if (pReader->GetLastError())
goto BadData;
cg.strength = max(min(cg.strength, 100), 10);
cg.endurance = max(min(cg.endurance, 100), 10);
cg.coordination = max(min(cg.coordination, 100), 10);
cg.quickness = max(min(cg.quickness, 100), 10);
cg.focus = max(min(cg.focus, 100), 10);
cg.self = max(min(cg.self, 100), 10);
int totalAttribs = cg.strength + cg.endurance + cg.coordination + cg.quickness + cg.focus + cg.self;
if (totalAttribs > 330)
{
goto BadData;
}
//if (cg.heritageGroup <= 0 || cg.heritageGroup >= 14)
if (cg.heritageGroup <= 0 || cg.heritageGroup >= 12 && !g_pConfig->AllowOlthoi())
{
BinaryWriter response;
response.Write<DWORD>(0xF643);
response.Write<DWORD>(errorCode);
SendNetMessage(response.GetData(), response.GetSize(), PRIVATE_MSG);
BinaryWriter popupString;
popupString.Write<DWORD>(4);
popupString.WriteString("No Olthoi at this time.");
SendNetMessage(&popupString, PRIVATE_MSG, FALSE, FALSE);
return;
//goto BadData;
}
if (cg.gender < 0 || cg.gender >= 3) // allow invalid gender? not sure
{
goto BadData;
}
SkillTable *pSkillTable = SkillSystem::GetSkillTable();
int numCreditsUsed = 0;
cg.numSkills = max(0, min(100, cg.numSkills));
ACCharGenData *cgd = CachedCharGenData;
assert(cgd != NULL);
HeritageGroup_CG *heritageGroup = cgd->mHeritageGroupList.lookup(cg.heritageGroup);
Sex_CG *scg = NULL;
for (DWORD i = 0; i < cg.numSkills; i++)
{
switch (cg.skillAdvancementClasses[i])
{
case SKILL_ADVANCEMENT_CLASS::UNDEF_SKILL_ADVANCEMENT_CLASS:
case SKILL_ADVANCEMENT_CLASS::UNTRAINED_SKILL_ADVANCEMENT_CLASS:
case SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS:
case SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS:
break;
default:
goto BadData;
}
const SkillBase *pSkillBase = pSkillTable->GetSkillBaseRaw(SkillTable::OldToNewSkill((STypeSkill)i));
if (pSkillBase != NULL)
{
//first we check our heritage specific skill costs.
bool found = false;
for (DWORD j = 0; j < heritageGroup->mSkillList.num_used; j++)
{
if (heritageGroup->mSkillList.array_data[j].skillNum == i)
{
if (cg.skillAdvancementClasses[i] == SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
numCreditsUsed += heritageGroup->mSkillList.array_data[j].normalCost;
else if (cg.skillAdvancementClasses[i] == SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
numCreditsUsed += heritageGroup->mSkillList.array_data[j].primaryCost;
found = true;
break;
}
}
//then if not found check regular skill costs.
if (!found)
{
if (cg.skillAdvancementClasses[i] == SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
numCreditsUsed += pSkillBase->_trained_cost;
else if (cg.skillAdvancementClasses[i] == SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
numCreditsUsed += pSkillBase->_specialized_cost;
}
}
else
{
if (cg.skillAdvancementClasses[i] == SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[i] == SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
{
goto BadData;
}
}
}
if (numCreditsUsed > 56)
{
goto BadData;
}
// need to reformat name here...
if (!CheckNameValidity(cg.name.c_str(), GetAccessLevel(), resultName))
{
errorCode = CG_VERIFICATION_RESPONSE_NAME_BANNED;
goto BadData;
}
if (!CheckBadName(cg.name))
{
errorCode = CG_VERIFICATION_RESPONSE_NAME_BANNED;
goto BadData;
}
// should check variables to make sure everythings within restriction
// wHairTextures[m_wGender][m_wHairStyle];
// WORD wHairTextures[2][4] = { { 0x10B8, 0x10B8, 0x10B8, 0x10B7 }, { 0x11FD, 0x11FD, 0x11FD, 0x10B7 } };
// group 4, 0x0000F643, 0x00000001, <guid>, <char name[str]>, 0x00000000
{
// check if name exists
if (g_pDBIO->IsCharacterNameOpen(resultName.c_str()))
{
const int MIN_PLAYER_GUID = 0x50000000;
const int MAX_PLAYER_GUID = 0x5FFFFFFF;
unsigned int newCharacterGUID = g_pDBIO->GetHighestWeenieID(MIN_PLAYER_GUID, MAX_PLAYER_GUID) + 1;
if (g_pDBIO->CreateCharacter(m_AccountInfo.id, newCharacterGUID, resultName.c_str()))
{
m_Characters = g_pDBIO->GetCharacterList(m_AccountInfo.id);
// start pReader Zaikhil
Position startPos;
// startPos = Position(0x7D64001D, Vector(74.748657f, 97.934601f, 12.004999f), Quaternion(0.926898f, 0.000000f, 0.000000f, -0.375314f)); // zaikhil
// startPos = Position(0x9722003A, Vector(168.354004f, 24.618000f, 102.005005f), Quaternion(-0.922790f, 0.000000f, 0.000000f, -0.385302f));
startPos = Position(0xD3380005, Vector(5.500000f, 109.800003f, 168.463333f), Quaternion(1.000000f, 0.000000f, 0.000000f, 0.000000f));
// WCID "1" is always a player weenie
CWeenieObject *weenie = g_pWeenieFactory->CreateWeenieByClassID(1, &startPos, false);
if(!weenie)
goto BadData;
weenie->SetID(newCharacterGUID);
// alter parameters for character creation here
weenie->SetName(resultName.c_str());
weenie->m_Qualities.SetInt(HERITAGE_GROUP_INT, cg.heritageGroup);
weenie->m_Qualities.SetInt(GENDER_INT, cg.gender);
weenie->m_Qualities.SetAttribute(STRENGTH_ATTRIBUTE, cg.strength);
weenie->m_Qualities.SetAttribute(ENDURANCE_ATTRIBUTE, cg.endurance);
weenie->m_Qualities.SetAttribute(COORDINATION_ATTRIBUTE, cg.coordination);
weenie->m_Qualities.SetAttribute(QUICKNESS_ATTRIBUTE, cg.quickness);
weenie->m_Qualities.SetAttribute(FOCUS_ATTRIBUTE, cg.focus);
weenie->m_Qualities.SetAttribute(SELF_ATTRIBUTE, cg.self);
if (heritageGroup)
{
weenie->m_Qualities.SetString(HERITAGE_GROUP_STRING, heritageGroup->name.c_str());
if (cg.templateNum >= 0 && cg.templateNum < heritageGroup->mTemplateList.num_used)
weenie->m_Qualities.SetString(TEMPLATE_STRING, heritageGroup->mTemplateList.array_data[cg.templateNum].name.c_str());
scg = heritageGroup->mGenderList.lookup(cg.gender);
if (scg)
{
weenie->m_Qualities.SetDataID(PALETTE_BASE_DID, scg->basePalette);
weenie->m_Qualities.SetDataID(ICON_DID, scg->iconImage);
weenie->m_Qualities.SetDataID(SOUND_TABLE_DID, scg->soundTable);
weenie->m_Qualities.SetDataID(MOTION_TABLE_DID, scg->motionTable);
weenie->m_Qualities.SetDataID(COMBAT_TABLE_DID, scg->combatTable);
weenie->m_Qualities.SetDataID(PHYSICS_EFFECT_TABLE_DID, scg->physicsTable);
weenie->m_Qualities.SetDataID(SETUP_DID, scg->setup);
weenie->m_Qualities.SetFloat(DEFAULT_SCALE_FLOAT, scg->scaling / 100);
//weenie->m_ObjDesc = scg->objDesc;
//weenie->m_ObjDesc.paletteID = scg->basePalette;
cg.hairStyle = max(0, min((int)scg->mHairStyleList.num_used - 1, cg.hairStyle));
HairStyle_CG *hairStyle = &scg->mHairStyleList.array_data[cg.hairStyle];
//weenie->m_ObjDesc += hairStyle->objDesc;
if (hairStyle->objDesc.firstAPChange)
weenie->m_Qualities.SetDataID(HEAD_OBJECT_DID, hairStyle->objDesc.firstAPChange->part_id);
if (hairStyle->alternateSetup)
weenie->m_Qualities.SetDataID(SETUP_DID, hairStyle->alternateSetup);
// fix Gearknight and olthoi head_object_did
if (cg.heritageGroup == Gearknight_HeritageGroup)
weenie->m_Qualities.SetDataID(HEAD_OBJECT_DID, hairStyle->objDesc.lastAPChange->part_id);
if (cg.heritageGroup == Olthoi_HeritageGroup)
weenie->m_Qualities.SetDataID(HEAD_OBJECT_DID, 0x010045f0);
if (cg.heritageGroup == OlthoiAcid_HeritageGroup)
weenie->m_Qualities.SetDataID(HEAD_OBJECT_DID, 0x01004616);
if (cg.eyesStrip != -1)
{
cg.eyesStrip = max(0, min((int)scg->mEyeStripList.num_used - 1, cg.eyesStrip));
EyesStrip_CG *eyesStrip = &scg->mEyeStripList.array_data[cg.eyesStrip];
if (eyesStrip)
{
if (hairStyle->bald)
{
//weenie->m_ObjDesc += eyesStrip->objDesc_Bald;
weenie->m_Qualities.SetDataID(DEFAULT_EYES_TEXTURE_DID, eyesStrip->objDesc_Bald.firstTMChange->old_tex_id);
weenie->m_Qualities.SetDataID(EYES_TEXTURE_DID, eyesStrip->objDesc_Bald.firstTMChange->new_tex_id);
}
else
{
//weenie->m_ObjDesc += eyesStrip->objDesc;
weenie->m_Qualities.SetDataID(DEFAULT_EYES_TEXTURE_DID, eyesStrip->objDesc.firstTMChange->old_tex_id);
weenie->m_Qualities.SetDataID(EYES_TEXTURE_DID, eyesStrip->objDesc.firstTMChange->new_tex_id);
}
}
}
if (cg.noseStrip != -1)
{
cg.noseStrip = max(0, min((int)scg->mNoseStripList.num_used - 1, cg.noseStrip));
FaceStrip_CG *faceStrip = &scg->mNoseStripList.array_data[cg.noseStrip];
if (faceStrip)
{
//weenie->m_ObjDesc += faceStrip->objDesc;
weenie->m_Qualities.SetDataID(DEFAULT_NOSE_TEXTURE_DID, faceStrip->objDesc.firstTMChange->old_tex_id);
weenie->m_Qualities.SetDataID(NOSE_TEXTURE_DID, faceStrip->objDesc.firstTMChange->new_tex_id);
}
}
if (cg.mouthStrip != -1)
{
cg.mouthStrip = max(0, min((int)scg->mMouthStripList.num_used - 1, cg.mouthStrip));
FaceStrip_CG *faceStrip = &scg->mMouthStripList.array_data[cg.mouthStrip];
if (faceStrip)
{
// weenie->m_ObjDesc += faceStrip->objDesc;
weenie->m_Qualities.SetDataID(DEFAULT_MOUTH_TEXTURE_DID, faceStrip->objDesc.firstTMChange->old_tex_id);
weenie->m_Qualities.SetDataID(MOUTH_TEXTURE_DID, faceStrip->objDesc.firstTMChange->new_tex_id);
}
}
PalSet *ps;
if (ps = PalSet::Get(scg->skinPalSet))
{
DWORD skinPalette = ps->GetPaletteID(cg.skinShade);
weenie->m_Qualities.SetDataID(SKIN_PALETTE_DID, skinPalette);
//weenie->m_ObjDesc.AddSubpalette(new Subpalette(skinPalette, 0 << 3, 0x18 << 3));
PalSet::Release(ps);
}
cg.hairColor = max(0, min((int)scg->mHairColorList.num_used - 1, cg.hairColor));
if (ps = PalSet::Get(scg->mHairColorList.array_data[cg.hairColor]))
{
DWORD hairPalette = ps->GetPaletteID(cg.hairShade);
weenie->m_Qualities.SetDataID(HAIR_PALETTE_DID, hairPalette);
//weenie->m_ObjDesc.AddSubpalette(new Subpalette(hairPalette, 0x18 << 3, 0x8 << 3));
PalSet::Release(ps);
}
if (cg.eyeColor != -1)
{
cg.eyeColor = max(0, min((int)scg->mEyeColorList.num_used - 1, cg.eyeColor));
DWORD eyesPalette = scg->mEyeColorList.array_data[cg.eyeColor];
weenie->m_Qualities.SetDataID(EYES_PALETTE_DID, eyesPalette);
//weenie->m_ObjDesc.AddSubpalette(new Subpalette(eyesPalette, 0x20 << 3, 0x8 << 3));
}
/*
EYES_TEXTURE_DID,
NOSE_TEXTURE_DID,
MOUTH_TEXTURE_DID,
DEFAULT_EYES_TEXTURE_DID,
DEFAULT_NOSE_TEXTURE_DID,
DEFAULT_MOUTH_TEXTURE_DID,
HAIR_PALETTE_DID,
EYES_PALETTE_DID,
SKIN_PALETTE_DID,
HEAD_OBJECT_DID,
*/
}
}
for (DWORD i = 0; i < cg.numSkills; i++)
{
weenie->m_Qualities.SetSkillAdvancementClass(SkillTable::OldToNewSkill((STypeSkill)i), cg.skillAdvancementClasses[i]);
if (cg.skillAdvancementClasses[i] == SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
weenie->m_Qualities.SetSkillLevel((STypeSkill)i, 10);
else if (cg.skillAdvancementClasses[i] == SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
Skill trained;
weenie->m_Qualities.InqSkill((STypeSkill)i, trained);
trained._init_level = 0;
trained._level_from_pp = 5;
trained._pp = 526;
weenie->m_Qualities.SetSkill((STypeSkill)i, trained);
}
}
time_t t = chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::stringstream ss;
ss << std::put_time(std::localtime(&t), "%m/%d/%y %I:%M:%S %p."); // convert time to a string of format '01/01/18 11:59:59 AM.'
std::string str = ss.str();
weenie->m_Qualities.SetInt(CREATION_TIMESTAMP_INT, t);
weenie->m_Qualities.SetString(DATE_OF_BIRTH_STRING, ss.str());
weenie->m_Qualities.SetInt(AGE_INT, 0);
if (weenie->m_Qualities.GetInt(HERITAGE_GROUP_INT, 0) == Lugian_HeritageGroup)
weenie->m_Qualities.SetDataID(MOTION_TABLE_DID, 0x9000216);
/*
cg.startArea = max(0, min(cgd->mStartAreaList.num_used - 1, cg.startArea));
assert(cgd->mStartAreaList.array_data[cg.startArea].mPositionList.num_used >= 1);
startPos = cgd->mStartAreaList.array_data[cg.startArea].mPositionList.array_data[0];
weenie->SetInitialPosition(startPos);
*/
startPos = Position(0xA9B00006, Vector(24.258204f, 123.777000f, 63.060749f), Quaternion(1, 0, 0, 0)); // holtburg
switch (cg.startArea)
{
default:
case 0:
// Holtburg
startPos = Position(g_pConfig->HoltburgStartPosition());
if (!startPos.objcell_id)
{
startPos = Position(0x870301AD, Vector(12.319900f, -28.482000f, 0.005000f), Quaternion(0.315322f, 0, 0, 0.948985f));
//startPos = Position(0xA9B00006, Vector(24.258204f, 123.777000f, 63.060749f), Quaternion(1, 0, 0, 0));
}
break;
case 1:
// Shoushi
startPos = Position(g_pConfig->ShoushiStartPosition());
if (!startPos.objcell_id)
{
startPos = Position(0x800401AD, Vector(12.319900f, -28.482000f, 0.005000f), Quaternion(0.315322f, 0.0, 0.0, 0.948985f));
//startPos = Position(0xDE51000C, Vector(26.712753f, 89.279999f, 17.778936f), Quaternion(0.931082f, 0.0, 0.0, -0.364811f));
}
break;
case 2:
// Yaraq
startPos = Position(g_pConfig->YaraqStartPosition());
if (!startPos.objcell_id)
{
startPos = Position(0x8C0401AD, Vector(12.319900f, -28.482000f, 0.005000f), Quaternion(0.315322f, 0.0, 0.0, 0.948985f));
//startPos = Position(0x7D680019, Vector(79.102280f, 19.573767f, 12.821287f), Quaternion(-0.656578f, 0.0, 0.0, 0.754258f));
}
break;
case 3:
// Sanamar
startPos = Position(g_pConfig->SanamarStartPosition());
if (!startPos.objcell_id)
{
startPos = Position(0x7203026C, Vector(11.66688f, -28.831614f, 0.005f), Quaternion(0, 0, 0, 1.0f));
//startPos = Position(0xA9B00006, Vector(24.258204f, 123.777000f, 63.060749f), Quaternion(1, 0, 0, 0));
}
break;
case 4:
// Olthoi Island
startPos = Position(g_pConfig->OlthoiStartPosition());
if (!startPos.objcell_id)
{
startPos = Position(0xE6D3000E, Vector(40.57f, 138.16f, 218.00f), Quaternion(0, 0, 0, 1));
}
break;
}
weenie->SetInitialPosition(startPos);
weenie->m_Qualities.SetPosition(SANCTUARY_POSITION, startPos);
weenie->m_Qualities.SetInt(AVAILABLE_SKILL_CREDITS_INT, 52 - max(0, min(52, numCreditsUsed)));
weenie->m_Qualities.SetInt(CONTAINERS_CAPACITY_INT, 7);
weenie->SetMaxVitals(false);
weenie->m_Qualities._create_list->clear(); //Clear the create list as we don't want what's in it.
if (!g_pWorld->CreateEntity(weenie))
return;; //Briefly add the weenie to the world so we don't get errors when adding the starting gear.
//add starter gear
GenerateStarterGear(weenie, cg, scg);
// Set Olthoi starting stats/skills
if (cg.heritageGroup == Olthoi_HeritageGroup)
{
weenie->m_Qualities.SetInt(LEVEL_INT, 180);
weenie->m_Qualities.SetAttribute(STRENGTH_ATTRIBUTE, 350);
weenie->m_Qualities.SetAttribute(ENDURANCE_ATTRIBUTE, 350);
weenie->m_Qualities.SetAttribute(COORDINATION_ATTRIBUTE, 300);
weenie->m_Qualities.SetAttribute(QUICKNESS_ATTRIBUTE, 350);
weenie->m_Qualities.SetAttribute(FOCUS_ATTRIBUTE, 200);
weenie->m_Qualities.SetAttribute(SELF_ATTRIBUTE, 200);
weenie->m_Qualities.SetAttribute2nd(HEALTH_ATTRIBUTE_2ND, 325);
weenie->m_Qualities.SetAttribute2nd(STAMINA_ATTRIBUTE_2ND, 325);
weenie->m_Qualities.SetInt(PLAYER_KILLER_STATUS_INT, PK_PKStatus); // Olthoi are always Red
}
if (cg.heritageGroup == OlthoiAcid_HeritageGroup)
{
weenie->m_Qualities.SetInt(LEVEL_INT, 180);
weenie->m_Qualities.SetAttribute(STRENGTH_ATTRIBUTE, 200);
weenie->m_Qualities.SetAttribute(ENDURANCE_ATTRIBUTE, 200);
weenie->m_Qualities.SetAttribute(COORDINATION_ATTRIBUTE, 300);
weenie->m_Qualities.SetAttribute(QUICKNESS_ATTRIBUTE, 250);
weenie->m_Qualities.SetAttribute(FOCUS_ATTRIBUTE, 400);
weenie->m_Qualities.SetAttribute(SELF_ATTRIBUTE, 400);
weenie->m_Qualities.SetAttribute2nd(HEALTH_ATTRIBUTE_2ND, 400);
weenie->m_Qualities.SetAttribute2nd(STAMINA_ATTRIBUTE_2ND, 400);
weenie->m_Qualities.SetAttribute2nd(MANA_ATTRIBUTE_2ND, 400);
weenie->m_Qualities.SetInt(PLAYER_KILLER_STATUS_INT, PK_PKStatus); // Olthoi are always Red
}
// add racial augmentations
switch (cg.heritageGroup)
{
case Shadowbound_HeritageGroup:
case Penumbraen_HeritageGroup:
weenie->m_Qualities.SetInt(AUGMENTATION_CRITICAL_EXPERTISE_INT, 1); break;
case Gearknight_HeritageGroup:
weenie->m_Qualities.SetInt(AUGMENTATION_DAMAGE_REDUCTION_INT, 1); break;
case Tumerok_HeritageGroup:
weenie->m_Qualities.SetInt(AUGMENTATION_CRITICAL_POWER_INT, 1); break;
case Lugian_HeritageGroup:
weenie->m_Qualities.SetInt(AUGMENTATION_INCREASED_CARRYING_CAPACITY_INT, 1); break;
case Empyrean_HeritageGroup:
weenie->m_Qualities.SetInt(AUGMENTATION_INFUSED_LIFE_MAGIC_INT, 1); break;
case Undead_HeritageGroup:
weenie->m_Qualities.SetInt(AUGMENTATION_CRITICAL_DEFENSE_INT, 1); break;
case Olthoi_HeritageGroup:
case OlthoiAcid_HeritageGroup:
break; // none
default: // sho, aluv, gharu, viamont
weenie->m_Qualities.SetInt(AUGMENTATION_JACK_OF_ALL_TRADES_INT, 1); break;
}
if (cg.heritageGroup < Olthoi_HeritageGroup) // Olthoi don't go to the training academy.
weenie->m_Qualities.SetBool(RECALLS_DISABLED_BOOL, 1); // Cannot use recalls out of the training academy.
weenie->Save();
g_pWorld->RemoveEntity(weenie);
//delete weenie; //RemoveEntity already performs the deletion.
BinaryWriter response;
response.Write<DWORD>(0xF643);
response.Write<DWORD>(1);
response.Write<DWORD>(newCharacterGUID);
response.WriteString(cg.name.c_str());
response.Write<DWORD>(0);
SendNetMessage(response.GetData(), response.GetSize(), PRIVATE_MSG);
}
else
{
SERVER_ERROR << "Failed to create character.";
BinaryWriter response;
response.Write<DWORD>(0xF643);
response.Write<DWORD>(CG_VERIFICATION_RESPONSE_DATABASE_DOWN); // update this error number
SendNetMessage(response.GetData(), response.GetSize(), PRIVATE_MSG);
}
}
else
{
SERVER_INFO << "Character name already exists.";
BinaryWriter response;
response.Write<DWORD>(0xF643);
response.Write<DWORD>(CG_VERIFICATION_RESPONSE_NAME_IN_USE); // name already exists
SendNetMessage(response.GetData(), response.GetSize(), PRIVATE_MSG);
}
}
SafeDelete(cg.skillAdvancementClasses);
return;
BadData:
{
BinaryWriter response;
response.Write<DWORD>(0xF643);
response.Write<DWORD>(errorCode);
SendNetMessage(response.GetData(), response.GetSize(), PRIVATE_MSG);
}
}
void CClient::GenerateStarterGear(CWeenieObject *weenieObject, ACCharGenResult cg, Sex_CG *scg)
{
if (!weenieObject)
return;
CMonsterWeenie *weenie = weenieObject->AsMonster();
if (weenie == NULL)
return;
if (cg.heritageGroup != Gearknight_HeritageGroup && cg.heritageGroup < 12) // Gearknights and olthoi don't get clothes to start.
{
if (cg.headgearStyle != UINT_MAX)
weenie->SpawnWielded(cg.headgearStyle, scg->mHeadgearList, cg.headgearColor, scg->mClothingColorsList, cg.headgearShade);
weenie->SpawnWielded(cg.shirtStyle, scg->mShirtList, cg.shirtColor, scg->mClothingColorsList, cg.shirtShade);
weenie->SpawnWielded(cg.trousersStyle, scg->mPantsList, cg.trousersColor, scg->mClothingColorsList, cg.trousersShade);
weenie->SpawnWielded(cg.footwearStyle, scg->mFootwearList, cg.footwearColor, scg->mClothingColorsList, cg.footwearShade);
}
if (cg.heritageGroup < 12) // Olthoi get their own set of starting items.
{
//weenie->m_Qualities.SetInt(COIN_VALUE_INT, GetAccessLevel() >= ADMIN_ACCESS ? 500000000 : 10000);
weenie->SpawnInContainer(W_COINSTACK_CLASS, 500);
weenie->SpawnInContainer(W_TUTORIALBOOK_CLASS, 1);
weenie->SpawnInContainer(W_TINKERINGTOOL_CLASS, 1);
weenie->SpawnInContainer(W_SACK_CLASS, 1);
weenie->SpawnInContainer(W_CALLINGSTONE_CLASS, 1);
weenie->SpawnInContainer(33613, 1); // Pathwarden Token
}
weenie->SpawnInContainer(31000, 1); // Blackmoor's Favor
if (cg.skillAdvancementClasses[ALCHEMY_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_MORTARANDPESTLE_CLASS, 1);
if (cg.skillAdvancementClasses[ALCHEMY_SKILL] >= SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_GEMAZURITE_CLASS, 3);
if (cg.skillAdvancementClasses[COOKING_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_FLOUR_CLASS, 6);
weenie->SpawnInContainer(W_WATER_CLASS, 6);
}
if (cg.skillAdvancementClasses[FLETCHING_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_ARROWHEAD_CLASS, 30);
weenie->SpawnInContainer(W_ARROWSHAFT_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEALING_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_HEALINGKITCRUDE_CLASS, 1);
if (cg.skillAdvancementClasses[HEALING_SKILL] >= SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_HEALINGKITCRUDE_CLASS, 1);
if (cg.skillAdvancementClasses[LOCKPICK_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_LOCKPICKCRUDE_CLASS, 1);
if (cg.skillAdvancementClasses[ITEM_APPRAISAL_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ARMOR_APPRAISAL_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WEAPON_APPRAISAL_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[MAGIC_ITEM_APPRAISAL_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_TINKERINGTOOL_CLASS, 1);
}
switch (cg.heritageGroup)
{
case Default:
case Aluvian_HeritageGroup:
weenie->SpawnInContainer(W_BREAD_CLASS, 1);
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGDAGGER_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_DAGGERTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGKNIFE_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Gharundim_HeritageGroup:
weenie->SpawnInContainer(W_CHEESE_CLASS, 1);
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSTAFF_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_STAFFTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGBASTONE_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Sho_HeritageGroup:
weenie->SpawnInContainer(W_APPLE_CLASS, 1);
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGKNUCKLES_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_CESTUSTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGHANDWRAPS_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Viamontian_HeritageGroup:
weenie->SpawnInContainer(W_APPLE_CLASS, 1);
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGBROADSWORD_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_SWORDTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSHORTSWORD_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Lugian_HeritageGroup:
weenie->SpawnInContainer(W_APPLE_CLASS, 1);
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGCLUB_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_MACETRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGDABUS_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Tumerok_HeritageGroup:
weenie->SpawnInContainer(W_APPLE_CLASS, 1);
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPEAR_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_SPEARTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGBUDIAQ_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Empyrean_HeritageGroup:
weenie->SpawnInContainer(W_APPLE_CLASS, 1);
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGBROADSWORD_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_SWORDTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSHORTSWORD_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Undead_HeritageGroup:
weenie->SpawnInContainer(W_APPLE_CLASS, 1);
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGHANDAXE_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_AXETRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGTUNGI_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Gearknight_HeritageGroup:
weenie->SpawnInContainer(W_APPLE_CLASS, 1);
weenie->SpawnInContainer(43022, 1); // Core Plating Deintegrator
weenie->SpawnInContainer(42979, 1); // Core Plating Integrator
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGCLUB_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_MACETRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGDABUS_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Penumbraen_HeritageGroup:
weenie->SpawnInContainer(W_APPLE_CLASS, 1);
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGKNUCKLES_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_CESTUSTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGHANDWRAPS_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Shadowbound_HeritageGroup:
weenie->SpawnInContainer(W_APPLE_CLASS, 1);
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGKNUCKLES_CLASS, 1);
if (cg.skillAdvancementClasses[TWO_HANDED_COMBAT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPADONE_CLASS, 1);
if (cg.skillAdvancementClasses[MISSILE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->SpawnInContainer(W_BOWTRAINING_CLASS, 1);
weenie->SpawnInContainer(W_ARROW_CLASS, 30);
}
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_CESTUSTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGHANDWRAPS_CLASS, 1);
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS ||
cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_WANDTRAINING_CLASS, 1);
break;
case Olthoi_HeritageGroup:
weenie->SpawnInContainer(43701, 1); // Olthoi Fibrous Healing Tissue
weenie->SpawnInContainer(43634, 10); // Acidic Infusions
case OlthoiAcid_HeritageGroup:
weenie->SpawnInContainer(43701, 1); // Olthoi Fibrous Healing Tissue
weenie->SpawnInContainer(43634, 10); // Acidic Infusions
weenie->SpawnInContainer(43489, 1); // Salivatory Goo
weenie->SpawnInContainer(43637, 100); // Saliva Invigorator
weenie->SpawnInContainer(W_PACKWARESSENCE_CLASS, 1);
break;
}
CContainerWeenie *sack = NULL;
for (auto pack : weenie->m_Packs)
{
if (pack->AsContainer())
{
sack = pack->AsContainer();
break;
}
}
if (sack == NULL)
sack = weenie;
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS || cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS || cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS || cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS || cg.skillAdvancementClasses[VOID_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
sack->SpawnInContainer(W_SCARABLEAD_CLASS, 5);
weenie->SpawnInContainer(W_TAPERPRISMATIC_CLASS, 25);
}
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->m_Qualities.AddSpell(StrengthOther1_SpellID);
weenie->m_Qualities.AddSpell(InvulnerabilitySelf1_SpellID);
weenie->SpawnInContainer(W_PACKCREATUREESSENCE_CLASS, 1);
}
if (cg.skillAdvancementClasses[CREATURE_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
{
weenie->m_Qualities.AddSpell(ArcaneEnlightenmentSelf1_SpellID);
}
if (cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->m_Qualities.AddSpell(BloodDrinker1_SpellID);
weenie->m_Qualities.AddSpell(Impenetrability1_SpellID);
weenie->SpawnInContainer(W_PACKITEMESSENCE_CLASS, 1);
}
if (cg.skillAdvancementClasses[ITEM_ENCHANTMENT_SKILL] >= SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
{
weenie->m_Qualities.AddSpell(SwiftKiller1_SpellID);
}
if (cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->m_Qualities.AddSpell(ArmorOther1_SpellID);
weenie->m_Qualities.AddSpell(HealSelf1_SpellID);
if (cg.heritageGroup != Empyrean_HeritageGroup) // Empyreans get the Life magic aug on creation
weenie->SpawnInContainer(W_PACKLIFEESSENCE_CLASS, 1);
}
if (cg.skillAdvancementClasses[LIFE_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
{
weenie->m_Qualities.AddSpell(RejuvenationOther1_SpellID);
}
if (cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
weenie->m_Qualities.AddSpell(FlameBolt1_SpellID);
weenie->m_Qualities.AddSpell(ForceBolt1_SpellID);
weenie->m_Qualities.AddSpell(LightningBolt1_SpellID);
weenie->m_Qualities.AddSpell(FrostBolt1_SpellID);
weenie->m_Qualities.AddSpell(ShockWave1_SpellID);
weenie->m_Qualities.AddSpell(WhirlingBlade1_SpellID);
weenie->m_Qualities.AddSpell(AcidStream1_SpellID);
weenie->SpawnInContainer(W_PACKWARESSENCE_CLASS, 1);
}
if (cg.skillAdvancementClasses[WAR_MAGIC_SKILL] >= SKILL_ADVANCEMENT_CLASS::SPECIALIZED_SKILL_ADVANCEMENT_CLASS)
{
}
if (cg.skillAdvancementClasses[DUAL_WIELD_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
{
switch (cg.heritageGroup)
{
case Default:
case Aluvian_HeritageGroup:
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGDAGGER_CLASS, 1);
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_DAGGERTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGKNIFE_CLASS, 1);
break;
case Gharundim_HeritageGroup:
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSTAFF_CLASS, 1);
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_STAFFTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGBASTONE_CLASS, 1);
break;
case Penumbraen_HeritageGroup:
case Shadowbound_HeritageGroup:
case Sho_HeritageGroup:
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGKNUCKLES_CLASS, 1);
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_CESTUSTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGHANDWRAPS_CLASS, 1);
break;
case Empyrean_HeritageGroup:
case Viamontian_HeritageGroup:
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGBROADSWORD_CLASS, 1);
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_SWORDTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSHORTSWORD_CLASS, 1);
break;
case Gearknight_HeritageGroup:
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGCLUB_CLASS, 1);
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_MACETRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGDABUS_CLASS, 1);
break;
case Tumerok_HeritageGroup:
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGSPEAR_CLASS, 1);
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_SPEARTRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGBUDIAQ_CLASS, 1);
break;
case Lugian_HeritageGroup:
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGCLUB_CLASS, 1);
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_MACETRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGDABUS_CLASS, 1);
break;
case Undead_HeritageGroup:
if (cg.skillAdvancementClasses[LIGHT_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGHANDAXE_CLASS, 1);
if (cg.skillAdvancementClasses[HEAVY_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_AXETRAINING_CLASS, 1);
if (cg.skillAdvancementClasses[FINESSE_WEAPONS_SKILL] >= SKILL_ADVANCEMENT_CLASS::TRAINED_SKILL_ADVANCEMENT_CLASS)
weenie->SpawnInContainer(W_TRAININGTUNGI_CLASS, 1);
break;
case Olthoi_HeritageGroup:
case OlthoiAcid_HeritageGroup:
break; // none
}
}
weenie->RecalculateCoinAmount(W_COINSTACK_CLASS);
}
void CClient::SendLandblock(DWORD dwFileID)
{
TURBINEFILE* pLandData = g_pCell->GetFile(dwFileID);
if (!pLandData)
{
if (m_pEvents)
{
m_pEvents->SendText(csprintf("Your client is requesting cell data (0x%08X) that this server does not have!", dwFileID), LTT_DEFAULT);
m_pEvents->SendText("If you are stuck in portal mode, type /render radius 5 to escape. The server administrator should reconfigure the server with a FULL cell.dat file!", LTT_DEFAULT);
}
return;
}
if ((dwFileID & 0xFFFF) != 0xFFFF)
{
SERVER_WARN << "Client requested Landblock" << dwFileID << "-should end pReader 0xFFFF";
}
if (pLandData)
{
BinaryWriter BlockPackage;
BlockPackage.Write<DWORD>(0xF7E2);
DWORD dwFileSize = pLandData->GetLength();
BYTE* pbFileData = pLandData->GetData();
DWORD dwPackageSize = (DWORD)((dwFileSize * 1.02f) + 12 + 1);
BYTE* pbPackageData = new BYTE[dwPackageSize];
if (Z_OK != compress2(pbPackageData, &dwPackageSize, pbFileData, dwFileSize, Z_BEST_COMPRESSION))
{
SERVER_ERROR << "Error compressing LandBlock package!";
}
BlockPackage.Write<DWORD>(1); //the resource type: 1 for 0xFFFF, 2 for 0xFFFE, 3 for 0x100
BlockPackage.Write<DWORD>(2); //1 = client_portal / 2 = client_cell_1 / 3 = client_local_English
BlockPackage.Write<DWORD>(1); //?
BlockPackage.Write<DWORD>(dwFileID); //the resource ID number
BlockPackage.Write<DWORD>(1); //the file version number
BlockPackage.Write<BYTE>(1); // 0 = uncompressed / 1 = compressed
BlockPackage.Write<DWORD>(2); //?
BlockPackage.Write<DWORD>(dwPackageSize + sizeof(DWORD) * 2); //the number of bytes required for the remainder of this message, including this DWORD
BlockPackage.Write<DWORD>(dwFileSize); //the size of the uncompressed file
BlockPackage.Write(pbPackageData, dwPackageSize); //bytes of zlib compressed file data
BlockPackage.Align();
delete[] pbPackageData;
delete pLandData;
SendNetMessage(BlockPackage.GetData(), BlockPackage.GetSize(), EVENT_MSG, FALSE);
}
}
void CClient::SendLandblockInfo(DWORD dwFileID)
{
if ((dwFileID & 0xFFFF) != 0xFFFE)
{
SERVER_WARN << "Client requested LandblockInfo" << dwFileID << "should end pReader 0xFFFE";
return;
}
TURBINEFILE *pObjData = g_pCell->GetFile(dwFileID);
if (!pObjData)
{
return;
}
if (pObjData)
{
BinaryWriter BlockInfoPackage;
BlockInfoPackage.Write<DWORD>(0xF7E2);
DWORD dwFileSize = pObjData->GetLength();
BYTE* pbFileData = pObjData->GetData();
DWORD dwPackageSize = (DWORD)((dwFileSize * 1.02f) + 12 + 1);
BYTE* pbPackageData = new BYTE[dwPackageSize];
if (Z_OK != compress2(pbPackageData, &dwPackageSize, pbFileData, dwFileSize, Z_BEST_COMPRESSION))
{
SERVER_ERROR << "Error compressing LandBlockInfo package!";
}
BlockInfoPackage.Write<DWORD>(2); // 1 for 0xFFFF, 2 for 0xFFFE, 3 for 0x100
BlockInfoPackage.Write<DWORD>(2);
BlockInfoPackage.Write<DWORD>(1);
BlockInfoPackage.Write<DWORD>(dwFileID);
BlockInfoPackage.Write<DWORD>(1);
BlockInfoPackage.Write<BYTE>(1);
BlockInfoPackage.Write<DWORD>(2);
BlockInfoPackage.Write<DWORD>(dwPackageSize + sizeof(DWORD) * 2);
BlockInfoPackage.Write<DWORD>(dwFileSize);
BlockInfoPackage.Write(pbPackageData, dwPackageSize);
BlockInfoPackage.Align();
delete[] pbPackageData;
delete pObjData;
SendNetMessage(BlockInfoPackage.GetData(), BlockInfoPackage.GetSize(), EVENT_MSG, FALSE);
}
}
void CClient::SendLandcell(DWORD dwFileID)
{
TURBINEFILE* pCellData = g_pCell->GetFile(dwFileID);
if (!pCellData)
{
if (m_pEvents)
{
m_pEvents->SendText(csprintf("Your client is requesting cell data (#%08X) that this server does not have!", dwFileID), LTT_DEFAULT);
m_pEvents->SendText("If you are stuck in portal mode, type /render radius 5 to escape. The server administrator should reconfigure the server with a FULL cell.dat file!", LTT_DEFAULT);
}
return;
}
if (pCellData)
{
BinaryWriter CellPackage;
CellPackage.Write<DWORD>(0xF7E2);
DWORD dwFileSize = pCellData->GetLength();
BYTE* pbFileData = pCellData->GetData();
DWORD dwPackageSize = (DWORD)((dwFileSize * 1.02f) + 12 + 1);
BYTE* pbPackageData = new BYTE[dwPackageSize];
if (Z_OK != compress2(pbPackageData, &dwPackageSize, pbFileData, dwFileSize, Z_BEST_COMPRESSION))
{
// These are CEnvCell if I recall correctly
SERVER_ERROR << "Error compressing landcell package!";
}
CellPackage.Write<DWORD>(3); // 1 for 0xFFFF, 2 for 0xFFFE, 3 for 0x100
CellPackage.Write<DWORD>(2);
CellPackage.Write<DWORD>(1);
CellPackage.Write<DWORD>(dwFileID);
CellPackage.Write<DWORD>(1);
CellPackage.Write<BYTE>(1);
CellPackage.Write<DWORD>(2);
CellPackage.Write<DWORD>(dwPackageSize + sizeof(DWORD) * 2);
CellPackage.Write<DWORD>(dwFileSize);
CellPackage.Write(pbPackageData, dwPackageSize);
CellPackage.Align();
delete[] pbPackageData;
delete pCellData;
//LOG(Temp, Normal, "Sent cell %08X ..\n", dwFileID);
SendNetMessage(CellPackage.GetData(), CellPackage.GetSize(), EVENT_MSG, FALSE);
}
//if (m_pEvents)
// m_pEvents->SendText(csprintf("The server has sent you cell #%04X!", dwFileID >> 16), LTT_DEFAULT);
}
void CClient::ProcessMessage(BYTE *data, DWORD length, WORD group)
{
if (g_bDebugToggle)
{
NETWORK_DEBUG << "Received response(group" << group << ")";
NETWORK_DEBUG << data;
}
BinaryReader in(data, length);
DWORD dwMessageCode = in.ReadDWORD();
#ifdef _DEBUG
// LOG(Client, Normal, "Processing response 0x%X (size %d):\n", dwMessageCode, length);
#endif
if (in.GetLastError())
{
NETWORK_ERROR << "Error processing response." << dwMessageCode;
return;
}
switch (dwMessageCode)
{
case 0xF653:
{
if (m_vars.bInWorld)
{
m_pEvents->ForceLogout();
}
break;
}
case 0xF655: // Delete Character
if (!m_vars.bInWorld)
{
DeleteCharacter(&in);
}
break;
case 0xF656: // Create Character
{
if (!m_vars.bInWorld)
{
CreateCharacter(&in);
}
break;
}
case 0xF6EA: // Request Object
{
if (m_vars.bInWorld)
{
DWORD dwEID = in.ReadDWORD();
if (in.GetLastError()) break;
CPlayerWeenie *pPlayer;
if ((m_pEvents) && (pPlayer = m_pEvents->GetPlayer()))
{
CWeenieObject *pTarget = g_pWorld->FindWithinPVS(pPlayer, dwEID);
if (pTarget)
pPlayer->MakeAware(pTarget);
else
{
pTarget = g_pWorld->FindObject(dwEID);
if (pTarget)
{
// LOG(Temp, Normal, "Player %s is requesting info on %s it shouldn't know about.\n", pPlayer->GetName().c_str(), pTarget->GetName().c_str());
}
}
}
}
break;
}
case 0xF7E6://DDD_InterrogationResponseMessage
{
DWORD clientLanguage = in.ReadDWORD();
PackableList<DWORD> fileIds;
fileIds.UnPack(&in);
if (in.GetLastError())
break;
BinaryWriter EndDDD;
EndDDD.Write<long>(0xF7EA);
SendNetMessage(EndDDD.GetData(), EndDDD.GetSize(), EVENT_MSG);
break;
}
case 0xF7EA: //DDD_OnEndDDD
{
//BinaryWriter EndDDD;
//EndDDD.Write<DWORD>(0xF7EA);
//SendNetMessage(EndDDD.GetData(), EndDDD.GetSize(), EVENT_MSG);
break;
}
case 0xF7E3: //DDD_RequestDataMessage
{
if (m_vars.bInWorld)
{
DWORD dwFileClass = in.ReadDWORD();
DWORD dwFileID = in.ReadDWORD();
if (in.GetLastError()) break;
//disabled downloading files.
m_pEvents->SendText("Your client is requesting map data from the server. This feature is disabled to preserve bandwidth", LTT_DEFAULT);
m_pEvents->SendText("If you are stuck in portal mode, type /render radius 5 to escape. Make sure you are using the approppriate cell.dat file!", LTT_DEFAULT);
//switch (dwFileClass)
//{
//default:
// LOG(Client, Warning, "Unknown download request: %08X %d\n", dwFileID, dwFileClass);
// break;
//case 1:
//{
// SendLandblock(dwFileID); // 1 is landblock 0xFFFF
// break;
//}
//case 2:
// SendLandblockInfo(dwFileID); // 2 is landblock environemnt 0xFFFE
// break;
//case 3:
// SendLandcell(dwFileID); // 0x100+
// break;
//}
}
break;
}
case 0xF7B1:
{
//should check the sequence
if (m_vars.bInWorld)
{
m_pEvents->ProcessEvent(&in);
}
break;
}
case 0xF7C8:
{
if (!m_vars.bInWorld)
{
EnterWorld();
}
break;
}
case 0xF657:
{
if (m_vars.bInWorld)
{
DWORD dwGUID = in.ReadDWORD();
char* szName = in.ReadString();
if (in.GetLastError()) break;
m_pEvents->LoginCharacter(dwGUID, /*szName*/GetAccount());
}
break;
}
// modified cmoski code
case 0xF7DE:
{ //Gets data from /general here to rebroadcast as a world message below \o/
DWORD size = in.ReadDWORD();
DWORD TurbineChatType = in.ReadDWORD(); //0x1 Inbound, 0x3 Outbound, 0x5 Outbound Ack
DWORD unk1 = in.ReadDWORD();
DWORD unk2 = in.ReadDWORD();
DWORD unk3 = in.ReadDWORD();
DWORD unk4 = in.ReadDWORD();
DWORD unk5 = in.ReadDWORD();
DWORD unk6 = in.ReadDWORD();
DWORD payload = in.ReadDWORD();
if (in.GetLastError())
break;
if (TurbineChatType == 0x3) //0x3 Outbound
{
if (!m_pEvents->GetPlayer() || !m_pEvents->CheckForChatSpam())
break;
DWORD serial = in.ReadDWORD(); // serial of this character's chat
DWORD channel_unk = in.ReadDWORD();
DWORD channel_unk2 = in.ReadDWORD();
DWORD listening_channel = in.ReadDWORD(); // ListeningChannel in SetTurbineChatChannels (0x000BEEF0-9)
std::string message = in.ReadWStringToString();
DWORD payloadSize = in.ReadDWORD();
DWORD playerGUID = in.ReadDWORD();
DWORD ob_unknown = in.ReadDWORD(); //Always 0?
DWORD ob_unknown2 = in.ReadDWORD();
std::string filteredText = FilterBadChatCharacters(message.c_str());
if (in.GetLastError() || !filteredText.size())
break;
BinaryWriter chatAck;
chatAck.Write<DWORD>(0xF7DE);
chatAck.Write<DWORD>(0x30); // Size
chatAck.Write<DWORD>(0x05); // Type 05 - ACK
chatAck.Write<DWORD>(0x02); // ??
chatAck.Write<DWORD>(0x01); // ??
chatAck.Write<DWORD>(0xB0045); // bitfield?
chatAck.Write<DWORD>(0x01);
chatAck.Write<DWORD>(0xB0045); // bitfield?
chatAck.Write<DWORD>(0x00);
chatAck.Write<DWORD>(0x10); // Payload size
chatAck.Write<DWORD>(serial); // serial
chatAck.Write<DWORD>(0x02);
chatAck.Write<DWORD>(0x02);
chatAck.Write<DWORD>(0x00);
SendNetMessage(&chatAck, 0x04, FALSE, FALSE);
if (filteredText.c_str()[0] == '!' || filteredText.c_str()[0] == '@' || filteredText.c_str()[0] == '/')
{
CommandBase::Execute((char*)filteredText.erase(0, 1).c_str(), m_pEvents->GetPlayer()->GetClient());
}
else
{
if (!m_pEvents->GetPlayer()->IsPlayerSquelched(playerGUID))
{
switch (listening_channel)
{
case General_ChatChannel:
case Trade_ChatChannel:
case LFG_ChatChannel:
case Roleplay_ChatChannel:
case Allegiance_ChatChannel:
// case Olthoi_ChatChannel:
// case Society_ChatChannel:
if (listening_channel > 1 && !g_pConfig->AllowGeneralChat())
break;
g_pWorld->BroadcastChatChannel(listening_channel, m_pEvents->GetPlayer(), filteredText);
break;
}
}
}
}
break;
}
default:
SERVER_INFO << "Unhandled response" << dwMessageCode << "from the client.";
break;
}
//if ( dwMessageCode != 0xF7B1 )
// LOG(Temp, Normal, "Received response %04X\n", dwMessageCode);
}
BOOL CClient::CheckAccount(const char* cmp)
{
return !_stricmp(m_vars.account.c_str(), cmp);
}
int CClient::GetAccessLevel()
{
return m_AccountInfo.access;
}
void CClient::SetAccessLevel(unsigned int access)
{
m_AccountInfo.access = access;
}
BOOL CClient::CheckAddress(SOCKADDR_IN *peer)
{
return !memcmp(GetHostAddress(), peer, sizeof(SOCKADDR_IN));
}
WORD CClient::GetSlot()
{
return m_vars.slot;
}
const char *CClient::GetAccount()
{
return m_vars.account.c_str();
}
const char *CClient::GetDescription()
{
// Must lead with index or the kick/ban feature won't function.
return csprintf("#%u %s \"%s\"", m_vars.slot, inet_ntoa(m_vars.addr.sin_addr), m_vars.account.c_str());
}
void CClient::SetLoginData(DWORD dwUnixTime, DWORD dwPortalStamp, DWORD dwCellStamp)
{
m_vars.fLoginTime = g_pGlobals->Time();
m_vars.ClientLoginUnixTime = dwUnixTime;
m_vars.PortalStamp = dwPortalStamp;
m_vars.CellStamp = dwCellStamp;
m_vars.bInitDats = FALSE;
}
SOCKADDR_IN *CClient::GetHostAddress()
{
return &m_vars.addr;
}