Merge pull request #374 from EQEmu/character_limit

Variable per-client character creation limits
This commit is contained in:
Uleat 2015-02-17 19:59:05 -05:00
commit ea38fd2421
38 changed files with 2259 additions and 1741 deletions

View File

@ -1,11 +1,23 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 02/17/2015 ==
Uleat: Implemented per-client character creation limiting. Caps are unknown atm..so, don't get over-zealous!
Notes:
- Titanium is hard-coded to min/max of 8 in both the server and client translator code (client doesn't support variations in available character count)
- SoF thru RoF2 have had their max characters raised to 12 from 10
- Changing the number of characters per account is accomplished by:
1) Setting consts::CHARACTER_CREATION_LIMIT to the desired count in the client's constants file in ../common/patches/
2) Ensuring that eq_dictionary's EmuConstants::CHARACTER_CREATION_LIMIT is equal-greater than the client's new value..referencing is good
3) Recompiling the server code
- A rules-based qualifier may be added at some point
== 02/16/2015 == == 02/16/2015 ==
Trevius: (RoF2) Bazaar Trading (Buying/Selling) is now fully functional. Bazaar (/bazaar) search is not yet functional. Trevius: (RoF2) Bazaar Trading (Buying/Selling) is now fully functional. Bazaar (/bazaar) search is not yet functional.
demonstar55: (RoF2) Send the bard focus effects, note custom servers will need to fix their items, server side we still use the old system, but RoF2 wasn't showing anything with not sending focus, so send them demonstar55: (RoF2) Send the bard focus effects, note custom servers will need to fix their items, server side we still use the old system, but RoF2 wasn't showing anything with not sending focus, so send them
== 02/14/2015 == == 02/14/2015 ==
Trevius: (RoF2) Bazaar is now partially functional. RoF2 clients can start/end trader mode and other clients can purchase from them. No other functionality yet. Trevius: (RoF2) Bazaar is now partially functional. RoF2 clients can start/end trader mode and other clients can purchase from them. No other functionality yet.
Uleat: Implemented higher bandolier and potion belt counts for clients that support it..you will still need to activate them through the proper aa's, etc...
== 02/12/2015 == == 02/12/2015 ==
Akkadius: Implement zone based gravity, required SQL DB change Akkadius: Implement zone based gravity, required SQL DB change

View File

@ -1,3 +1,24 @@
/*
EQEMu: Everquest Server Emulator
Copyright (C) 2001-2015 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef CLIENTVERSIONS_H #ifndef CLIENTVERSIONS_H
#define CLIENTVERSIONS_H #define CLIENTVERSIONS_H
@ -46,32 +67,87 @@ static const char* ClientVersionName(ClientVersion version)
switch (version) switch (version)
{ {
case ClientVersion::Unknown: case ClientVersion::Unknown:
return "ClientVersion::Unknown"; return "Unknown";
case ClientVersion::Client62: case ClientVersion::Client62:
return "ClientVersion::Client62"; return "Client62";
case ClientVersion::Titanium: case ClientVersion::Titanium:
return "ClientVersion::Titanium"; return "Titanium";
case ClientVersion::SoF: case ClientVersion::SoF:
return "ClientVersion::SoF"; return "SoF";
case ClientVersion::SoD: case ClientVersion::SoD:
return "ClientVersion::SoD"; return "SoD";
case ClientVersion::UF: case ClientVersion::UF:
return "ClientVersion::UF"; return "UF";
case ClientVersion::RoF: case ClientVersion::RoF:
return "ClientVersion::RoF"; return "RoF";
case ClientVersion::RoF2: case ClientVersion::RoF2:
return "ClientVersion::RoF2"; return "RoF2";
case ClientVersion::MobNPC: case ClientVersion::MobNPC:
return "ClientVersion::MobNPC"; return "MobNPC";
case ClientVersion::MobMerc: case ClientVersion::MobMerc:
return "ClientVersion::MobMerc"; return "MobMerc";
case ClientVersion::MobBot: case ClientVersion::MobBot:
return "ClientVersion::MobBot"; return "MobBot";
case ClientVersion::MobPet: case ClientVersion::MobPet:
return "ClientVersion::MobPet"; return "MobPet";
default: default:
return "<ERROR> Invalid ClientVersion"; return "<ERROR> Invalid ClientVersion";
}; };
} }
static uint32 ClientBitFromVersion(ClientVersion clientVersion)
{
switch (clientVersion)
{
case ClientVersion::Unknown:
case ClientVersion::Client62:
return 0;
case ClientVersion::Titanium:
case ClientVersion::SoF:
case ClientVersion::SoD:
case ClientVersion::UF:
case ClientVersion::RoF:
case ClientVersion::RoF2:
case ClientVersion::MobNPC:
case ClientVersion::MobMerc:
case ClientVersion::MobBot:
case ClientVersion::MobPet:
return ((uint32)1 << (static_cast<unsigned int>(clientVersion) - 1));
default:
return 0;
}
}
static ClientVersion ClientVersionFromBit(uint32 clientVersionBit)
{
switch (clientVersionBit)
{
case (uint32)static_cast<unsigned int>(ClientVersion::Unknown):
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::Client62) - 1)):
return ClientVersion::Unknown;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::Titanium) - 1)):
return ClientVersion::Titanium;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::SoF) - 1)):
return ClientVersion::SoF;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::SoD) - 1)):
return ClientVersion::SoD;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::UF) - 1)):
return ClientVersion::UF;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::RoF) - 1)):
return ClientVersion::RoF;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::RoF2) - 1)):
return ClientVersion::RoF2;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::MobNPC) - 1)):
return ClientVersion::MobNPC;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::MobMerc) - 1)):
return ClientVersion::MobMerc;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::MobBot) - 1)):
return ClientVersion::MobBot;
case ((uint32)1 << (static_cast<unsigned int>(ClientVersion::MobPet) - 1)):
return ClientVersion::MobPet;
default:
return ClientVersion::Unknown;
}
}
#endif /* CLIENTVERSIONS_H */ #endif /* CLIENTVERSIONS_H */

View File

@ -157,18 +157,29 @@ namespace Convert {
/*84*/ uint32 Points; /*84*/ uint32 Points;
/*88*/ /*88*/
} PVPStatsEntry_Struct; } PVPStatsEntry_Struct;
static const size_t BANDOLIERS_SIZE = 4;
static const size_t BANDOLIER_ITEM_COUNT = 4;
struct BandolierItem_Struct { struct BandolierItem_Struct {
uint32 item_id; uint32 ID;
uint32 icon; uint32 Icon;
char item_name[64]; char Name[64];
}; };
struct Bandolier_Struct { struct Bandolier_Struct {
char name[32]; char Name[32];
Convert::BandolierItem_Struct items[EmuConstants::BANDOLIER_SIZE]; Convert::BandolierItem_Struct Items[Convert::BANDOLIER_ITEM_COUNT];
};
static const size_t POTION_BELT_ITEM_COUNT = 4;
struct PotionBeltItem_Struct {
uint32 ID;
uint32 Icon;
char Name[64];
}; };
struct PotionBelt_Struct { struct PotionBelt_Struct {
Convert::BandolierItem_Struct items[EmuConstants::POTION_BELT_SIZE]; Convert::PotionBeltItem_Struct Items[Convert::POTION_BELT_ITEM_COUNT];
}; };
struct SuspendedMinion_Struct struct SuspendedMinion_Struct
{ {
/*000*/ uint16 SpellID; /*000*/ uint16 SpellID;
@ -346,7 +357,7 @@ namespace Convert {
/*12800*/ uint32 expAA; /*12800*/ uint32 expAA;
/*12804*/ uint32 aapoints; //avaliable, unspent /*12804*/ uint32 aapoints; //avaliable, unspent
/*12808*/ uint8 unknown12844[36]; /*12808*/ uint8 unknown12844[36];
/*12844*/ Convert::Bandolier_Struct bandoliers[EmuConstants::BANDOLIERS_COUNT]; /*12844*/ Convert::Bandolier_Struct bandoliers[Convert::BANDOLIERS_SIZE];
/*14124*/ uint8 unknown14160[4506]; /*14124*/ uint8 unknown14160[4506];
/*18630*/ Convert::SuspendedMinion_Struct SuspendedMinion; // No longer in use /*18630*/ Convert::SuspendedMinion_Struct SuspendedMinion; // No longer in use
/*19240*/ uint32 timeentitledonaccount; /*19240*/ uint32 timeentitledonaccount;
@ -1430,15 +1441,15 @@ bool Database::CheckDatabaseConvertPPDeblob(){
if (rquery != ""){ results = QueryDatabase(rquery); } if (rquery != ""){ results = QueryDatabase(rquery); }
/* Run Bandolier Convert */ /* Run Bandolier Convert */
first_entry = 0; rquery = ""; first_entry = 0; rquery = "";
for (i = 0; i < EmuConstants::BANDOLIERS_COUNT; i++){ for (i = 0; i < Convert::BANDOLIERS_SIZE; i++){
if (strlen(pp->bandoliers[i].name) < 32) { if (strlen(pp->bandoliers[i].Name) < 32) {
for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ for (int si = 0; si < Convert::BANDOLIER_ITEM_COUNT; si++){
if (pp->bandoliers[i].items[si].item_id > 0){ if (pp->bandoliers[i].Items[si].ID > 0){
if (first_entry != 1) { if (first_entry != 1) {
rquery = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); rquery = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].Items[si].ID, pp->bandoliers[i].Items[si].Icon, pp->bandoliers[i].Name);
first_entry = 1; first_entry = 1;
} }
rquery = rquery + StringFormat(", (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); rquery = rquery + StringFormat(", (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].Items[si].ID, pp->bandoliers[i].Items[si].Icon, pp->bandoliers[i].Name);
} }
} }
} }
@ -1446,13 +1457,13 @@ bool Database::CheckDatabaseConvertPPDeblob(){
if (rquery != ""){ results = QueryDatabase(rquery); } if (rquery != ""){ results = QueryDatabase(rquery); }
/* Run Potion Belt Convert */ /* Run Potion Belt Convert */
first_entry = 0; rquery = ""; first_entry = 0; rquery = "";
for (i = 0; i < EmuConstants::POTION_BELT_SIZE; i++){ for (i = 0; i < Convert::POTION_BELT_ITEM_COUNT; i++){
if (pp->potionbelt.items[i].item_id > 0){ if (pp->potionbelt.Items[i].ID > 0){
if (first_entry != 1){ if (first_entry != 1){
rquery = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); rquery = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%i, %u, %u, %u)", character_id, i, pp->potionbelt.Items[i].ID, pp->potionbelt.Items[i].Icon);
first_entry = 1; first_entry = 1;
} }
rquery = rquery + StringFormat(", (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); rquery = rquery + StringFormat(", (%i, %u, %u, %u)", character_id, i, pp->potionbelt.Items[i].ID, pp->potionbelt.Items[i].Icon);
} }
} }

View File

@ -1,7 +1,7 @@
/* /*
EQEMu: Everquest Server Emulator EQEMu: Everquest Server Emulator
Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) Copyright (C) 2001-2015 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -25,8 +25,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
// class EmuConstants // class EmuConstants
// //
uint16 EmuConstants::InventoryMapSize(int16 indexMap) { uint16 EmuConstants::InventoryMapSize(int16 indexMap)
switch (indexMap) { {
switch (indexMap)
{
case MapPossessions: case MapPossessions:
return MAP_POSSESSIONS_SIZE; return MAP_POSSESSIONS_SIZE;
case MapBank: case MapBank:
@ -83,7 +85,8 @@ uint16 EmuConstants::InventoryMapSize(int16 indexMap) {
} }
/* /*
std::string EmuConstants::InventoryLocationName(Location_Struct location) { std::string EmuConstants::InventoryLocationName(Location_Struct location)
{
// not ready for implementation... // not ready for implementation...
std::string ret_str; std::string ret_str;
StringFormat(ret_str, "%s, %s, %s, %s", InventoryMapName(location.map), InventoryMainName(location.main), InventorySubName(location.sub), InventoryAugName(location.aug)); StringFormat(ret_str, "%s, %s, %s, %s", InventoryMapName(location.map), InventoryMainName(location.main), InventorySubName(location.sub), InventoryAugName(location.aug));
@ -91,8 +94,10 @@ std::string EmuConstants::InventoryLocationName(Location_Struct location) {
} }
*/ */
std::string EmuConstants::InventoryMapName(int16 indexMap) { std::string EmuConstants::InventoryMapName(int16 indexMap)
switch (indexMap) { {
switch (indexMap)
{
case INVALID_INDEX: case INVALID_INDEX:
return "Invalid Map"; return "Invalid Map";
case MapPossessions: case MapPossessions:
@ -100,7 +105,7 @@ std::string EmuConstants::InventoryMapName(int16 indexMap) {
case MapBank: case MapBank:
return "Bank"; return "Bank";
case MapSharedBank: case MapSharedBank:
return "Shared Bank"; return "SharedBank";
case MapTrade: case MapTrade:
return "Trade"; return "Trade";
case MapWorld: case MapWorld:
@ -110,9 +115,9 @@ std::string EmuConstants::InventoryMapName(int16 indexMap) {
case MapTribute: case MapTribute:
return "Tribute"; return "Tribute";
case MapTrophyTribute: case MapTrophyTribute:
return "Trophy Tribute"; return "TrophyTribute";
case MapGuildTribute: case MapGuildTribute:
return "Guild Tribute"; return "GuildTribute";
case MapMerchant: case MapMerchant:
return "Merchant"; return "Merchant";
case MapDeleted: case MapDeleted:
@ -124,23 +129,23 @@ std::string EmuConstants::InventoryMapName(int16 indexMap) {
case MapInspect: case MapInspect:
return "Inspect"; return "Inspect";
case MapRealEstate: case MapRealEstate:
return "Real Estate"; return "RealEstate";
case MapViewMODPC: case MapViewMODPC:
return "View MOD PC"; return "ViewMODPC";
case MapViewMODBank: case MapViewMODBank:
return "View MOD Bank"; return "ViewMODBank";
case MapViewMODSharedBank: case MapViewMODSharedBank:
return "View MOD Shared Bank"; return "ViewMODSharedBank";
case MapViewMODLimbo: case MapViewMODLimbo:
return "View MOD Limbo"; return "ViewMODLimbo";
case MapAltStorage: case MapAltStorage:
return "Alt Storage"; return "AltStorage";
case MapArchived: case MapArchived:
return "Archived"; return "Archived";
case MapMail: case MapMail:
return "Mail"; return "Mail";
case MapGuildTrophyTribute: case MapGuildTrophyTribute:
return "Guild Trophy Tribute"; return "GuildTrophyTribute";
case MapKrono: case MapKrono:
return "Krono"; return "Krono";
case MapOther: case MapOther:
@ -150,20 +155,22 @@ std::string EmuConstants::InventoryMapName(int16 indexMap) {
} }
} }
std::string EmuConstants::InventoryMainName(int16 indexMain) { std::string EmuConstants::InventoryMainName(int16 indexMain)
switch (indexMain) { {
switch (indexMain)
{
case INVALID_INDEX: case INVALID_INDEX:
return "Invalid Main"; return "Invalid Main";
case MainCharm: case MainCharm:
return "Charm"; return "Charm";
case MainEar1: case MainEar1:
return "Ear 1"; return "Ear1";
case MainHead: case MainHead:
return "Head"; return "Head";
case MainFace: case MainFace:
return "Face"; return "Face";
case MainEar2: case MainEar2:
return "Ear 2"; return "Ear2";
case MainNeck: case MainNeck:
return "Neck"; return "Neck";
case MainShoulders: case MainShoulders:
@ -173,9 +180,9 @@ std::string EmuConstants::InventoryMainName(int16 indexMain) {
case MainBack: case MainBack:
return "Back"; return "Back";
case MainWrist1: case MainWrist1:
return "Wrist 1"; return "Wrist1";
case MainWrist2: case MainWrist2:
return "Wrist 2"; return "Wrist2";
case MainRange: case MainRange:
return "Range"; return "Range";
case MainHands: case MainHands:
@ -185,9 +192,9 @@ std::string EmuConstants::InventoryMainName(int16 indexMain) {
case MainSecondary: case MainSecondary:
return "Secondary"; return "Secondary";
case MainFinger1: case MainFinger1:
return "Finger 1"; return "Finger1";
case MainFinger2: case MainFinger2:
return "Finger 2"; return "Finger2";
case MainChest: case MainChest:
return "Chest"; return "Chest";
case MainLegs: case MainLegs:
@ -197,30 +204,30 @@ std::string EmuConstants::InventoryMainName(int16 indexMain) {
case MainWaist: case MainWaist:
return "Waist"; return "Waist";
case MainPowerSource: case MainPowerSource:
return "Power Source"; return "PowerSource";
case MainAmmo: case MainAmmo:
return "Ammo"; return "Ammo";
case MainGeneral1: case MainGeneral1:
return "General 1"; return "General1";
case MainGeneral2: case MainGeneral2:
return "General 2"; return "General2";
case MainGeneral3: case MainGeneral3:
return "General 3"; return "General3";
case MainGeneral4: case MainGeneral4:
return "General 4"; return "General4";
case MainGeneral5: case MainGeneral5:
return "General 5"; return "General5";
case MainGeneral6: case MainGeneral6:
return "General 6"; return "General6";
case MainGeneral7: case MainGeneral7:
return "General 7"; return "General7";
case MainGeneral8: case MainGeneral8:
return "General 8"; return "General8";
/* /*
case MainGeneral9: case MainGeneral9:
return "General 9"; return "General9";
case MainGeneral10: case MainGeneral10:
return "General 10"; return "General10";
*/ */
case MainCursor: case MainCursor:
return "Cursor"; return "Cursor";
@ -229,7 +236,8 @@ std::string EmuConstants::InventoryMainName(int16 indexMain) {
} }
} }
std::string EmuConstants::InventorySubName(int16 indexSub) { std::string EmuConstants::InventorySubName(int16 indexSub)
{
if (indexSub == INVALID_INDEX) if (indexSub == INVALID_INDEX)
return "Invalid Sub"; return "Invalid Sub";
@ -237,12 +245,13 @@ std::string EmuConstants::InventorySubName(int16 indexSub) {
return "Unknown Sub"; return "Unknown Sub";
std::string ret_str; std::string ret_str;
ret_str = StringFormat("Container %i", (indexSub + 1)); // zero-based index..but, count starts at one ret_str = StringFormat("Container%i", (indexSub + 1)); // zero-based index..but, count starts at one
return ret_str; return ret_str;
} }
std::string EmuConstants::InventoryAugName(int16 indexAug) { std::string EmuConstants::InventoryAugName(int16 indexAug)
{
if (indexAug == INVALID_INDEX) if (indexAug == INVALID_INDEX)
return "Invalid Aug"; return "Invalid Aug";
@ -250,7 +259,7 @@ std::string EmuConstants::InventoryAugName(int16 indexAug) {
return "Unknown Aug"; return "Unknown Aug";
std::string ret_str; std::string ret_str;
ret_str = StringFormat("Augment %i", (indexAug + 1)); // zero-based index..but, count starts at one ret_str = StringFormat("Augment%i", (indexAug + 1)); // zero-based index..but, count starts at one
return ret_str; return ret_str;
} }
@ -260,14 +269,16 @@ std::string EmuConstants::InventoryAugName(int16 indexAug) {
// class EQLimits // class EQLimits
// //
// client validation // client validation
bool EQLimits::IsValidPCClientVersion(ClientVersion clientVersion) { bool EQLimits::IsValidPCClientVersion(ClientVersion clientVersion)
{
if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_PC_CLIENT) if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_PC_CLIENT)
return true; return true;
return false; return false;
} }
ClientVersion EQLimits::ValidatePCClientVersion(ClientVersion clientVersion) { ClientVersion EQLimits::ValidatePCClientVersion(ClientVersion clientVersion)
{
if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_PC_CLIENT) if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_PC_CLIENT)
return clientVersion; return clientVersion;
@ -275,14 +286,16 @@ ClientVersion EQLimits::ValidatePCClientVersion(ClientVersion clientVersion) {
} }
// npc validation // npc validation
bool EQLimits::IsValidNPCClientVersion(ClientVersion clientVersion) { bool EQLimits::IsValidNPCClientVersion(ClientVersion clientVersion)
{
if (clientVersion > LAST_PC_CLIENT && clientVersion <= LAST_NPC_CLIENT) if (clientVersion > LAST_PC_CLIENT && clientVersion <= LAST_NPC_CLIENT)
return true; return true;
return false; return false;
} }
ClientVersion EQLimits::ValidateNPCClientVersion(ClientVersion clientVersion) { ClientVersion EQLimits::ValidateNPCClientVersion(ClientVersion clientVersion)
{
if (clientVersion > LAST_PC_CLIENT && clientVersion <= LAST_NPC_CLIENT) if (clientVersion > LAST_PC_CLIENT && clientVersion <= LAST_NPC_CLIENT)
return clientVersion; return clientVersion;
@ -290,22 +303,47 @@ ClientVersion EQLimits::ValidateNPCClientVersion(ClientVersion clientVersion) {
} }
// mob validation // mob validation
bool EQLimits::IsValidMobClientVersion(ClientVersion clientVersion) { bool EQLimits::IsValidMobClientVersion(ClientVersion clientVersion)
{
if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_NPC_CLIENT) if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_NPC_CLIENT)
return true; return true;
return false; return false;
} }
ClientVersion EQLimits::ValidateMobClientVersion(ClientVersion clientVersion) { ClientVersion EQLimits::ValidateMobClientVersion(ClientVersion clientVersion)
{
if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_NPC_CLIENT) if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_NPC_CLIENT)
return clientVersion; return clientVersion;
return ClientVersion::Unknown; return ClientVersion::Unknown;
} }
// database
size_t EQLimits::CharacterCreationLimit(ClientVersion clientVersion)
{
static const size_t local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ NOT_USED,
/*Client62*/ NOT_USED,
/*Titanium*/ Titanium::consts::CHARACTER_CREATION_LIMIT,
/*SoF*/ SoF::consts::CHARACTER_CREATION_LIMIT,
/*SoD*/ SoD::consts::CHARACTER_CREATION_LIMIT,
/*UF*/ UF::consts::CHARACTER_CREATION_LIMIT,
/*RoF*/ RoF::consts::CHARACTER_CREATION_LIMIT,
/*RoF2*/ RoF2::consts::CHARACTER_CREATION_LIMIT,
/*MobNPC*/ NOT_USED,
/*MobMerc*/ NOT_USED,
/*MobBot*/ NOT_USED,
/*MobPet*/ NOT_USED
};
return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
}
// inventory // inventory
uint16 EQLimits::InventoryMapSize(int16 indexMap, ClientVersion clientVersion) { uint16 EQLimits::InventoryMapSize(int16 indexMap, ClientVersion clientVersion)
{
// not all maps will have an instantiated container..some are references for queue generators (i.e., bazaar, mail, etc...) // not all maps will have an instantiated container..some are references for queue generators (i.e., bazaar, mail, etc...)
// a zero '0' indicates a needed value..otherwise, change to '_NOTUSED' for a null value so indices requiring research can be identified // a zero '0' indicates a needed value..otherwise, change to '_NOTUSED' for a null value so indices requiring research can be identified
// ALL of these values need to be verified before pushing to live // ALL of these values need to be verified before pushing to live
@ -704,7 +742,8 @@ uint16 EQLimits::InventoryMapSize(int16 indexMap, ClientVersion clientVersion) {
return NOT_USED; return NOT_USED;
} }
uint64 EQLimits::PossessionsBitmask(ClientVersion clientVersion) { uint64 EQLimits::PossessionsBitmask(ClientVersion clientVersion)
{
// these are for the new inventory system (RoF)..not the current (Ti) one... // these are for the new inventory system (RoF)..not the current (Ti) one...
// 0x0000000000200000 is SlotPowerSource (SoF+) // 0x0000000000200000 is SlotPowerSource (SoF+)
// 0x0000000080000000 is SlotGeneral9 (RoF+) // 0x0000000080000000 is SlotGeneral9 (RoF+)
@ -730,7 +769,8 @@ uint64 EQLimits::PossessionsBitmask(ClientVersion clientVersion) {
//return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))]; //return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
} }
uint64 EQLimits::EquipmentBitmask(ClientVersion clientVersion) { uint64 EQLimits::EquipmentBitmask(ClientVersion clientVersion)
{
static const uint64 local[CLIENT_VERSION_COUNT] = { static const uint64 local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ NOT_USED, /*Unknown*/ NOT_USED,
/*62*/ 0x00000000005FFFFF, /*62*/ 0x00000000005FFFFF,
@ -751,7 +791,8 @@ uint64 EQLimits::EquipmentBitmask(ClientVersion clientVersion) {
//return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))]; //return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
} }
uint64 EQLimits::GeneralBitmask(ClientVersion clientVersion) { uint64 EQLimits::GeneralBitmask(ClientVersion clientVersion)
{
static const uint64 local[CLIENT_VERSION_COUNT] = { static const uint64 local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ NOT_USED, /*Unknown*/ NOT_USED,
/*62*/ 0x000000007F800000, /*62*/ 0x000000007F800000,
@ -772,7 +813,8 @@ uint64 EQLimits::GeneralBitmask(ClientVersion clientVersion) {
//return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))]; //return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
} }
uint64 EQLimits::CursorBitmask(ClientVersion clientVersion) { uint64 EQLimits::CursorBitmask(ClientVersion clientVersion)
{
static const uint64 local[CLIENT_VERSION_COUNT] = { static const uint64 local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ NOT_USED, /*Unknown*/ NOT_USED,
/*62*/ 0x0000000200000000, /*62*/ 0x0000000200000000,
@ -793,7 +835,8 @@ uint64 EQLimits::CursorBitmask(ClientVersion clientVersion) {
//return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))]; //return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
} }
bool EQLimits::AllowsEmptyBagInBag(ClientVersion clientVersion) { bool EQLimits::AllowsEmptyBagInBag(ClientVersion clientVersion)
{
static const bool local[CLIENT_VERSION_COUNT] = { static const bool local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ false, /*Unknown*/ false,
/*62*/ false, /*62*/ false,
@ -814,7 +857,8 @@ bool EQLimits::AllowsEmptyBagInBag(ClientVersion clientVersion) {
//return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))]; //return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
} }
bool EQLimits::AllowsClickCastFromBag(ClientVersion clientVersion) { bool EQLimits::AllowsClickCastFromBag(ClientVersion clientVersion)
{
static const bool local[CLIENT_VERSION_COUNT] = { static const bool local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ false, /*Unknown*/ false,
/*62*/ false, /*62*/ false,
@ -835,7 +879,8 @@ bool EQLimits::AllowsClickCastFromBag(ClientVersion clientVersion) {
} }
// items // items
uint16 EQLimits::ItemCommonSize(ClientVersion clientVersion) { uint16 EQLimits::ItemCommonSize(ClientVersion clientVersion)
{
static const uint16 local[CLIENT_VERSION_COUNT] = { static const uint16 local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ NOT_USED, /*Unknown*/ NOT_USED,
/*62*/ EmuConstants::ITEM_COMMON_SIZE, /*62*/ EmuConstants::ITEM_COMMON_SIZE,
@ -855,7 +900,8 @@ uint16 EQLimits::ItemCommonSize(ClientVersion clientVersion) {
return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))]; return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
} }
uint16 EQLimits::ItemContainerSize(ClientVersion clientVersion) { uint16 EQLimits::ItemContainerSize(ClientVersion clientVersion)
{
static const uint16 local[CLIENT_VERSION_COUNT] = { static const uint16 local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ NOT_USED, /*Unknown*/ NOT_USED,
/*62*/ EmuConstants::ITEM_CONTAINER_SIZE, /*62*/ EmuConstants::ITEM_CONTAINER_SIZE,
@ -875,7 +921,8 @@ uint16 EQLimits::ItemContainerSize(ClientVersion clientVersion) {
return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))]; return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
} }
bool EQLimits::CoinHasWeight(ClientVersion clientVersion) { bool EQLimits::CoinHasWeight(ClientVersion clientVersion)
{
static const bool local[CLIENT_VERSION_COUNT] = { static const bool local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ true, /*Unknown*/ true,
/*62*/ true, /*62*/ true,
@ -894,63 +941,3 @@ bool EQLimits::CoinHasWeight(ClientVersion clientVersion) {
return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))]; return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
} }
uint32 EQLimits::BandoliersCount(ClientVersion clientVersion) {
static const uint32 local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ NOT_USED,
/*62*/ EmuConstants::BANDOLIERS_COUNT,
/*Titanium*/ EmuConstants::BANDOLIERS_COUNT,
/*SoF*/ EmuConstants::BANDOLIERS_COUNT,
/*SoD*/ EmuConstants::BANDOLIERS_COUNT,
/*Underfoot*/ EmuConstants::BANDOLIERS_COUNT,
/*RoF*/ EmuConstants::BANDOLIERS_COUNT,
/*RoF2*/ EmuConstants::BANDOLIERS_COUNT,
/*NPC*/ NOT_USED,
/*Merc*/ NOT_USED,
/*Bot*/ NOT_USED,
/*Pet*/ NOT_USED
};
return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
}
uint32 EQLimits::BandolierSize(ClientVersion clientVersion) {
static const uint32 local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ NOT_USED,
/*62*/ EmuConstants::BANDOLIER_SIZE,
/*Titanium*/ EmuConstants::BANDOLIER_SIZE,
/*SoF*/ EmuConstants::BANDOLIER_SIZE,
/*SoD*/ EmuConstants::BANDOLIER_SIZE,
/*Underfoot*/ EmuConstants::BANDOLIER_SIZE,
/*RoF*/ EmuConstants::BANDOLIER_SIZE,
/*RoF2*/ EmuConstants::BANDOLIER_SIZE,
/*NPC*/ NOT_USED,
/*Merc*/ NOT_USED,
/*Bot*/ NOT_USED,
/*Pet*/ NOT_USED
};
return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
}
uint32 EQLimits::PotionBeltSize(ClientVersion clientVersion) {
static const uint32 local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ NOT_USED,
/*62*/ EmuConstants::POTION_BELT_SIZE,
/*Titanium*/ EmuConstants::POTION_BELT_SIZE,
/*SoF*/ EmuConstants::POTION_BELT_SIZE,
/*SoD*/ EmuConstants::POTION_BELT_SIZE,
/*Underfoot*/ EmuConstants::POTION_BELT_SIZE,
/*RoF*/ EmuConstants::POTION_BELT_SIZE,
/*RoF2*/ EmuConstants::POTION_BELT_SIZE,
/*NPC*/ NOT_USED,
/*Merc*/ NOT_USED,
/*Bot*/ NOT_USED,
/*Pet*/ NOT_USED
};
return local[static_cast<unsigned int>(ValidateMobClientVersion(clientVersion))];
}

View File

@ -1,7 +1,7 @@
/* /*
EQEMu: Everquest Server Emulator EQEMu: Everquest Server Emulator
Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) Copyright (C) 2001-2015 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -42,12 +42,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//using namespace RoF2::maps; // server inventory maps enumeration (code and database sync'd to reference) //using namespace RoF2::maps; // server inventory maps enumeration (code and database sync'd to reference)
//using namespace RoF::slots; // server possessions slots enumeration (code and database sync'd to reference) //using namespace RoF::slots; // server possessions slots enumeration (code and database sync'd to reference)
class EmuConstants { class EmuConstants
{
// an immutable value is required to initialize arrays, etc... use this class as a repository for those // an immutable value is required to initialize arrays, etc... use this class as a repository for those
public: public:
// database // database
static const ClientVersion CHARACTER_CREATION_CLIENT = ClientVersion::RoF2; // adjust according to starting item placement and target client static const ClientVersion CHARACTER_CREATION_CLIENT = ClientVersion::RoF2; // adjust according to starting item placement and target client
static const size_t CHARACTER_CREATION_LIMIT = RoF2::consts::CHARACTER_CREATION_LIMIT;
// inventory // inventory
static uint16 InventoryMapSize(int16 indexMap); static uint16 InventoryMapSize(int16 indexMap);
//static std::string InventoryLocationName(Location_Struct location); //static std::string InventoryLocationName(Location_Struct location);
@ -140,23 +143,18 @@ public:
static const uint16 ITEM_COMMON_SIZE = RoF::consts::ITEM_COMMON_SIZE; static const uint16 ITEM_COMMON_SIZE = RoF::consts::ITEM_COMMON_SIZE;
static const uint16 ITEM_CONTAINER_SIZE = Titanium::consts::ITEM_CONTAINER_SIZE; static const uint16 ITEM_CONTAINER_SIZE = Titanium::consts::ITEM_CONTAINER_SIZE;
// player profile // BANDOLIERS_SIZE sets maximum limit..active limit will need to be handled by the appropriate AA or spell (or item?)
//static const uint32 CLASS_BITMASK = 0; // needs value static const size_t BANDOLIERS_SIZE = RoF2::consts::BANDOLIERS_SIZE; // number of bandolier instances
//static const uint32 RACE_BITMASK = 0; // needs value static const size_t BANDOLIER_ITEM_COUNT = RoF2::consts::BANDOLIER_ITEM_COUNT; // number of equipment slots in bandolier instance
// BANDOLIERS_COUNT sets maximum limit..active limit will need to be handled by the appropriate AA // POTION_BELT_SIZE sets maximum limit..active limit will need to be handled by the appropriate AA or spell (or item?)
static const uint32 BANDOLIERS_COUNT = Titanium::consts::BANDOLIERS_COUNT; // count = number of bandolier instances static const size_t POTION_BELT_ITEM_COUNT = RoF2::consts::POTION_BELT_ITEM_COUNT;
static const uint32 BANDOLIER_SIZE = Titanium::consts::BANDOLIER_SIZE; // size = number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = Titanium::consts::POTION_BELT_SIZE;
static const size_t TEXT_LINK_BODY_LENGTH = 56; static const size_t TEXT_LINK_BODY_LENGTH = RoF2::consts::TEXT_LINK_BODY_LENGTH;
// legacy-related functions
//static int ServerToPerlSlot(int slot); // encode
//static int PerlToServerSlot(int slot); // decode
}; };
class EQLimits { class EQLimits
{
// values should default to a non-beneficial value..unless value conflicts with intended operation // values should default to a non-beneficial value..unless value conflicts with intended operation
// //
// EmuConstants may be used as references..but, not every reference needs to be in EmuConstants (i.e., AllowsEmptyBagInBag(), CoinHasWeight(), etc...) // EmuConstants may be used as references..but, not every reference needs to be in EmuConstants (i.e., AllowsEmptyBagInBag(), CoinHasWeight(), etc...)
@ -174,6 +172,9 @@ public:
static bool IsValidMobClientVersion(ClientVersion clientVersion); static bool IsValidMobClientVersion(ClientVersion clientVersion);
static ClientVersion ValidateMobClientVersion(ClientVersion clientVersion); static ClientVersion ValidateMobClientVersion(ClientVersion clientVersion);
// database
static size_t CharacterCreationLimit(ClientVersion clientVersion);
// inventory // inventory
static uint16 InventoryMapSize(int16 indexMap, ClientVersion clientVersion); static uint16 InventoryMapSize(int16 indexMap, ClientVersion clientVersion);
static uint64 PossessionsBitmask(ClientVersion clientVersion); static uint64 PossessionsBitmask(ClientVersion clientVersion);
@ -190,11 +191,6 @@ public:
// player profile // player profile
static bool CoinHasWeight(ClientVersion clientVersion); static bool CoinHasWeight(ClientVersion clientVersion);
static uint32 BandoliersCount(ClientVersion clientVersion);
static uint32 BandolierSize(ClientVersion clientVersion);
static uint32 PotionBeltSize(ClientVersion clientVersion);
}; };
#endif /* EQ_DICTIONARY_H */ #endif /* EQ_DICTIONARY_H */

View File

@ -15,6 +15,7 @@
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#ifndef EQ_PACKET_STRUCTS_H #ifndef EQ_PACKET_STRUCTS_H
#define EQ_PACKET_STRUCTS_H #define EQ_PACKET_STRUCTS_H
@ -123,83 +124,80 @@ struct LDoNTrapTemplate
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/* // All clients translate the character select information to some degree
** Color_Struct
** Size: 4 bytes
** Used for convenience
** Merth: Gave struct a name so gcc 2.96 would compile
**
*/
struct Color_Struct struct Color_Struct
{ {
union union {
{ struct {
struct uint8 Blue;
{ uint8 Green;
uint8 blue; uint8 Red;
uint8 green; uint8 UseTint; // if there's a tint this is FF
uint8 red; } RGB;
uint8 use_tint; // if there's a tint this is FF uint32 Color;
} rgb;
uint32 color;
}; };
}; };
/* struct EquipStruct
* Visible equiptment. {
* Size: 20 Octets uint32 Material;
*/ uint32 Unknown1;
struct EquipStruct { uint32 EliteMaterial;
/*00*/ uint32 material; uint32 HeroForgeModel;
/*04*/ uint32 unknown1; uint32 Material2; // Same as material?
/*08*/ uint32 elitematerial;
/*12*/ uint32 heroforgemodel;
/*16*/ uint32 material2; // Same as material?
/*20*/
}; };
struct CharSelectEquip { struct CharSelectEquip
uint32 material; {
uint32 unknown1; uint32 Material;
uint32 elitematerial; uint32 Unknown1;
uint32 heroforgemodel; uint32 EliteMaterial;
uint32 material2; uint32 HeroForgeModel;
Color_Struct color; uint32 Material2;
Color_Struct Color;
}; };
/* struct CharacterSelectEntry_Struct
** Character Selection Struct {
** Length: 1704 Bytes char Name[64];
** uint8 Class;
*/ uint32 Race;
struct CharacterSelect_Struct { uint8 Level;
/*0000*/ uint32 race[10]; // Characters Race uint8 ShroudClass;
/*0040*/ //Color_Struct cs_colors[10][9]; // Characters Equipment Colors uint32 ShroudRace;
/*0400*/ uint8 beardcolor[10]; // Characters beard Color uint16 Zone;
/*0410*/ uint8 hairstyle[10]; // Characters hair style uint16 Instance;
/*0420*/ //uint32 equip[10][9]; // 0=helm, 1=chest, 2=arm, 3=bracer, 4=hand, 5=leg, 6=boot, 7=melee1, 8=melee2 (Might not be) uint8 Gender;
/*0000*/ CharSelectEquip equip[10][9]; uint8 Face;
/*0780*/ uint32 secondary[10]; // Characters secondary IDFile number CharSelectEquip Equip[9];
/*0820*/ uint32 drakkin_heritage[10]; // added for SoF uint8 Unknown15; // Seen FF
/*0860*/ uint32 drakkin_tattoo[10]; // added for SoF uint8 Unknown19; // Seen FF
/*0900*/ uint32 drakkin_details[10]; // added for SoF uint32 DrakkinTattoo;
/*0940*/ uint32 deity[10]; // Characters Deity uint32 DrakkinDetails;
/*0980*/ uint8 gohome[10]; // 1=Go Home available, 0=not uint32 Deity;
/*0990*/ uint8 tutorial[10]; // 1=Tutorial available, 0=not uint32 PrimaryIDFile;
/*1000*/ uint8 beard[10]; // Characters Beard Type uint32 SecondaryIDFile;
/*1010*/ uint8 unknown902[10]; // 10x ff uint8 HairColor;
/*1020*/ uint32 primary[10]; // Characters primary IDFile number uint8 BeardColor;
/*1060*/ uint8 haircolor[10]; // Characters Hair Color uint8 EyeColor1;
/*1070*/ uint8 unknown0962[2]; // 2x 00 uint8 EyeColor2;
/*1072*/ uint32 zone[10]; // Characters Current Zone uint8 HairStyle;
/*1112*/ uint8 class_[10]; // Characters Classes uint8 Beard;
/*1022*/ uint8 face[10]; // Characters Face Type uint8 Enabled;
/*1032*/ char name[10][64]; // Characters Names uint8 Tutorial; // Seen 1 for new char or 0 for existing
/*1672*/ uint8 gender[10]; // Characters Gender uint32 DrakkinHeritage;
/*1682*/ uint8 eyecolor1[10]; // Characters Eye Color uint8 Unknown1; // Seen 0
/*1692*/ uint8 eyecolor2[10]; // Characters Eye 2 Color uint8 GoHome; // Seen 0 for new char and 1 for existing
/*1702*/ uint8 level[10]; // Characters Levels uint32 LastLogin;
/*1712*/ uint8 Unknown2; // Seen 0
};
struct CharacterSelect_Struct
{
uint32 CharCount; //number of chars in this packet
uint32 TotalChars; //total number of chars allowed?
CharacterSelectEntry_Struct Entries[0];
}; };
/* /*
@ -756,29 +754,46 @@ struct Tribute_Struct {
uint32 tier; uint32 tier;
}; };
//len = 72 // Bandolier item positions
struct BandolierItem_Struct { enum
uint32 item_id; {
uint32 icon; bandolierPrimary = 0,
char item_name[64]; bandolierSecondary,
};
//len = 320
enum { //bandolier item positions
bandolierMainHand = 0,
bandolierOffHand,
bandolierRange, bandolierRange,
bandolierAmmo bandolierAmmo
}; };
struct Bandolier_Struct {
char name[32]; //len = 72
BandolierItem_Struct items[EmuConstants::BANDOLIER_SIZE]; struct BandolierItem_Struct
}; {
struct PotionBelt_Struct { uint32 ID;
BandolierItem_Struct items[EmuConstants::POTION_BELT_SIZE]; uint32 Icon;
char Name[64];
}; };
struct MovePotionToBelt_Struct { //len = 320
struct Bandolier_Struct
{
char Name[32];
BandolierItem_Struct Items[EmuConstants::BANDOLIER_ITEM_COUNT];
};
//len = 72
struct PotionBeltItem_Struct
{
uint32 ID;
uint32 Icon;
char Name[64];
};
//len = 288
struct PotionBelt_Struct
{
PotionBeltItem_Struct Items[EmuConstants::POTION_BELT_ITEM_COUNT];
};
struct MovePotionToBelt_Struct
{
uint32 Action; uint32 Action;
uint32 SlotNumber; uint32 SlotNumber;
uint32 ItemID; uint32 ItemID;
@ -1103,7 +1118,7 @@ struct PlayerProfile_Struct
/*12800*/ uint32 expAA; /*12800*/ uint32 expAA;
/*12804*/ uint32 aapoints; //avaliable, unspent /*12804*/ uint32 aapoints; //avaliable, unspent
/*12808*/ uint8 unknown12844[36]; /*12808*/ uint8 unknown12844[36];
/*12844*/ Bandolier_Struct bandoliers[EmuConstants::BANDOLIERS_COUNT]; /*12844*/ Bandolier_Struct bandoliers[EmuConstants::BANDOLIERS_SIZE];
/*14124*/ uint8 unknown14160[4506]; /*14124*/ uint8 unknown14160[4506];
/*18630*/ SuspendedMinion_Struct SuspendedMinion; // No longer in use /*18630*/ SuspendedMinion_Struct SuspendedMinion; // No longer in use
/*19240*/ uint32 timeentitledonaccount; /*19240*/ uint32 timeentitledonaccount;
@ -4106,30 +4121,35 @@ struct DynamicWall_Struct {
/*80*/ /*80*/
}; };
enum { //bandolier actions // Bandolier actions
BandolierCreate = 0, enum
BandolierRemove = 1, {
BandolierSet = 2 bandolierCreate = 0,
bandolierRemove,
bandolierSet
}; };
struct BandolierCreate_Struct { struct BandolierCreate_Struct
/*00*/ uint32 action; //0 for create {
/*04*/ uint8 number; /*00*/ uint32 Action; //0 for create
/*05*/ char name[32]; /*04*/ uint8 Number;
/*37*/ uint16 unknown37; //seen 0x93FD /*05*/ char Name[32];
/*39*/ uint8 unknown39; //0 /*37*/ uint16 Unknown37; //seen 0x93FD
/*39*/ uint8 Unknown39; //0
}; };
struct BandolierDelete_Struct { struct BandolierDelete_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct BandolierSet_Struct { struct BandolierSet_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct Arrow_Struct { struct Arrow_Struct {

View File

@ -2043,7 +2043,7 @@ namespace RoF
for (int r = 0; r < 7; r++) for (int r = 0; r < 7; r++)
{ {
outapp->WriteUInt32(emu->item_tint[r].color); outapp->WriteUInt32(emu->item_tint[r].Color);
} }
// Write zeroes for extra two tint values // Write zeroes for extra two tint values
outapp->WriteUInt32(0); outapp->WriteUInt32(0);
@ -2053,7 +2053,7 @@ namespace RoF
for (int r = 0; r < 7; r++) for (int r = 0; r < 7; r++)
{ {
outapp->WriteUInt32(emu->item_tint[r].color); outapp->WriteUInt32(emu->item_tint[r].Color);
} }
// Write zeroes for extra two tint values // Write zeroes for extra two tint values
outapp->WriteUInt32(0); outapp->WriteUInt32(0);
@ -2286,58 +2286,49 @@ namespace RoF
outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown
outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown
outapp->WriteUInt32(structs::MAX_PLAYER_BANDOLIER); outapp->WriteUInt32(consts::BANDOLIERS_SIZE);
for (uint32 r = 0; r < EmuConstants::BANDOLIERS_COUNT; r++) // Copy bandoliers where server and client indexes converge
{ for (uint32 r = 0; r < EmuConstants::BANDOLIERS_SIZE && r < consts::BANDOLIERS_SIZE; ++r) {
outapp->WriteString(emu->bandoliers[r].name); outapp->WriteString(emu->bandoliers[r].Name);
for (uint32 j = 0; j < consts::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true
for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j) outapp->WriteString(emu->bandoliers[r].Items[j].Name);
{ outapp->WriteUInt32(emu->bandoliers[r].Items[j].ID);
outapp->WriteString(emu->bandoliers[r].items[j].item_name); if (emu->bandoliers[r].Items[j].Icon) {
outapp->WriteUInt32(emu->bandoliers[r].items[j].item_id); outapp->WriteSInt32(emu->bandoliers[r].Items[j].Icon);
if (emu->bandoliers[r].items[j].icon)
{
outapp->WriteSInt32(emu->bandoliers[r].items[j].icon);
} }
else else {
{
// If no icon, it must send -1 or Treasure Chest Icon (836) is displayed // If no icon, it must send -1 or Treasure Chest Icon (836) is displayed
outapp->WriteSInt32(-1); outapp->WriteSInt32(-1);
} }
} }
} }
// Nullify bandoliers where server and client indexes diverge, with a client bias
for (uint32 r = 0; r < structs::MAX_PLAYER_BANDOLIER - EmuConstants::BANDOLIERS_COUNT; r++) for (uint32 r = EmuConstants::BANDOLIERS_SIZE; r < consts::BANDOLIERS_SIZE; ++r) {
{
outapp->WriteString(""); outapp->WriteString("");
for (uint32 j = 0; j < consts::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true
for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j)
{
outapp->WriteString(""); outapp->WriteString("");
outapp->WriteUInt32(0); outapp->WriteUInt32(0);
outapp->WriteSInt32(-1); outapp->WriteSInt32(-1);
} }
} }
outapp->WriteUInt32(structs::MAX_POTIONS_IN_BELT); outapp->WriteUInt32(consts::POTION_BELT_ITEM_COUNT);
for (uint32 r = 0; r < EmuConstants::POTION_BELT_SIZE; r++) // Copy potion belt where server and client indexes converge
{ for (uint32 r = 0; r < EmuConstants::POTION_BELT_ITEM_COUNT && r < consts::POTION_BELT_ITEM_COUNT; ++r) {
outapp->WriteString(emu->potionbelt.items[r].item_name); outapp->WriteString(emu->potionbelt.Items[r].Name);
outapp->WriteUInt32(emu->potionbelt.items[r].item_id); outapp->WriteUInt32(emu->potionbelt.Items[r].ID);
if (emu->potionbelt.items[r].icon) if (emu->potionbelt.Items[r].Icon) {
{ outapp->WriteSInt32(emu->potionbelt.Items[r].Icon);
outapp->WriteSInt32(emu->potionbelt.items[r].icon);
} }
else else {
{ // If no icon, it must send -1 or Treasure Chest Icon (836) is displayed
outapp->WriteSInt32(-1); outapp->WriteSInt32(-1);
} }
} }
// Nullify potion belt where server and client indexes diverge, with a client bias
for (uint32 r = 0; r < structs::MAX_POTIONS_IN_BELT - EmuConstants::POTION_BELT_SIZE; r++) for (uint32 r = EmuConstants::POTION_BELT_ITEM_COUNT; r < consts::POTION_BELT_ITEM_COUNT; ++r) {
{
outapp->WriteString(""); outapp->WriteString("");
outapp->WriteUInt32(0); outapp->WriteUInt32(0);
outapp->WriteSInt32(-1); outapp->WriteSInt32(-1);
@ -2906,85 +2897,99 @@ namespace RoF
ENCODE(OP_SendCharInfo) ENCODE(OP_SendCharInfo)
{ {
ENCODE_LENGTH_EXACT(CharacterSelect_Struct); ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_VAR_ENCODE(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct);
//EQApplicationPacket *packet = *p; // Zero-character count shunt
//const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; if (emu->CharCount == 0) {
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
eq->CharCount = emu->CharCount;
int char_count; FINISH_ENCODE();
int namelen = 0; return;
for (char_count = 0; char_count < 10; char_count++) {
if (emu->name[char_count][0] == '\0')
break;
if (strcmp(emu->name[char_count], "<none>") == 0)
break;
namelen += strlen(emu->name[char_count]);
} }
int total_length = sizeof(structs::CharacterSelect_Struct) unsigned char *emu_ptr = __emu_buffer;
+ char_count * sizeof(structs::CharacterSelectEntry_Struct) emu_ptr += sizeof(CharacterSelect_Struct);
+ namelen; CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr;
size_t names_length = 0;
size_t character_count = 0;
for (; character_count < emu->CharCount && character_count < consts::CHARACTER_CREATION_LIMIT; ++character_count) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
names_length += strlen(emu_cse->Name);
emu_ptr += sizeof(CharacterSelectEntry_Struct);
}
size_t total_length = sizeof(structs::CharacterSelect_Struct)
+ character_count * sizeof(structs::CharacterSelectEntry_Struct)
+ names_length;
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length);
structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr;
//unsigned char *eq_buffer = new unsigned char[total_length]; eq->CharCount = character_count;
//structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; //eq->TotalChars = emu->TotalChars;
eq->char_count = char_count; //if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
//eq->total_chars = 10; // eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
unsigned char *bufptr = (unsigned char *)eq->entries; emu_ptr = __emu_buffer;
int r; emu_ptr += sizeof(CharacterSelect_Struct);
for (r = 0; r < char_count; r++) {
{ //pre-name section... unsigned char *eq_ptr = __packet->pBuffer;
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; eq_ptr += sizeof(structs::CharacterSelect_Struct);
memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1);
for (int counter = 0; counter < character_count; ++counter) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr;
strcpy(eq_cse->Name, emu_cse->Name);
eq_ptr += strlen(eq_cse->Name);
eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr;
eq_cse->Class = emu_cse->Class;
eq_cse->Race = emu_cse->Race;
eq_cse->Level = emu_cse->Level;
eq_cse->ShroudClass = emu_cse->ShroudClass;
eq_cse->ShroudRace = emu_cse->ShroudRace;
eq_cse->Zone = emu_cse->Zone;
eq_cse->Instance = emu_cse->Instance;
eq_cse->Gender = emu_cse->Gender;
eq_cse->Face = emu_cse->Face;
for (int equip_index = 0; equip_index < _MaterialCount; equip_index++) {
eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material;
eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1;
eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteMaterial;
eq_cse->Equip[equip_index].HeroForgeModel = emu_cse->Equip[equip_index].HeroForgeModel;
eq_cse->Equip[equip_index].Material2 = emu_cse->Equip[equip_index].Material2;
eq_cse->Equip[equip_index].Color.Color = emu_cse->Equip[equip_index].Color.Color;
} }
//adjust for name.
bufptr += strlen(emu->name[r]); eq_cse->Unknown15 = emu_cse->Unknown15;
{ //post-name section... eq_cse->Unknown19 = emu_cse->Unknown19;
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo;
eq2->class_ = emu->class_[r]; eq_cse->DrakkinDetails = emu_cse->DrakkinDetails;
eq2->race = emu->race[r]; eq_cse->Deity = emu_cse->Deity;
eq2->level = emu->level[r]; eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile;
eq2->class_2 = emu->class_[r]; eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile;
eq2->race2 = emu->race[r]; eq_cse->HairColor = emu_cse->HairColor;
eq2->zone = emu->zone[r]; eq_cse->BeardColor = emu_cse->BeardColor;
eq2->instance = 0; eq_cse->EyeColor1 = emu_cse->EyeColor1;
eq2->gender = emu->gender[r]; eq_cse->EyeColor2 = emu_cse->EyeColor2;
eq2->face = emu->face[r]; eq_cse->HairStyle = emu_cse->HairStyle;
int k; eq_cse->Beard = emu_cse->Beard;
for (k = 0; k < _MaterialCount; k++) { eq_cse->Enabled = emu_cse->Enabled;
eq2->equip[k].material = emu->equip[r][k].material; eq_cse->Tutorial = emu_cse->Tutorial;
eq2->equip[k].unknown1 = emu->equip[r][k].unknown1; eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage;
eq2->equip[k].elitematerial = emu->equip[r][k].elitematerial; eq_cse->Unknown1 = emu_cse->Unknown1;
eq2->equip[k].heroforgemodel = emu->equip[r][k].heroforgemodel; eq_cse->GoHome = emu_cse->GoHome;
eq2->equip[k].material2 = emu->equip[r][k].material2; eq_cse->LastLogin = emu_cse->LastLogin;
eq2->equip[k].color.color = emu->equip[r][k].color.color; eq_cse->Unknown2 = emu_cse->Unknown2;
}
eq2->u15 = 0xff; emu_ptr += sizeof(CharacterSelectEntry_Struct);
eq2->u19 = 0xFF; eq_ptr += sizeof(structs::CharacterSelectEntry_Struct);
eq2->drakkin_tattoo = emu->drakkin_tattoo[r];
eq2->drakkin_details = emu->drakkin_details[r];
eq2->deity = emu->deity[r];
eq2->primary = emu->primary[r];
eq2->secondary = emu->secondary[r];
eq2->haircolor = emu->haircolor[r];
eq2->beardcolor = emu->beardcolor[r];
eq2->eyecolor1 = emu->eyecolor1[r];
eq2->eyecolor2 = emu->eyecolor2[r];
eq2->hairstyle = emu->hairstyle[r];
eq2->beard = emu->beard[r];
eq2->char_enabled = 1;
eq2->tutorial = emu->tutorial[r];
eq2->drakkin_heritage = emu->drakkin_heritage[r];
eq2->unknown1 = 0;
eq2->gohome = emu->gohome[r];
eq2->LastLogin = 1212696584;
eq2->unknown2 = 0;
}
bufptr += sizeof(structs::CharacterSelectEntry_Struct);
} }
FINISH_ENCODE(); FINISH_ENCODE();
@ -3648,7 +3653,7 @@ namespace RoF
OUT(elite_material); OUT(elite_material);
OUT(hero_forge_model); OUT(hero_forge_model);
OUT(unknown18); OUT(unknown18);
OUT(color.color); OUT(color.Color);
OUT(wear_slot_id); OUT(wear_slot_id);
FINISH_ENCODE(); FINISH_ENCODE();
@ -3959,18 +3964,18 @@ namespace RoF
for (k = 0; k < 9; ++k) for (k = 0; k < 9; ++k)
{ {
{ {
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].Color);
} }
} }
structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer;
for (k = 0; k < 9; k++) { for (k = 0; k < 9; k++) {
Equipment[k].material = emu->equipment[k].material; Equipment[k].Material = emu->equipment[k].Material;
Equipment[k].unknown1 = emu->equipment[k].unknown1; Equipment[k].Unknown1 = emu->equipment[k].Unknown1;
Equipment[k].elitematerial = emu->equipment[k].elitematerial; Equipment[k].EliteMaterial = emu->equipment[k].EliteMaterial;
Equipment[k].heroforgemodel = emu->equipment[k].heroforgemodel; Equipment[k].HeroForgeModel = emu->equipment[k].HeroForgeModel;
Equipment[k].material2 = emu->equipment[k].material2; Equipment[k].Material2 = emu->equipment[k].Material2;
} }
Buffer += (sizeof(structs::EquipStruct) * 9); Buffer += (sizeof(structs::EquipStruct) * 9);
@ -3983,13 +3988,13 @@ namespace RoF
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary].material); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary].Material);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary].material); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary].Material);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);

View File

@ -2118,7 +2118,7 @@ namespace RoF2
for (int r = 0; r < 7; r++) for (int r = 0; r < 7; r++)
{ {
outapp->WriteUInt32(emu->item_tint[r].color); outapp->WriteUInt32(emu->item_tint[r].Color);
} }
// Write zeroes for extra two tint values // Write zeroes for extra two tint values
outapp->WriteUInt32(0); outapp->WriteUInt32(0);
@ -2128,7 +2128,7 @@ namespace RoF2
for (int r = 0; r < 7; r++) for (int r = 0; r < 7; r++)
{ {
outapp->WriteUInt32(emu->item_tint[r].color); outapp->WriteUInt32(emu->item_tint[r].Color);
} }
// Write zeroes for extra two tint values // Write zeroes for extra two tint values
outapp->WriteUInt32(0); outapp->WriteUInt32(0);
@ -2360,58 +2360,49 @@ namespace RoF2
outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown
outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown
outapp->WriteUInt32(structs::MAX_PLAYER_BANDOLIER); outapp->WriteUInt32(consts::BANDOLIERS_SIZE);
for (uint32 r = 0; r < EmuConstants::BANDOLIERS_COUNT; r++) // Copy bandoliers where server and client indexes converge
{ for (uint32 r = 0; r < EmuConstants::BANDOLIERS_SIZE && r < consts::BANDOLIERS_SIZE; ++r) {
outapp->WriteString(emu->bandoliers[r].name); outapp->WriteString(emu->bandoliers[r].Name);
for (uint32 j = 0; j < consts::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true
for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j) outapp->WriteString(emu->bandoliers[r].Items[j].Name);
{ outapp->WriteUInt32(emu->bandoliers[r].Items[j].ID);
outapp->WriteString(emu->bandoliers[r].items[j].item_name); if (emu->bandoliers[r].Items[j].Icon) {
outapp->WriteUInt32(emu->bandoliers[r].items[j].item_id); outapp->WriteSInt32(emu->bandoliers[r].Items[j].Icon);
if (emu->bandoliers[r].items[j].icon) }
{ else {
outapp->WriteSInt32(emu->bandoliers[r].items[j].icon); // If no icon, it must send -1 or Treasure Chest Icon (836) is displayed
} outapp->WriteSInt32(-1);
else }
{ }
// If no icon, it must send -1 or Treasure Chest Icon (836) is displayed }
outapp->WriteSInt32(-1); // Nullify bandoliers where server and client indexes diverge, with a client bias
} for (uint32 r = EmuConstants::BANDOLIERS_SIZE; r < consts::BANDOLIERS_SIZE; ++r) {
} outapp->WriteString("");
} for (uint32 j = 0; j < consts::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true
outapp->WriteString("");
for (uint32 r = 0; r < structs::MAX_PLAYER_BANDOLIER - EmuConstants::BANDOLIERS_COUNT; r++) outapp->WriteUInt32(0);
{ outapp->WriteSInt32(-1);
outapp->WriteString(""); }
}
for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j)
{ outapp->WriteUInt32(consts::POTION_BELT_ITEM_COUNT);
outapp->WriteString("");
outapp->WriteUInt32(0); // Copy potion belt where server and client indexes converge
outapp->WriteSInt32(-1); for (uint32 r = 0; r < EmuConstants::POTION_BELT_ITEM_COUNT && r < consts::POTION_BELT_ITEM_COUNT; ++r) {
} outapp->WriteString(emu->potionbelt.Items[r].Name);
} outapp->WriteUInt32(emu->potionbelt.Items[r].ID);
if (emu->potionbelt.Items[r].Icon) {
outapp->WriteUInt32(structs::MAX_POTIONS_IN_BELT); outapp->WriteSInt32(emu->potionbelt.Items[r].Icon);
}
for (uint32 r = 0; r < EmuConstants::POTION_BELT_SIZE; r++) else {
{ // If no icon, it must send -1 or Treasure Chest Icon (836) is displayed
outapp->WriteString(emu->potionbelt.items[r].item_name); outapp->WriteSInt32(-1);
outapp->WriteUInt32(emu->potionbelt.items[r].item_id); }
if (emu->potionbelt.items[r].icon) }
{ // Nullify potion belt where server and client indexes diverge, with a client bias
outapp->WriteSInt32(emu->potionbelt.items[r].icon); for (uint32 r = EmuConstants::POTION_BELT_ITEM_COUNT; r < consts::POTION_BELT_ITEM_COUNT; ++r) {
}
else
{
outapp->WriteSInt32(-1);
}
}
for (uint32 r = 0; r < structs::MAX_POTIONS_IN_BELT - EmuConstants::POTION_BELT_SIZE; r++)
{
outapp->WriteString(""); outapp->WriteString("");
outapp->WriteUInt32(0); outapp->WriteUInt32(0);
outapp->WriteSInt32(-1); outapp->WriteSInt32(-1);
@ -2990,85 +2981,99 @@ namespace RoF2
ENCODE(OP_SendCharInfo) ENCODE(OP_SendCharInfo)
{ {
ENCODE_LENGTH_EXACT(CharacterSelect_Struct); ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_VAR_ENCODE(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct);
//EQApplicationPacket *packet = *p; // Zero-character count shunt
//const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; if (emu->CharCount == 0) {
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
eq->CharCount = emu->CharCount;
int char_count; FINISH_ENCODE();
int namelen = 0; return;
for (char_count = 0; char_count < 10; char_count++) {
if (emu->name[char_count][0] == '\0')
break;
if (strcmp(emu->name[char_count], "<none>") == 0)
break;
namelen += strlen(emu->name[char_count]);
} }
int total_length = sizeof(structs::CharacterSelect_Struct) unsigned char *emu_ptr = __emu_buffer;
+ char_count * sizeof(structs::CharacterSelectEntry_Struct) emu_ptr += sizeof(CharacterSelect_Struct);
+ namelen; CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr;
size_t names_length = 0;
size_t character_count = 0;
for (; character_count < emu->CharCount && character_count < consts::CHARACTER_CREATION_LIMIT; ++character_count) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
names_length += strlen(emu_cse->Name);
emu_ptr += sizeof(CharacterSelectEntry_Struct);
}
size_t total_length = sizeof(structs::CharacterSelect_Struct)
+ character_count * sizeof(structs::CharacterSelectEntry_Struct)
+ names_length;
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length);
structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr;
//unsigned char *eq_buffer = new unsigned char[total_length]; eq->CharCount = character_count;
//structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; //eq->TotalChars = emu->TotalChars;
eq->char_count = char_count; //if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
//eq->total_chars = 10; // eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
unsigned char *bufptr = (unsigned char *)eq->entries; emu_ptr = __emu_buffer;
int r; emu_ptr += sizeof(CharacterSelect_Struct);
for (r = 0; r < char_count; r++) {
{ //pre-name section... unsigned char *eq_ptr = __packet->pBuffer;
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; eq_ptr += sizeof(structs::CharacterSelect_Struct);
memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1);
for (int counter = 0; counter < character_count; ++counter) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr;
strcpy(eq_cse->Name, emu_cse->Name);
eq_ptr += strlen(eq_cse->Name);
eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr;
eq_cse->Class = emu_cse->Class;
eq_cse->Race = emu_cse->Race;
eq_cse->Level = emu_cse->Level;
eq_cse->ShroudClass = emu_cse->ShroudClass;
eq_cse->ShroudRace = emu_cse->ShroudRace;
eq_cse->Zone = emu_cse->Zone;
eq_cse->Instance = emu_cse->Instance;
eq_cse->Gender = emu_cse->Gender;
eq_cse->Face = emu_cse->Face;
for (int equip_index = 0; equip_index < _MaterialCount; equip_index++) {
eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material;
eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1;
eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteMaterial;
eq_cse->Equip[equip_index].HeroForgeModel = emu_cse->Equip[equip_index].HeroForgeModel;
eq_cse->Equip[equip_index].Material2 = emu_cse->Equip[equip_index].Material2;
eq_cse->Equip[equip_index].Color.Color = emu_cse->Equip[equip_index].Color.Color;
} }
//adjust for name.
bufptr += strlen(emu->name[r]); eq_cse->Unknown15 = emu_cse->Unknown15;
{ //post-name section... eq_cse->Unknown19 = emu_cse->Unknown19;
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo;
eq2->class_ = emu->class_[r]; eq_cse->DrakkinDetails = emu_cse->DrakkinDetails;
eq2->race = emu->race[r]; eq_cse->Deity = emu_cse->Deity;
eq2->level = emu->level[r]; eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile;
eq2->class_2 = emu->class_[r]; eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile;
eq2->race2 = emu->race[r]; eq_cse->HairColor = emu_cse->HairColor;
eq2->zone = emu->zone[r]; eq_cse->BeardColor = emu_cse->BeardColor;
eq2->instance = 0; eq_cse->EyeColor1 = emu_cse->EyeColor1;
eq2->gender = emu->gender[r]; eq_cse->EyeColor2 = emu_cse->EyeColor2;
eq2->face = emu->face[r]; eq_cse->HairStyle = emu_cse->HairStyle;
int k; eq_cse->Beard = emu_cse->Beard;
for (k = 0; k < _MaterialCount; k++) { eq_cse->Enabled = emu_cse->Enabled;
eq2->equip[k].material = emu->equip[r][k].material; eq_cse->Tutorial = emu_cse->Tutorial;
eq2->equip[k].unknown1 = emu->equip[r][k].unknown1; eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage;
eq2->equip[k].elitematerial = emu->equip[r][k].elitematerial; eq_cse->Unknown1 = emu_cse->Unknown1;
eq2->equip[k].heroforgemodel = emu->equip[r][k].heroforgemodel; eq_cse->GoHome = emu_cse->GoHome;
eq2->equip[k].material2 = emu->equip[r][k].material2; eq_cse->LastLogin = emu_cse->LastLogin;
eq2->equip[k].color.color = emu->equip[r][k].color.color; eq_cse->Unknown2 = emu_cse->Unknown2;
}
eq2->u15 = 0xff; emu_ptr += sizeof(CharacterSelectEntry_Struct);
eq2->u19 = 0xFF; eq_ptr += sizeof(structs::CharacterSelectEntry_Struct);
eq2->drakkin_tattoo = emu->drakkin_tattoo[r];
eq2->drakkin_details = emu->drakkin_details[r];
eq2->deity = emu->deity[r];
eq2->primary = emu->primary[r];
eq2->secondary = emu->secondary[r];
eq2->haircolor = emu->haircolor[r];
eq2->beardcolor = emu->beardcolor[r];
eq2->eyecolor1 = emu->eyecolor1[r];
eq2->eyecolor2 = emu->eyecolor2[r];
eq2->hairstyle = emu->hairstyle[r];
eq2->beard = emu->beard[r];
eq2->char_enabled = 1;
eq2->tutorial = emu->tutorial[r];
eq2->drakkin_heritage = emu->drakkin_heritage[r];
eq2->unknown1 = 0;
eq2->gohome = emu->gohome[r];
eq2->LastLogin = 1212696584;
eq2->unknown2 = 0;
}
bufptr += sizeof(structs::CharacterSelectEntry_Struct);
} }
FINISH_ENCODE(); FINISH_ENCODE();
@ -3772,7 +3777,7 @@ namespace RoF2
OUT(elite_material); OUT(elite_material);
OUT(hero_forge_model); OUT(hero_forge_model);
OUT(unknown18); OUT(unknown18);
OUT(color.color); OUT(color.Color);
OUT(wear_slot_id); OUT(wear_slot_id);
FINISH_ENCODE(); FINISH_ENCODE();
@ -4087,18 +4092,18 @@ namespace RoF2
for (k = 0; k < 9; ++k) for (k = 0; k < 9; ++k)
{ {
{ {
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].Color);
} }
} }
structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer;
for (k = 0; k < 9; k++) { for (k = 0; k < 9; k++) {
Equipment[k].material = emu->equipment[k].material; Equipment[k].Material = emu->equipment[k].Material;
Equipment[k].unknown1 = emu->equipment[k].unknown1; Equipment[k].Unknown1 = emu->equipment[k].Unknown1;
Equipment[k].elitematerial = emu->equipment[k].elitematerial; Equipment[k].EliteMaterial = emu->equipment[k].EliteMaterial;
Equipment[k].heroforgemodel = emu->equipment[k].heroforgemodel; Equipment[k].HeroForgeModel = emu->equipment[k].HeroForgeModel;
Equipment[k].material2 = emu->equipment[k].material2; Equipment[k].Material2 = emu->equipment[k].Material2;
} }
Buffer += (sizeof(structs::EquipStruct) * 9); Buffer += (sizeof(structs::EquipStruct) * 9);
@ -4111,13 +4116,13 @@ namespace RoF2
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary].material); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary].Material);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary].material); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary].Material);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);

View File

@ -103,6 +103,8 @@ namespace RoF2 {
} }
namespace consts { namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 12;
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 24; static const uint16 MAP_BANK_SIZE = 24;
static const uint16 MAP_SHARED_BANK_SIZE = 2; static const uint16 MAP_SHARED_BANK_SIZE = 2;
@ -178,9 +180,10 @@ namespace RoF2 {
static const uint16 ITEM_COMMON_SIZE = 6; static const uint16 ITEM_COMMON_SIZE = 6;
static const uint16 ITEM_CONTAINER_SIZE = 255; // 255; (server max will be 255..unsure what actual client is - test) static const uint16 ITEM_CONTAINER_SIZE = 255; // 255; (server max will be 255..unsure what actual client is - test)
static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const size_t BANDOLIERS_SIZE = 20; // number of bandolier instances
static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 5;
static const size_t POTION_BELT_ITEM_COUNT = 5;
static const size_t TEXT_LINK_BODY_LENGTH = 56; static const size_t TEXT_LINK_BODY_LENGTH = 56;
} }

View File

@ -97,11 +97,6 @@ static const uint32 MAX_PLAYER_TRIBUTES = 5;
static const uint32 MAX_TRIBUTE_TIERS = 10; static const uint32 MAX_TRIBUTE_TIERS = 10;
static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; static const uint32 TRIBUTE_NONE = 0xFFFFFFFF;
static const uint32 MAX_PLAYER_BANDOLIER = 20;
static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4;
static const uint32 MAX_POTIONS_IN_BELT = 5;
static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16;
static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16;
static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY);
@ -147,84 +142,87 @@ struct AdventureInfo {
*/ */
struct Color_Struct struct Color_Struct
{ {
union union {
{ struct {
struct uint8 Blue;
{ uint8 Green;
uint8 blue; uint8 Red;
uint8 green; uint8 UseTint; // if there's a tint this is FF
uint8 red; } RGB;
uint8 use_tint; // if there's a tint this is FF uint32 Color;
} rgb;
uint32 color;
}; };
}; };
/* struct CharSelectEquip
* Visible equiptment. {
* Size: 20 Octets uint32 Material;
*/ uint32 Unknown1;
struct EquipStruct { uint32 EliteMaterial;
/*00*/ uint32 material; uint32 HeroForgeModel;
/*04*/ uint32 unknown1; uint32 Material2;
/*08*/ uint32 elitematerial; Color_Struct Color;
/*12*/ uint32 heroforgemodel;
/*16*/ uint32 material2; // Same as material?
/*20*/
}; };
struct CharSelectEquip { struct CharacterSelectEntry_Struct
uint32 material; {
uint32 unknown1; /*0000*/ char Name[1]; // Name null terminated
uint32 elitematerial; /*0000*/ uint8 Class;
uint32 heroforgemodel; /*0000*/ uint32 Race;
uint32 material2; /*0000*/ uint8 Level;
Color_Struct color; /*0000*/ uint8 ShroudClass;
}; /*0000*/ uint32 ShroudRace;
/*0000*/ uint16 Zone;
struct CharacterSelectEntry_Struct { /*0000*/ uint16 Instance;
/*0000*/ char name[1]; // Name null terminated /*0000*/ uint8 Gender;
/*0000*/ uint8 class_; /*0000*/ uint8 Face;
/*0000*/ uint32 race; /*0000*/ CharSelectEquip Equip[9];
/*0000*/ uint8 level; /*0000*/ uint8 Unknown15; // Seen FF
/*0000*/ uint8 class_2; /*0000*/ uint8 Unknown19; // Seen FF
/*0000*/ uint32 race2; /*0000*/ uint32 DrakkinTattoo;
/*0000*/ uint16 zone; /*0000*/ uint32 DrakkinDetails;
/*0000*/ uint16 instance; /*0000*/ uint32 Deity;
/*0000*/ uint8 gender; /*0000*/ uint32 PrimaryIDFile;
/*0000*/ uint8 face; /*0000*/ uint32 SecondaryIDFile;
/*0000*/ CharSelectEquip equip[9]; /*0000*/ uint8 HairColor;
/*0000*/ uint8 u15; // Seen FF /*0000*/ uint8 BeardColor;
/*0000*/ uint8 u19; // Seen FF /*0000*/ uint8 EyeColor1;
/*0000*/ uint32 drakkin_tattoo; /*0000*/ uint8 EyeColor2;
/*0000*/ uint32 drakkin_details; /*0000*/ uint8 HairStyle;
/*0000*/ uint32 deity; /*0000*/ uint8 Beard;
/*0000*/ uint32 primary; /*0000*/ uint8 Enabled;
/*0000*/ uint32 secondary; /*0000*/ uint8 Tutorial; // Seen 1 for new char or 0 for existing
/*0000*/ uint8 haircolor; /*0000*/ uint32 DrakkinHeritage;
/*0000*/ uint8 beardcolor; /*0000*/ uint8 Unknown1; // Seen 0
/*0000*/ uint8 eyecolor1; /*0000*/ uint8 GoHome; // Seen 0 for new char and 1 for existing
/*0000*/ uint8 eyecolor2;
/*0000*/ uint8 hairstyle;
/*0000*/ uint8 beard;
/*0000*/ uint8 char_enabled;
/*0000*/ uint8 tutorial; // Seen 1 for new char or 0 for existing
/*0000*/ uint32 drakkin_heritage;
/*0000*/ uint8 unknown1; // Seen 0
/*0000*/ uint8 gohome; // Seen 0 for new char and 1 for existing
/*0000*/ uint32 LastLogin; /*0000*/ uint32 LastLogin;
/*0000*/ uint8 unknown2; // Seen 0 /*0000*/ uint8 Unknown2; // Seen 0
}; };
/* /*
** Character Selection Struct ** Character Selection Struct
** **
*/ */
struct CharacterSelect_Struct { struct CharacterSelect_Struct
/*000*/ uint32 char_count; //number of chars in this packet {
/*004*/ CharacterSelectEntry_Struct entries[0]; /*000*/ uint32 CharCount; //number of chars in this packet
/*004*/ CharacterSelectEntry_Struct Entries[0];
}; };
/*
* Visible equiptment.
* Size: 20 Octets
*/
struct EquipStruct
{
/*00*/ uint32 Material;
/*04*/ uint32 Unknown1;
/*08*/ uint32 EliteMaterial;
/*12*/ uint32 HeroForgeModel;
/*16*/ uint32 Material2; // Same as material?
/*20*/
};
struct Membership_Entry_Struct struct Membership_Entry_Struct
{ {
/*000*/ uint32 purchase_id; // Seen 1, then increments 90287 to 90300 /*000*/ uint32 purchase_id; // Seen 1, then increments 90287 to 90300
@ -896,38 +894,66 @@ struct Tribute_Struct {
uint32 tier; uint32 tier;
}; };
struct BandolierItem_Struct { // Bandolier item positions
char item_name[1]; // Variable Length enum
uint32 item_id; {
uint32 icon; bandolierPrimary = 0,
}; bandolierSecondary,
//len = 72
struct BandolierItem_Struct_Old {
uint32 item_id;
uint32 icon;
char item_name[64];
};
//len = 320
enum { //bandolier item positions
bandolierMainHand = 0,
bandolierOffHand,
bandolierRange, bandolierRange,
bandolierAmmo bandolierAmmo
}; };
struct Bandolier_Struct {
char name[1]; // Variable Length struct BandolierItem_Struct
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; {
char Name[1]; // Variable Length
uint32 ID;
uint32 Icon;
}; };
struct Bandolier_Struct_Old { //len = 72
char name[32]; struct BandolierItem_Struct_Old
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; {
uint32 ID;
uint32 Icon;
char Name[64];
}; };
struct PotionBelt_Struct { //len = 320
BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; struct Bandolier_Struct
{
char Name[1]; // Variable Length
BandolierItem_Struct Items[consts::BANDOLIER_ITEM_COUNT];
};
struct Bandolier_Struct_Old
{
char Name[32];
BandolierItem_Struct Items[consts::BANDOLIER_ITEM_COUNT];
};
struct PotionBeltItem_Struct
{
char Name[1]; // Variable Length
uint32 ID;
uint32 Icon;
};
//len = 72
struct PotionBeltItem_Struct_Old
{
uint32 ID;
uint32 Icon;
char Name[64];
};
struct PotionBelt_Struct
{
PotionBeltItem_Struct Items[consts::POTION_BELT_ITEM_COUNT];
};
struct PotionBelt_Struct_Old
{
PotionBeltItem_Struct_Old Items[consts::POTION_BELT_ITEM_COUNT];
}; };
struct GroupLeadershipAA_Struct { struct GroupLeadershipAA_Struct {
@ -1137,7 +1163,7 @@ union
/*12949*/ uint32 aapoints; // Unspent AA points - Seen 1 /*12949*/ uint32 aapoints; // Unspent AA points - Seen 1
/*12953*/ uint16 unknown_rof20; // /*12953*/ uint16 unknown_rof20; //
/*12955*/ uint32 bandolier_count; // Seen 20 /*12955*/ uint32 bandolier_count; // Seen 20
/*12959*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // [20] 740 bytes (Variable Name Sizes) - bandolier contents /*12959*/ Bandolier_Struct bandoliers[consts::BANDOLIERS_SIZE]; // [20] 740 bytes (Variable Name Sizes) - bandolier contents
/*13699*/ uint32 potionbelt_count; // Seen 5 /*13699*/ uint32 potionbelt_count; // Seen 5
/*13703*/ PotionBelt_Struct potionbelt; // [5] 45 bytes potion belt - (Variable Name Sizes) /*13703*/ PotionBelt_Struct potionbelt; // [5] 45 bytes potion belt - (Variable Name Sizes)
/*13748*/ int32 unknown_rof21; // Seen -1 /*13748*/ int32 unknown_rof21; // Seen -1
@ -4109,30 +4135,35 @@ struct DynamicWall_Struct {
/*80*/ /*80*/
}; };
enum { //bandolier actions // Bandolier actions
BandolierCreate = 0, enum
BandolierRemove = 1, {
BandolierSet = 2 bandolierCreate = 0,
bandolierRemove,
bandolierSet
}; };
struct BandolierCreate_Struct { struct BandolierCreate_Struct
/*00*/ uint32 action; //0 for create {
/*04*/ uint8 number; /*00*/ uint32 Action; //0 for create
/*05*/ char name[32]; /*04*/ uint8 Number;
/*37*/ uint16 unknown37; //seen 0x93FD /*05*/ char Name[32];
/*39*/ uint8 unknown39; //0 /*37*/ uint16 Unknown37; //seen 0x93FD
/*39*/ uint8 Unknown39; //0
}; };
struct BandolierDelete_Struct { struct BandolierDelete_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct BandolierSet_Struct { struct BandolierSet_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct Arrow_Struct { struct Arrow_Struct {

View File

@ -102,6 +102,8 @@ namespace RoF {
} }
namespace consts { namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 12;
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 24; static const uint16 MAP_BANK_SIZE = 24;
static const uint16 MAP_SHARED_BANK_SIZE = 2; static const uint16 MAP_SHARED_BANK_SIZE = 2;
@ -177,9 +179,10 @@ namespace RoF {
static const uint16 ITEM_COMMON_SIZE = 6; static const uint16 ITEM_COMMON_SIZE = 6;
static const uint16 ITEM_CONTAINER_SIZE = 255; // 255; (server max will be 255..unsure what actual client is - test) static const uint16 ITEM_CONTAINER_SIZE = 255; // 255; (server max will be 255..unsure what actual client is - test)
static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const size_t BANDOLIERS_SIZE = 20; // number of bandolier instances
static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 5;
static const size_t POTION_BELT_ITEM_COUNT = 5;
static const size_t TEXT_LINK_BODY_LENGTH = 55; static const size_t TEXT_LINK_BODY_LENGTH = 55;
} }

View File

@ -97,11 +97,6 @@ static const uint32 MAX_PLAYER_TRIBUTES = 5;
static const uint32 MAX_TRIBUTE_TIERS = 10; static const uint32 MAX_TRIBUTE_TIERS = 10;
static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; static const uint32 TRIBUTE_NONE = 0xFFFFFFFF;
static const uint32 MAX_PLAYER_BANDOLIER = 20;
static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4;
static const uint32 MAX_POTIONS_IN_BELT = 5;
static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16;
static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16;
static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY);
@ -147,71 +142,87 @@ struct AdventureInfo {
*/ */
struct Color_Struct struct Color_Struct
{ {
union union {
{ struct {
struct uint8 Blue;
{ uint8 Green;
uint8 blue; uint8 Red;
uint8 green; uint8 UseTint; // if there's a tint this is FF
uint8 red; } RGB;
uint8 use_tint; // if there's a tint this is FF uint32 Color;
} rgb;
uint32 color;
}; };
}; };
struct CharSelectEquip { struct CharSelectEquip
uint32 material; {
uint32 unknown1; uint32 Material;
uint32 elitematerial; uint32 Unknown1;
uint32 heroforgemodel; uint32 EliteMaterial;
uint32 material2; uint32 HeroForgeModel;
Color_Struct color; uint32 Material2;
Color_Struct Color;
}; };
struct CharacterSelectEntry_Struct { struct CharacterSelectEntry_Struct
/*0000*/ char name[1]; // Name null terminated {
/*0000*/ uint8 class_; /*0000*/ char Name[1]; // Name null terminated
/*0000*/ uint32 race; /*0000*/ uint8 Class;
/*0000*/ uint8 level; /*0000*/ uint32 Race;
/*0000*/ uint8 class_2; /*0000*/ uint8 Level;
/*0000*/ uint32 race2; /*0000*/ uint8 ShroudClass;
/*0000*/ uint16 zone; /*0000*/ uint32 ShroudRace;
/*0000*/ uint16 instance; /*0000*/ uint16 Zone;
/*0000*/ uint8 gender; /*0000*/ uint16 Instance;
/*0000*/ uint8 face; /*0000*/ uint8 Gender;
/*0000*/ CharSelectEquip equip[9]; /*0000*/ uint8 Face;
/*0000*/ uint8 u15; // Seen FF /*0000*/ CharSelectEquip Equip[9];
/*0000*/ uint8 u19; // Seen FF /*0000*/ uint8 Unknown15; // Seen FF
/*0000*/ uint32 drakkin_tattoo; /*0000*/ uint8 Unknown19; // Seen FF
/*0000*/ uint32 drakkin_details; /*0000*/ uint32 DrakkinTattoo;
/*0000*/ uint32 deity; /*0000*/ uint32 DrakkinDetails;
/*0000*/ uint32 primary; /*0000*/ uint32 Deity;
/*0000*/ uint32 secondary; /*0000*/ uint32 PrimaryIDFile;
/*0000*/ uint8 haircolor; /*0000*/ uint32 SecondaryIDFile;
/*0000*/ uint8 beardcolor; /*0000*/ uint8 HairColor;
/*0000*/ uint8 eyecolor1; /*0000*/ uint8 BeardColor;
/*0000*/ uint8 eyecolor2; /*0000*/ uint8 EyeColor1;
/*0000*/ uint8 hairstyle; /*0000*/ uint8 EyeColor2;
/*0000*/ uint8 beard; /*0000*/ uint8 HairStyle;
/*0000*/ uint8 char_enabled; /*0000*/ uint8 Beard;
/*0000*/ uint8 tutorial; // Seen 1 for new char or 0 for existing /*0000*/ uint8 Enabled;
/*0000*/ uint32 drakkin_heritage; /*0000*/ uint8 Tutorial; // Seen 1 for new char or 0 for existing
/*0000*/ uint8 unknown1; // Seen 0 /*0000*/ uint32 DrakkinHeritage;
/*0000*/ uint8 gohome; // Seen 0 for new char and 1 for existing /*0000*/ uint8 Unknown1; // Seen 0
/*0000*/ uint8 GoHome; // Seen 0 for new char and 1 for existing
/*0000*/ uint32 LastLogin; /*0000*/ uint32 LastLogin;
/*0000*/ uint8 unknown2; // Seen 0 /*0000*/ uint8 Unknown2; // Seen 0
}; };
/* /*
** Character Selection Struct ** Character Selection Struct
** **
*/ */
struct CharacterSelect_Struct { struct CharacterSelect_Struct
/*000*/ uint32 char_count; //number of chars in this packet {
/*004*/ CharacterSelectEntry_Struct entries[0]; /*000*/ uint32 CharCount; //number of chars in this packet
/*004*/ CharacterSelectEntry_Struct Entries[0];
}; };
/*
* Visible equiptment.
* Size: 20 Octets
*/
struct EquipStruct
{
/*00*/ uint32 Material;
/*04*/ uint32 Unknown1;
/*08*/ uint32 EliteMaterial;
/*12*/ uint32 HeroForgeModel;
/*16*/ uint32 Material2; // Same as material?
/*20*/
};
struct Membership_Entry_Struct struct Membership_Entry_Struct
{ {
/*000*/ uint32 purchase_id; // Seen 1, then increments 90287 to 90300 /*000*/ uint32 purchase_id; // Seen 1, then increments 90287 to 90300
@ -252,20 +263,6 @@ struct Membership_Struct
}; };
/*
* Visible equiptment.
* Size: 20 Octets
*/
struct EquipStruct {
/*00*/ uint32 material;
/*04*/ uint32 unknown1;
/*08*/ uint32 elitematerial;
/*12*/ uint32 heroforgemodel;
/*16*/ uint32 material2; // Same as material?
/*20*/
};
/* /*
** Generic Spawn Struct ** Generic Spawn Struct
** Length: 897 Octets ** Length: 897 Octets
@ -880,38 +877,66 @@ struct Tribute_Struct {
uint32 tier; uint32 tier;
}; };
struct BandolierItem_Struct { // Bandolier item positions
char item_name[1]; // Variable Length enum
uint32 item_id; {
uint32 icon; bandolierPrimary = 0,
}; bandolierSecondary,
//len = 72
struct BandolierItem_Struct_Old {
uint32 item_id;
uint32 icon;
char item_name[64];
};
//len = 320
enum { //bandolier item positions
bandolierMainHand = 0,
bandolierOffHand,
bandolierRange, bandolierRange,
bandolierAmmo bandolierAmmo
}; };
struct Bandolier_Struct {
char name[1]; // Variable Length struct BandolierItem_Struct
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; {
char Name[1]; // Variable Length
uint32 ID;
uint32 Icon;
}; };
struct Bandolier_Struct_Old { //len = 72
char name[32]; struct BandolierItem_Struct_Old
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; {
uint32 ID;
uint32 Icon;
char Name[64];
}; };
struct PotionBelt_Struct { //len = 320
BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; struct Bandolier_Struct
{
char Name[1]; // Variable Length
BandolierItem_Struct Items[consts::BANDOLIER_ITEM_COUNT];
};
struct Bandolier_Struct_Old
{
char Name[32];
BandolierItem_Struct Items[consts::BANDOLIER_ITEM_COUNT];
};
struct PotionBeltItem_Struct
{
char Name[1]; // Variable Length
uint32 ID;
uint32 Icon;
};
//len = 72
struct PotionBeltItem_Struct_Old
{
uint32 ID;
uint32 Icon;
char Name[64];
};
struct PotionBelt_Struct
{
PotionBeltItem_Struct Items[consts::POTION_BELT_ITEM_COUNT];
};
struct PotionBelt_Struct_Old
{
PotionBeltItem_Struct_Old Items[consts::POTION_BELT_ITEM_COUNT];
}; };
struct GroupLeadershipAA_Struct { struct GroupLeadershipAA_Struct {
@ -1121,7 +1146,7 @@ union
/*12949*/ uint32 aapoints; // Unspent AA points - Seen 1 /*12949*/ uint32 aapoints; // Unspent AA points - Seen 1
/*12953*/ uint16 unknown_rof20; // /*12953*/ uint16 unknown_rof20; //
/*12955*/ uint32 bandolier_count; // Seen 20 /*12955*/ uint32 bandolier_count; // Seen 20
/*12959*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // [20] 740 bytes (Variable Name Sizes) - bandolier contents /*12959*/ Bandolier_Struct bandoliers[consts::BANDOLIERS_SIZE]; // [20] 740 bytes (Variable Name Sizes) - bandolier contents
/*13699*/ uint32 potionbelt_count; // Seen 5 /*13699*/ uint32 potionbelt_count; // Seen 5
/*13703*/ PotionBelt_Struct potionbelt; // [5] 45 bytes potion belt - (Variable Name Sizes) /*13703*/ PotionBelt_Struct potionbelt; // [5] 45 bytes potion belt - (Variable Name Sizes)
/*13748*/ int32 unknown_rof21; // Seen -1 /*13748*/ int32 unknown_rof21; // Seen -1
@ -4113,30 +4138,35 @@ struct DynamicWall_Struct {
/*80*/ /*80*/
}; };
enum { //bandolier actions // Bandolier actions
BandolierCreate = 0, enum
BandolierRemove = 1, {
BandolierSet = 2 bandolierCreate = 0,
bandolierRemove,
bandolierSet
}; };
struct BandolierCreate_Struct { struct BandolierCreate_Struct
/*00*/ uint32 action; //0 for create {
/*04*/ uint8 number; /*00*/ uint32 Action; //0 for create
/*05*/ char name[32]; /*04*/ uint8 Number;
/*37*/ uint16 unknown37; //seen 0x93FD /*05*/ char Name[32];
/*39*/ uint8 unknown39; //0 /*37*/ uint16 Unknown37; //seen 0x93FD
/*39*/ uint8 Unknown39; //0
}; };
struct BandolierDelete_Struct { struct BandolierDelete_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct BandolierSet_Struct { struct BandolierSet_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct Arrow_Struct { struct Arrow_Struct {

View File

@ -1542,13 +1542,13 @@ namespace SoD
OUT(beard); OUT(beard);
// OUT(unknown00178[10]); // OUT(unknown00178[10]);
for (r = 0; r < 9; r++) { for (r = 0; r < 9; r++) {
eq->equipment[r].material = emu->item_material[r]; eq->equipment[r].Material = emu->item_material[r];
eq->equipment[r].unknown1 = 0; eq->equipment[r].Unknown1 = 0;
eq->equipment[r].elitematerial = 0; eq->equipment[r].EliteMaterial = 0;
//eq->colors[r].color = emu->colors[r].color; //eq->colors[r].color = emu->colors[r].color;
} }
for (r = 0; r < 7; r++) { for (r = 0; r < 7; r++) {
OUT(item_tint[r].color); OUT(item_tint[r].Color);
} }
// OUT(unknown00224[48]); // OUT(unknown00224[48]);
//NOTE: new client supports 300 AAs, our internal rep/PP //NOTE: new client supports 300 AAs, our internal rep/PP
@ -1606,26 +1606,46 @@ namespace SoD
OUT(endurance); OUT(endurance);
OUT(aapoints_spent); OUT(aapoints_spent);
OUT(aapoints); OUT(aapoints);
// OUT(unknown06160[4]); // OUT(unknown06160[4]);
//NOTE: new client supports 20 bandoliers, our internal rep
//only supports 4.. // Copy bandoliers where server and client indexes converge
for (r = 0; r < 4; r++) { for (r = 0; r < EmuConstants::BANDOLIERS_SIZE && r < consts::BANDOLIERS_SIZE; ++r) {
OUT_str(bandoliers[r].name); OUT_str(bandoliers[r].Name);
uint32 k; for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { OUT(bandoliers[r].Items[k].ID);
OUT(bandoliers[r].items[k].item_id); OUT(bandoliers[r].Items[k].Icon);
OUT(bandoliers[r].items[k].icon); OUT_str(bandoliers[r].Items[k].Name);
OUT_str(bandoliers[r].items[k].item_name);
} }
} }
// OUT(unknown07444[5120]); // Nullify bandoliers where server and client indexes diverge, with a client bias
for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { for (r = EmuConstants::BANDOLIERS_SIZE; r < consts::BANDOLIERS_SIZE; ++r) {
OUT(potionbelt.items[r].item_id); eq->bandoliers[r].Name[0] = '\0';
OUT(potionbelt.items[r].icon); for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
OUT_str(potionbelt.items[r].item_name); eq->bandoliers[r].Items[k].ID = 0;
eq->bandoliers[r].Items[k].Icon = 0;
eq->bandoliers[r].Items[k].Name[0] = '\0';
}
} }
// OUT(unknown07444[5120]);
// Copy potion belt where server and client indexes converge
for (r = 0; r < EmuConstants::POTION_BELT_ITEM_COUNT && r < consts::POTION_BELT_ITEM_COUNT; ++r) {
OUT(potionbelt.Items[r].ID);
OUT(potionbelt.Items[r].Icon);
OUT_str(potionbelt.Items[r].Name);
}
// Nullify potion belt where server and client indexes diverge, with a client bias
for (r = EmuConstants::POTION_BELT_ITEM_COUNT; r < consts::POTION_BELT_ITEM_COUNT; ++r) {
eq->potionbelt.Items[r].ID = 0;
eq->potionbelt.Items[r].Icon = 0;
eq->potionbelt.Items[r].Name[0] = '\0';
}
// OUT(unknown12852[8]); // OUT(unknown12852[8]);
// OUT(unknown12864[76]); // OUT(unknown12864[76]);
OUT_str(name); OUT_str(name);
OUT_str(last_name); OUT_str(last_name);
OUT(guild_id); OUT(guild_id);
@ -1891,76 +1911,95 @@ namespace SoD
ENCODE(OP_SendCharInfo) ENCODE(OP_SendCharInfo)
{ {
ENCODE_LENGTH_EXACT(CharacterSelect_Struct); ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_VAR_ENCODE(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct);
//EQApplicationPacket *packet = *p; // Zero-character count shunt
//const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; if (emu->CharCount == 0) {
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
eq->CharCount = emu->CharCount;
eq->TotalChars = eq->TotalChars;
int char_count; if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
int namelen = 0; eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
for (char_count = 0; char_count < 10; char_count++) {
if (emu->name[char_count][0] == '\0') FINISH_ENCODE();
break; return;
if (strcmp(emu->name[char_count], "<none>") == 0)
break;
namelen += strlen(emu->name[char_count]);
} }
int total_length = sizeof(structs::CharacterSelect_Struct) unsigned char *emu_ptr = __emu_buffer;
+ char_count * sizeof(structs::CharacterSelectEntry_Struct) emu_ptr += sizeof(CharacterSelect_Struct);
+ namelen; CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr;
size_t names_length = 0;
size_t character_count = 0;
for (; character_count < emu->CharCount && character_count < consts::CHARACTER_CREATION_LIMIT; ++character_count) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
names_length += strlen(emu_cse->Name);
emu_ptr += sizeof(CharacterSelectEntry_Struct);
}
size_t total_length = sizeof(structs::CharacterSelect_Struct)
+ character_count * sizeof(structs::CharacterSelectEntry_Struct)
+ names_length;
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length);
structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr;
//unsigned char *eq_buffer = new unsigned char[total_length]; eq->CharCount = character_count;
//structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; eq->TotalChars = emu->TotalChars;
eq->char_count = char_count; if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
eq->total_chars = 10; eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
unsigned char *bufptr = (unsigned char *)eq->entries; emu_ptr = __emu_buffer;
int r; emu_ptr += sizeof(CharacterSelect_Struct);
for (r = 0; r < char_count; r++) {
{ //pre-name section... unsigned char *eq_ptr = __packet->pBuffer;
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; eq_ptr += sizeof(structs::CharacterSelect_Struct);
eq2->level = emu->level[r];
eq2->hairstyle = emu->hairstyle[r]; for (int counter = 0; counter < character_count; ++counter) {
eq2->gender = emu->gender[r]; emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr;
eq_cse->Level = emu_cse->Level;
eq_cse->HairStyle = emu_cse->HairStyle;
eq_cse->Gender = emu_cse->Gender;
strcpy(eq_cse->Name, emu_cse->Name);
eq_ptr += strlen(eq_cse->Name);
eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr;
eq_cse->Beard = emu_cse->Beard;
eq_cse->HairColor = emu_cse->HairColor;
eq_cse->Face = emu_cse->Face;
for (int equip_index = 0; equip_index < _MaterialCount; equip_index++) {
eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material;
eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1;
eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteMaterial;
eq_cse->Equip[equip_index].Color.Color = emu_cse->Equip[equip_index].Color.Color;
} }
//adjust for name.
bufptr += strlen(emu->name[r]); eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile;
{ //post-name section... eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile;
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; eq_cse->Tutorial = emu_cse->Tutorial;
eq2->beard = emu->beard[r]; eq_cse->Unknown15 = emu_cse->Unknown15;
eq2->haircolor = emu->haircolor[r]; eq_cse->Deity = emu_cse->Deity;
eq2->face = emu->face[r]; eq_cse->Zone = emu_cse->Zone;
int k; eq_cse->Unknown19 = emu_cse->Unknown19;
for (k = 0; k < _MaterialCount; k++) { eq_cse->Race = emu_cse->Race;
eq2->equip[k].material = emu->equip[r][k].material; eq_cse->GoHome = emu_cse->GoHome;
eq2->equip[k].unknown1 = emu->equip[r][k].unknown1; eq_cse->Class = emu_cse->Class;
eq2->equip[k].elitematerial = emu->equip[r][k].elitematerial; eq_cse->EyeColor1 = emu_cse->EyeColor1;
eq2->equip[k].color.color = emu->equip[r][k].color.color; eq_cse->BeardColor = emu_cse->BeardColor;
} eq_cse->EyeColor2 = emu_cse->EyeColor2;
eq2->primary = emu->primary[r]; eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage;
eq2->secondary = emu->secondary[r]; eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo;
eq2->tutorial = emu->tutorial[r]; // was u15 eq_cse->DrakkinDetails = emu_cse->DrakkinDetails;
eq2->u15 = 0xff;
eq2->deity = emu->deity[r]; emu_ptr += sizeof(CharacterSelectEntry_Struct);
eq2->zone = emu->zone[r]; eq_ptr += sizeof(structs::CharacterSelectEntry_Struct);
eq2->u19 = 0xFF;
eq2->race = emu->race[r];
eq2->gohome = emu->gohome[r];
eq2->class_ = emu->class_[r];
eq2->eyecolor1 = emu->eyecolor1[r];
eq2->beardcolor = emu->beardcolor[r];
eq2->eyecolor2 = emu->eyecolor2[r];
eq2->drakkin_heritage = emu->drakkin_heritage[r];
eq2->drakkin_tattoo = emu->drakkin_tattoo[r];
eq2->drakkin_details = emu->drakkin_details[r];
}
bufptr += sizeof(structs::CharacterSelectEntry_Struct);
} }
FINISH_ENCODE(); FINISH_ENCODE();
@ -2355,7 +2394,7 @@ namespace SoD
OUT(material); OUT(material);
OUT(unknown06); OUT(unknown06);
OUT(elite_material); OUT(elite_material);
OUT(color.color); OUT(color.Color);
OUT(wear_slot_id); OUT(wear_slot_id);
FINISH_ENCODE(); FINISH_ENCODE();
@ -2723,7 +2762,7 @@ namespace SoD
for (k = 0; k < 9; ++k) for (k = 0; k < 9; ++k)
{ {
{ {
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].Color);
} }
} }
} }
@ -2733,11 +2772,11 @@ namespace SoD
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary].material); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary].Material);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary].material); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary].Material);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
} }
@ -2748,9 +2787,9 @@ namespace SoD
structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer;
for (k = 0; k < 9; k++) { for (k = 0; k < 9; k++) {
Equipment[k].material = emu->equipment[k].material; Equipment[k].Material = emu->equipment[k].Material;
Equipment[k].unknown1 = emu->equipment[k].unknown1; Equipment[k].Unknown1 = emu->equipment[k].Unknown1;
Equipment[k].elitematerial = emu->equipment[k].elitematerial; Equipment[k].EliteMaterial = emu->equipment[k].EliteMaterial;
} }
Buffer += (sizeof(structs::EquipStruct) * 9); Buffer += (sizeof(structs::EquipStruct) * 9);
@ -3465,7 +3504,7 @@ namespace SoD
IN(material); IN(material);
IN(unknown06); IN(unknown06);
IN(elite_material); IN(elite_material);
IN(color.color); IN(color.Color);
IN(wear_slot_id); IN(wear_slot_id);
emu->hero_forge_model = 0; emu->hero_forge_model = 0;
emu->unknown18 = 0; emu->unknown18 = 0;

View File

@ -101,6 +101,8 @@ namespace SoD {
} }
namespace consts { namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 12;
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 24; static const uint16 MAP_BANK_SIZE = 24;
static const uint16 MAP_SHARED_BANK_SIZE = 2; static const uint16 MAP_SHARED_BANK_SIZE = 2;
@ -174,9 +176,10 @@ namespace SoD {
static const uint16 ITEM_COMMON_SIZE = 5; static const uint16 ITEM_COMMON_SIZE = 5;
static const uint16 ITEM_CONTAINER_SIZE = 10; static const uint16 ITEM_CONTAINER_SIZE = 10;
static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const size_t BANDOLIERS_SIZE = 20; // number of bandolier instances
static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 5;
static const size_t POTION_BELT_ITEM_COUNT = 5;
static const size_t TEXT_LINK_BODY_LENGTH = 50; static const size_t TEXT_LINK_BODY_LENGTH = 50;
} }

View File

@ -103,54 +103,53 @@ struct AdventureInfo {
*/ */
struct Color_Struct struct Color_Struct
{ {
union union {
{ struct {
struct uint8 Blue;
{ uint8 Green;
uint8 blue; uint8 Red;
uint8 green; uint8 UseTint; // if there's a tint this is FF
uint8 red; } RGB;
uint8 use_tint; // if there's a tint this is FF uint32 Color;
} rgb;
uint32 color;
}; };
}; };
struct CharSelectEquip { struct CharSelectEquip
//totally guessed; {
uint32 material; uint32 Material;
uint32 unknown1; uint32 Unknown1;
uint32 elitematerial; uint32 EliteMaterial;
Color_Struct color; Color_Struct Color;
}; };
struct CharacterSelectEntry_Struct { struct CharacterSelectEntry_Struct
/*0000*/ uint8 level; // {
/*0000*/ uint8 hairstyle; // /*0000*/ uint8 Level; //
/*0002*/ uint8 gender; // /*0000*/ uint8 HairStyle; //
/*0003*/ char name[1]; //variable length, edi+0 /*0002*/ uint8 Gender; //
/*0000*/ uint8 beard; // /*0003*/ char Name[1]; // variable length, edi+0
/*0001*/ uint8 haircolor; // /*0000*/ uint8 Beard; //
/*0000*/ uint8 face; // /*0001*/ uint8 HairColor; //
/*0000*/ CharSelectEquip equip[9]; /*0000*/ uint8 Face; //
/*0000*/ uint32 primary; // /*0000*/ CharSelectEquip Equip[9];
/*0000*/ uint32 secondary; // /*0000*/ uint32 PrimaryIDFile; //
/*0000*/ uint8 u15; // 0xff /*0000*/ uint32 SecondaryIDFile; //
/*0000*/ uint32 deity; // /*0000*/ uint8 Unknown15; // 0xff
/*0000*/ uint16 zone; // /*0000*/ uint32 Deity; //
/*0000*/ uint16 instance; /*0000*/ uint16 Zone; //
/*0000*/ uint8 gohome; // /*0000*/ uint16 Instance;
/*0000*/ uint8 u19; // 0xff /*0000*/ uint8 GoHome; //
/*0000*/ uint32 race; // /*0000*/ uint8 Unknown19; // 0xff
/*0000*/ uint8 tutorial; // /*0000*/ uint32 Race; //
/*0000*/ uint8 class_; // /*0000*/ uint8 Tutorial; //
/*0000*/ uint8 eyecolor1; // /*0000*/ uint8 Class; //
/*0000*/ uint8 beardcolor; // /*0000*/ uint8 EyeColor1; //
/*0000*/ uint8 eyecolor2; // /*0000*/ uint8 BeardColor; //
/*0000*/ uint32 drakkin_heritage; // Drakkin Heritage /*0000*/ uint8 EyeColor2; //
/*0000*/ uint32 drakkin_tattoo; // Drakkin Tattoo /*0000*/ uint32 DrakkinHeritage; // Drakkin Heritage
/*0000*/ uint32 drakkin_details; // Drakkin Details (Facial Spikes) /*0000*/ uint32 DrakkinTattoo; // Drakkin Tattoo
/*0000*/ uint8 unknown; // New field to SoD /*0000*/ uint32 DrakkinDetails; // Drakkin Details (Facial Spikes)
/*0000*/ uint8 Unknown; // New field to SoD
}; };
@ -158,20 +157,22 @@ struct CharacterSelectEntry_Struct {
** Character Selection Struct ** Character Selection Struct
** **
*/ */
struct CharacterSelect_Struct { struct CharacterSelect_Struct
/*0000*/ uint32 char_count; //number of chars in this packet {
/*0004*/ uint32 total_chars; //total number of chars allowed? /*0000*/ uint32 CharCount; //number of chars in this packet
/*0008*/ CharacterSelectEntry_Struct entries[0]; /*0004*/ uint32 TotalChars; //total number of chars allowed?
/*0008*/ CharacterSelectEntry_Struct Entries[0];
}; };
/* /*
* Visible equiptment. * Visible equiptment.
* Size: 12 Octets * Size: 12 Octets
*/ */
struct EquipStruct { struct EquipStruct
/*00*/ uint32 material; {
/*04*/ uint32 unknown1; /*00*/ uint32 Material;
/*08*/ uint32 elitematerial; /*04*/ uint32 Unknown1;
/*08*/ uint32 EliteMaterial;
/*12*/ /*12*/
}; };
@ -676,9 +677,6 @@ struct Disciplines_Struct {
}; };
static const uint32 MAX_PLAYER_TRIBUTES = 5; static const uint32 MAX_PLAYER_TRIBUTES = 5;
static const uint32 MAX_PLAYER_BANDOLIER = 20;
static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4;
static const uint32 MAX_POTIONS_IN_BELT = 5;
static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; static const uint32 TRIBUTE_NONE = 0xFFFFFFFF;
struct Tribute_Struct { struct Tribute_Struct {
@ -686,26 +684,42 @@ struct Tribute_Struct {
uint32 tier; uint32 tier;
}; };
//len = 72 // Bandolier item positions
struct BandolierItem_Struct { enum
uint32 item_id; {
uint32 icon; bandolierPrimary = 0,
char item_name[64]; bandolierSecondary,
};
//len = 320
enum { //bandolier item positions
bandolierMainHand = 0,
bandolierOffHand,
bandolierRange, bandolierRange,
bandolierAmmo bandolierAmmo
}; };
struct Bandolier_Struct {
char name[32]; //len = 72
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; struct BandolierItem_Struct
{
uint32 ID;
uint32 Icon;
char Name[64];
}; };
struct PotionBelt_Struct {
BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; //len = 320
struct Bandolier_Struct
{
char Name[32];
BandolierItem_Struct Items[consts::BANDOLIER_ITEM_COUNT];
};
//len = 72
struct PotionBeltItem_Struct
{
uint32 ID;
uint32 Icon;
char Name[64];
};
//len = 288
struct PotionBelt_Struct
{
PotionBeltItem_Struct Items[consts::POTION_BELT_ITEM_COUNT];
}; };
static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16;
@ -925,7 +939,7 @@ struct PlayerProfile_Struct
/*08288*/ uint32 aapoints_spent; // Number of spent AA points /*08288*/ uint32 aapoints_spent; // Number of spent AA points
/*08292*/ uint32 aapoints; // Unspent AA points /*08292*/ uint32 aapoints; // Unspent AA points
/*08296*/ uint8 unknown06160[4]; /*08296*/ uint8 unknown06160[4];
/*08300*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // [6400] bandolier contents /*08300*/ Bandolier_Struct bandoliers[consts::BANDOLIERS_SIZE]; // [6400] bandolier contents
/*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot /*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot
/*15060*/ uint8 unknown12852[8]; /*15060*/ uint8 unknown12852[8];
/*15068*/ uint32 available_slots; /*15068*/ uint32 available_slots;
@ -3686,30 +3700,35 @@ struct DynamicWall_Struct {
/*80*/ /*80*/
}; };
enum { //bandolier actions // Bandolier actions
BandolierCreate = 0, enum
BandolierRemove = 1, {
BandolierSet = 2 bandolierCreate = 0,
bandolierRemove,
bandolierSet
}; };
struct BandolierCreate_Struct { struct BandolierCreate_Struct
/*00*/ uint32 action; //0 for create {
/*04*/ uint8 number; /*00*/ uint32 Action; //0 for create
/*05*/ char name[32]; /*04*/ uint8 Number;
/*37*/ uint16 unknown37; //seen 0x93FD /*05*/ char Name[32];
/*39*/ uint8 unknown39; //0 /*37*/ uint16 Unknown37; //seen 0x93FD
/*39*/ uint8 Unknown39; //0
}; };
struct BandolierDelete_Struct { struct BandolierDelete_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct BandolierSet_Struct { struct BandolierSet_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct Arrow_Struct { struct Arrow_Struct {

View File

@ -1200,13 +1200,13 @@ namespace SoF
OUT(beard); OUT(beard);
// OUT(unknown00178[10]); // OUT(unknown00178[10]);
for (r = 0; r < 9; r++) { for (r = 0; r < 9; r++) {
eq->equipment[r].material = emu->item_material[r]; eq->equipment[r].Material = emu->item_material[r];
eq->equipment[r].unknown1 = 0; eq->equipment[r].Unknown1 = 0;
eq->equipment[r].elitematerial = 0; eq->equipment[r].EliteMaterial = 0;
//eq->colors[r].color = emu->colors[r].color; //eq->colors[r].color = emu->colors[r].color;
} }
for (r = 0; r < 7; r++) { for (r = 0; r < 7; r++) {
OUT(item_tint[r].color); OUT(item_tint[r].Color);
} }
// OUT(unknown00224[48]); // OUT(unknown00224[48]);
//NOTE: new client supports 300 AAs, our internal rep/PP //NOTE: new client supports 300 AAs, our internal rep/PP
@ -1264,26 +1264,46 @@ namespace SoF
OUT(endurance); OUT(endurance);
OUT(aapoints_spent); OUT(aapoints_spent);
OUT(aapoints); OUT(aapoints);
// OUT(unknown06160[4]); // OUT(unknown06160[4]);
//NOTE: new client supports 20 bandoliers, our internal rep
//only supports 4.. // Copy bandoliers where server and client indexes converge
for (r = 0; r < 4; r++) { for (r = 0; r < EmuConstants::BANDOLIERS_SIZE && r < consts::BANDOLIERS_SIZE; ++r) {
OUT_str(bandoliers[r].name); OUT_str(bandoliers[r].Name);
uint32 k; for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { OUT(bandoliers[r].Items[k].ID);
OUT(bandoliers[r].items[k].item_id); OUT(bandoliers[r].Items[k].Icon);
OUT(bandoliers[r].items[k].icon); OUT_str(bandoliers[r].Items[k].Name);
OUT_str(bandoliers[r].items[k].item_name);
} }
} }
// OUT(unknown07444[5120]); // Nullify bandoliers where server and client indexes diverge, with a client bias
for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { for (r = EmuConstants::BANDOLIERS_SIZE; r < consts::BANDOLIERS_SIZE; ++r) {
OUT(potionbelt.items[r].item_id); eq->bandoliers[r].Name[0] = '\0';
OUT(potionbelt.items[r].icon); for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
OUT_str(potionbelt.items[r].item_name); eq->bandoliers[r].Items[k].ID = 0;
eq->bandoliers[r].Items[k].Icon = 0;
eq->bandoliers[r].Items[k].Name[0] = '\0';
}
} }
// OUT(unknown07444[5120]);
// Copy potion belt where server and client indexes converge
for (r = 0; r < EmuConstants::POTION_BELT_ITEM_COUNT && r < consts::POTION_BELT_ITEM_COUNT; ++r) {
OUT(potionbelt.Items[r].ID);
OUT(potionbelt.Items[r].Icon);
OUT_str(potionbelt.Items[r].Name);
}
// Nullify potion belt where server and client indexes diverge, with a client bias
for (r = EmuConstants::POTION_BELT_ITEM_COUNT; r < consts::POTION_BELT_ITEM_COUNT; ++r) {
eq->potionbelt.Items[r].ID = 0;
eq->potionbelt.Items[r].Icon = 0;
eq->potionbelt.Items[r].Name[0] = '\0';
}
// OUT(unknown12852[8]); // OUT(unknown12852[8]);
// OUT(unknown12864[76]); // OUT(unknown12864[76]);
OUT_str(name); OUT_str(name);
OUT_str(last_name); OUT_str(last_name);
OUT(guild_id); OUT(guild_id);
@ -1550,76 +1570,95 @@ namespace SoF
ENCODE(OP_SendCharInfo) ENCODE(OP_SendCharInfo)
{ {
ENCODE_LENGTH_EXACT(CharacterSelect_Struct); ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_VAR_ENCODE(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct);
//EQApplicationPacket *packet = *p; // Zero-character count shunt
//const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; if (emu->CharCount == 0) {
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
eq->CharCount = emu->CharCount;
eq->TotalChars = eq->TotalChars;
int char_count; if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
int namelen = 0; eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
for (char_count = 0; char_count < 10; char_count++) {
if (emu->name[char_count][0] == '\0') FINISH_ENCODE();
break; return;
if (strcmp(emu->name[char_count], "<none>") == 0)
break;
namelen += strlen(emu->name[char_count]);
} }
int total_length = sizeof(structs::CharacterSelect_Struct) unsigned char *emu_ptr = __emu_buffer;
+ char_count * sizeof(structs::CharacterSelectEntry_Struct) emu_ptr += sizeof(CharacterSelect_Struct);
+ namelen; CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr;
size_t names_length = 0;
size_t character_count = 0;
for (; character_count < emu->CharCount && character_count < consts::CHARACTER_CREATION_LIMIT; ++character_count) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
names_length += strlen(emu_cse->Name);
emu_ptr += sizeof(CharacterSelectEntry_Struct);
}
size_t total_length = sizeof(structs::CharacterSelect_Struct)
+ character_count * sizeof(structs::CharacterSelectEntry_Struct)
+ names_length;
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length);
structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr;
//unsigned char *eq_buffer = new unsigned char[total_length]; eq->CharCount = character_count;
//structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; eq->TotalChars = emu->TotalChars;
eq->char_count = char_count; if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
eq->total_chars = 10; eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
unsigned char *bufptr = (unsigned char *)eq->entries; emu_ptr = __emu_buffer;
int r; emu_ptr += sizeof(CharacterSelect_Struct);
for (r = 0; r < char_count; r++) {
{ //pre-name section... unsigned char *eq_ptr = __packet->pBuffer;
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; eq_ptr += sizeof(structs::CharacterSelect_Struct);
eq2->level = emu->level[r];
eq2->hairstyle = emu->hairstyle[r]; for (int counter = 0; counter < character_count; ++counter) {
eq2->gender = emu->gender[r]; emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr;
eq_cse->Level = emu_cse->Level;
eq_cse->HairStyle = emu_cse->HairStyle;
eq_cse->Gender = emu_cse->Gender;
strcpy(eq_cse->Name, emu_cse->Name);
eq_ptr += strlen(eq_cse->Name);
eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr;
eq_cse->Beard = emu_cse->Beard;
eq_cse->HairColor = emu_cse->HairColor;
eq_cse->Face = emu_cse->Face;
for (int equip_index = 0; equip_index < _MaterialCount; equip_index++) {
eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material;
eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1;
eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteMaterial;
eq_cse->Equip[equip_index].Color.Color = emu_cse->Equip[equip_index].Color.Color;
} }
//adjust for name.
bufptr += strlen(emu->name[r]); eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile;
{ //post-name section... eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile;
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; eq_cse->Tutorial = emu_cse->Tutorial;
eq2->beard = emu->beard[r]; eq_cse->Unknown15 = emu_cse->Unknown15;
eq2->haircolor = emu->haircolor[r]; eq_cse->Deity = emu_cse->Deity;
eq2->face = emu->face[r]; eq_cse->Zone = emu_cse->Zone;
int k; eq_cse->Unknown19 = emu_cse->Unknown19;
for (k = 0; k < _MaterialCount; k++) { eq_cse->Race = emu_cse->Race;
eq2->equip[k].material = emu->equip[r][k].material; eq_cse->GoHome = emu_cse->GoHome;
eq2->equip[k].unknown1 = emu->equip[r][k].unknown1; eq_cse->Class = emu_cse->Class;
eq2->equip[k].elitematerial = emu->equip[r][k].elitematerial; eq_cse->EyeColor1 = emu_cse->EyeColor1;
eq2->equip[k].color.color = emu->equip[r][k].color.color; eq_cse->BeardColor = emu_cse->BeardColor;
} eq_cse->EyeColor2 = emu_cse->EyeColor2;
eq2->primary = emu->primary[r]; eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage;
eq2->secondary = emu->secondary[r]; eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo;
eq2->tutorial = emu->tutorial[r]; // was u15 eq_cse->DrakkinDetails = emu_cse->DrakkinDetails;
eq2->u15 = 0xff;
eq2->deity = emu->deity[r]; emu_ptr += sizeof(CharacterSelectEntry_Struct);
eq2->zone = emu->zone[r]; eq_ptr += sizeof(structs::CharacterSelectEntry_Struct);
eq2->u19 = 0xFF;
eq2->race = emu->race[r];
eq2->gohome = emu->gohome[r];
eq2->class_ = emu->class_[r];
eq2->eyecolor1 = emu->eyecolor1[r];
eq2->beardcolor = emu->beardcolor[r];
eq2->eyecolor2 = emu->eyecolor2[r];
eq2->drakkin_heritage = emu->drakkin_heritage[r];
eq2->drakkin_tattoo = emu->drakkin_tattoo[r];
eq2->drakkin_details = emu->drakkin_details[r];
}
bufptr += sizeof(structs::CharacterSelectEntry_Struct);
} }
FINISH_ENCODE(); FINISH_ENCODE();
@ -1941,7 +1980,7 @@ namespace SoF
OUT(material); OUT(material);
OUT(unknown06); OUT(unknown06);
OUT(elite_material); OUT(elite_material);
OUT(color.color); OUT(color.Color);
OUT(wear_slot_id); OUT(wear_slot_id);
FINISH_ENCODE(); FINISH_ENCODE();
@ -2025,10 +2064,10 @@ namespace SoF
eq->drakkin_heritage = emu->drakkin_heritage; eq->drakkin_heritage = emu->drakkin_heritage;
eq->gender = emu->gender; eq->gender = emu->gender;
for (k = 0; k < 9; k++) { for (k = 0; k < 9; k++) {
eq->equipment[k].material = emu->equipment[k].material; eq->equipment[k].Material = emu->equipment[k].Material;
eq->equipment[k].unknown1 = emu->equipment[k].unknown1; eq->equipment[k].Unknown1 = emu->equipment[k].Unknown1;
eq->equipment[k].elitematerial = emu->equipment[k].elitematerial; eq->equipment[k].EliteMaterial = emu->equipment[k].EliteMaterial;
eq->colors[k].color = emu->colors[k].color; eq->colors[k].Color = emu->colors[k].Color;
} }
eq->StandState = emu->StandState; eq->StandState = emu->StandState;
eq->guildID = emu->guildID; eq->guildID = emu->guildID;
@ -2090,7 +2129,7 @@ namespace SoF
eq->petOwnerId = emu->petOwnerId; eq->petOwnerId = emu->petOwnerId;
eq->pvp = 0; // 0 = non-pvp colored name, 1 = red pvp name eq->pvp = 0; // 0 = non-pvp colored name, 1 = red pvp name
for (k = 0; k < 9; k++) { for (k = 0; k < 9; k++) {
eq->colors[k].color = emu->colors[k].color; eq->colors[k].Color = emu->colors[k].Color;
} }
eq->anon = emu->anon; eq->anon = emu->anon;
eq->face = emu->face; eq->face = emu->face;
@ -2789,7 +2828,7 @@ namespace SoF
IN(material); IN(material);
IN(unknown06); IN(unknown06);
IN(elite_material); IN(elite_material);
IN(color.color); IN(color.Color);
IN(wear_slot_id); IN(wear_slot_id);
emu->hero_forge_model = 0; emu->hero_forge_model = 0;
emu->unknown18 = 0; emu->unknown18 = 0;

View File

@ -101,6 +101,8 @@ namespace SoF {
} }
namespace consts { namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 12;
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 24; static const uint16 MAP_BANK_SIZE = 24;
static const uint16 MAP_SHARED_BANK_SIZE = 2; static const uint16 MAP_SHARED_BANK_SIZE = 2;
@ -174,9 +176,10 @@ namespace SoF {
static const uint16 ITEM_COMMON_SIZE = 5; static const uint16 ITEM_COMMON_SIZE = 5;
static const uint16 ITEM_CONTAINER_SIZE = 10; static const uint16 ITEM_CONTAINER_SIZE = 10;
static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const size_t BANDOLIERS_SIZE = 20; // number of bandolier instances
static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 5;
static const size_t POTION_BELT_ITEM_COUNT = 5;
static const size_t TEXT_LINK_BODY_LENGTH = 50; static const size_t TEXT_LINK_BODY_LENGTH = 50;
} }

View File

@ -103,72 +103,74 @@ struct AdventureInfo {
*/ */
struct Color_Struct struct Color_Struct
{ {
union union {
{ struct {
struct uint8 Blue;
{ uint8 Green;
uint8 blue; uint8 Red;
uint8 green; uint8 UseTint; // if there's a tint this is FF
uint8 red; } RGB;
uint8 use_tint; // if there's a tint this is FF uint32 Color;
} rgb;
uint32 color;
}; };
}; };
struct CharSelectEquip { struct CharSelectEquip
uint32 material; {
uint32 unknown1; uint32 Material;
uint32 elitematerial; uint32 Unknown1;
Color_Struct color; uint32 EliteMaterial;
Color_Struct Color;
}; };
struct CharacterSelectEntry_Struct { struct CharacterSelectEntry_Struct
/*0000*/ uint8 level; // {
/*0000*/ uint8 hairstyle; // /*0000*/ uint8 Level; //
/*0002*/ uint8 gender; // /*0000*/ uint8 HairStyle; //
/*0003*/ char name[1]; //variable length, edi+0 /*0002*/ uint8 Gender; //
/*0000*/ uint8 beard; // /*0003*/ char Name[1]; // variable length, edi+0
/*0001*/ uint8 haircolor; // /*0000*/ uint8 Beard; //
/*0000*/ uint8 face; // /*0001*/ uint8 HairColor; //
/*0000*/ CharSelectEquip equip[9]; /*0000*/ uint8 Face; //
/*0000*/ uint32 primary; // /*0000*/ CharSelectEquip Equip[9];
/*0000*/ uint32 secondary; // /*0000*/ uint32 PrimaryIDFile; //
/*0000*/ uint8 u15; // 0xff /*0000*/ uint32 SecondaryIDFile; //
/*0000*/ uint32 deity; // /*0000*/ uint8 Unknown15; // 0xff
/*0000*/ uint16 zone; // /*0000*/ uint32 Deity; //
/*0000*/ uint16 instance; /*0000*/ uint16 Zone; //
/*0000*/ uint8 gohome; // /*0000*/ uint16 Instance;
/*0000*/ uint8 u19; // 0xff /*0000*/ uint8 GoHome; //
/*0000*/ uint32 race; // /*0000*/ uint8 Unknown19; // 0xff
/*0000*/ uint8 tutorial; // /*0000*/ uint32 Race; //
/*0000*/ uint8 class_; // /*0000*/ uint8 Tutorial; //
/*0000*/ uint8 eyecolor1; // /*0000*/ uint8 Class; //
/*0000*/ uint8 beardcolor; // /*0000*/ uint8 EyeColor1; //
/*0000*/ uint8 eyecolor2; // /*0000*/ uint8 BeardColor; //
/*0000*/ uint32 drakkin_heritage; // Drakkin Heritage /*0000*/ uint8 EyeColor2; //
/*0000*/ uint32 drakkin_tattoo; // Drakkin Tattoo /*0000*/ uint32 DrakkinHeritage; // Drakkin Heritage
/*0000*/ uint32 drakkin_details; // Drakkin Details (Facial Spikes) /*0000*/ uint32 DrakkinTattoo; // Drakkin Tattoo
/*0000*/ uint32 DrakkinDetails; // Drakkin Details (Facial Spikes)
}; };
/* /*
** Character Selection Struct ** Character Selection Struct
** **
*/ */
struct CharacterSelect_Struct { struct CharacterSelect_Struct
/*0000*/ uint32 char_count; //number of chars in this packet {
/*0004*/ uint32 total_chars; //total number of chars allowed? /*0000*/ uint32 CharCount; //number of chars in this packet
/*0008*/ CharacterSelectEntry_Struct entries[0]; /*0004*/ uint32 TotalChars; //total number of chars allowed?
/*0008*/ CharacterSelectEntry_Struct Entries[0];
}; };
/* /*
* Visible equiptment. * Visible equiptment.
* Size: 12 Octets * Size: 12 Octets
*/ */
struct EquipStruct { struct EquipStruct
/*00*/ uint32 material; {
/*04*/ uint32 unknown1; /*00*/ uint32 Material;
/*08*/ uint32 elitematerial; /*04*/ uint32 Unknown1;
/*08*/ uint32 EliteMaterial;
/*12*/ /*12*/
}; };
@ -653,9 +655,6 @@ struct Disciplines_Struct {
}; };
static const uint32 MAX_PLAYER_TRIBUTES = 5; static const uint32 MAX_PLAYER_TRIBUTES = 5;
static const uint32 MAX_PLAYER_BANDOLIER = 20;
static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4;
static const uint32 MAX_POTIONS_IN_BELT = 5;
static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; static const uint32 TRIBUTE_NONE = 0xFFFFFFFF;
struct Tribute_Struct { struct Tribute_Struct {
@ -663,26 +662,42 @@ struct Tribute_Struct {
uint32 tier; uint32 tier;
}; };
//len = 72 // Bandolier item positions
struct BandolierItem_Struct { enum
uint32 item_id; {
uint32 icon; bandolierPrimary = 0,
char item_name[64]; bandolierSecondary,
};
//len = 320
enum { //bandolier item positions
bandolierMainHand = 0,
bandolierOffHand,
bandolierRange, bandolierRange,
bandolierAmmo bandolierAmmo
}; };
struct Bandolier_Struct {
char name[32]; //len = 72
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; struct BandolierItem_Struct
{
uint32 ID;
uint32 Icon;
char Name[64];
}; };
struct PotionBelt_Struct {
BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; //len = 320
struct Bandolier_Struct
{
char Name[32];
BandolierItem_Struct Items[consts::BANDOLIER_ITEM_COUNT];
};
//len = 72
struct PotionBeltItem_Struct
{
uint32 ID;
uint32 Icon;
char Name[64];
};
//len = 288
struct PotionBelt_Struct
{
PotionBeltItem_Struct Items[consts::POTION_BELT_ITEM_COUNT];
}; };
static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16;
@ -901,7 +916,7 @@ struct PlayerProfile_Struct //23576 Octets
/*08288*/ uint32 aapoints_spent; // Number of spent AA points /*08288*/ uint32 aapoints_spent; // Number of spent AA points
/*08292*/ uint32 aapoints; // Unspent AA points /*08292*/ uint32 aapoints; // Unspent AA points
/*08296*/ uint8 unknown06160[4]; /*08296*/ uint8 unknown06160[4];
/*08300*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // [6400] bandolier contents /*08300*/ Bandolier_Struct bandoliers[consts::BANDOLIERS_SIZE]; // [6400] bandolier contents
/*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot /*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot
/*15060*/ uint8 unknown12852[8]; /*15060*/ uint8 unknown12852[8];
/*15068*/ uint32 available_slots; /*15068*/ uint32 available_slots;
@ -3548,30 +3563,35 @@ struct DynamicWall_Struct {
/*80*/ /*80*/
}; };
enum { //bandolier actions // Bandolier actions
BandolierCreate = 0, enum
BandolierRemove = 1, {
BandolierSet = 2 bandolierCreate = 0,
bandolierRemove,
bandolierSet
}; };
struct BandolierCreate_Struct { struct BandolierCreate_Struct
/*00*/ uint32 action; //0 for create {
/*04*/ uint8 number; /*00*/ uint32 Action; //0 for create
/*05*/ char name[32]; /*04*/ uint8 Number;
/*37*/ uint16 unknown37; //seen 0x93FD /*05*/ char Name[32];
/*39*/ uint8 unknown39; //0 /*37*/ uint16 Unknown37; //seen 0x93FD
/*39*/ uint8 Unknown39; //0
}; };
struct BandolierDelete_Struct { struct BandolierDelete_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct BandolierSet_Struct { struct BandolierSet_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct Arrow_Struct { struct Arrow_Struct {

View File

@ -865,7 +865,7 @@ namespace Titanium
// OUT(unknown00178[10]); // OUT(unknown00178[10]);
for (r = 0; r < 9; r++) { for (r = 0; r < 9; r++) {
OUT(item_material[r]); OUT(item_material[r]);
OUT(item_tint[r].color); OUT(item_tint[r].Color);
} }
// OUT(unknown00224[48]); // OUT(unknown00224[48]);
for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) {
@ -922,24 +922,46 @@ namespace Titanium
OUT(endurance); OUT(endurance);
OUT(aapoints_spent); OUT(aapoints_spent);
OUT(aapoints); OUT(aapoints);
// OUT(unknown06160[4]); // OUT(unknown06160[4]);
for (r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) {
OUT_str(bandoliers[r].name); // Copy bandoliers where server and client indexes converge
uint32 k; for (r = 0; r < EmuConstants::BANDOLIERS_SIZE && r < consts::BANDOLIERS_SIZE; ++r) {
for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { OUT_str(bandoliers[r].Name);
OUT(bandoliers[r].items[k].item_id); for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
OUT(bandoliers[r].items[k].icon); OUT(bandoliers[r].Items[k].ID);
OUT_str(bandoliers[r].items[k].item_name); OUT(bandoliers[r].Items[k].Icon);
OUT_str(bandoliers[r].Items[k].Name);
} }
} }
// OUT(unknown07444[5120]); // Nullify bandoliers where server and client indexes diverge, with a client bias
for (r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) { for (r = EmuConstants::BANDOLIERS_SIZE; r < consts::BANDOLIERS_SIZE; ++r) {
OUT(potionbelt.items[r].item_id); eq->bandoliers[r].Name[0] = '\0';
OUT(potionbelt.items[r].icon); for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
OUT_str(potionbelt.items[r].item_name); eq->bandoliers[r].Items[k].ID = 0;
eq->bandoliers[r].Items[k].Icon = 0;
eq->bandoliers[r].Items[k].Name[0] = '\0';
}
} }
// OUT(unknown07444[5120]);
// Copy potion belt where server and client indexes converge
for (r = 0; r < EmuConstants::POTION_BELT_ITEM_COUNT && r < consts::POTION_BELT_ITEM_COUNT; ++r) {
OUT(potionbelt.Items[r].ID);
OUT(potionbelt.Items[r].Icon);
OUT_str(potionbelt.Items[r].Name);
}
// Nullify potion belt where server and client indexes diverge, with a client bias
for (r = EmuConstants::POTION_BELT_ITEM_COUNT; r < consts::POTION_BELT_ITEM_COUNT; ++r) {
eq->potionbelt.Items[r].ID = 0;
eq->potionbelt.Items[r].Icon = 0;
eq->potionbelt.Items[r].Name[0] = '\0';
}
// OUT(unknown12852[8]); // OUT(unknown12852[8]);
// OUT(unknown12864[76]); // OUT(unknown12864[76]);
OUT_str(name); OUT_str(name);
OUT_str(last_name); OUT_str(last_name);
OUT(guild_id); OUT(guild_id);
@ -1133,39 +1155,96 @@ namespace Titanium
ENCODE(OP_SendCharInfo) ENCODE(OP_SendCharInfo)
{ {
ENCODE_LENGTH_EXACT(CharacterSelect_Struct); ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct); SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct);
int r; unsigned char *emu_ptr = __emu_buffer;
for (r = 0; r < 10; r++) { emu_ptr += sizeof(CharacterSelect_Struct);
OUT(zone[r]); CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr;
OUT(eyecolor1[r]);
OUT(eyecolor2[r]); for (size_t index = 0; index < 10; ++index) {
OUT(hairstyle[r]); memset(eq->Name[index], 0, 64);
OUT(primary[r]); }
if (emu->race[r] > 473)
eq->race[r] = 1; // Non character-indexed packet fields
else eq->Unknown830[0] = 0;
eq->race[r] = emu->race[r]; eq->Unknown830[1] = 0;
OUT(class_[r]); eq->Unknown0962[0] = 0;
OUT_str(name[r]); eq->Unknown0962[1] = 0;
OUT(gender[r]);
OUT(level[r]); size_t char_index = 0;
OUT(secondary[r]); for (; char_index < emu->CharCount && char_index < 8; ++char_index) {
OUT(face[r]); emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
OUT(beard[r]);
int k; eq->Race[char_index] = emu_cse->Race;
for (k = 0; k < 9; k++) { if (eq->Race[char_index] > 473)
eq->equip[r][k] = emu->equip[r][k].material; eq->Race[char_index] = 1;
eq->cs_colors[r][k].color = emu->equip[r][k].color.color;
for (int index = 0; index < _MaterialCount; ++index) {
eq->CS_Colors[char_index][index].Color = emu_cse->Equip[index].Color.Color;
} }
OUT(haircolor[r]);
OUT(gohome[r]); eq->BeardColor[char_index] = emu_cse->BeardColor;
OUT(tutorial[r]); eq->HairStyle[char_index] = emu_cse->HairStyle;
OUT(deity[r]);
OUT(beardcolor[r]); for (int index = 0; index < _MaterialCount; ++index) {
eq->unknown820[r] = 0xFF; eq->Equip[char_index][index] = emu_cse->Equip[index].Material;
eq->unknown902[r] = 0xFF; }
eq->SecondaryIDFile[char_index] = emu_cse->SecondaryIDFile;
eq->Unknown820[char_index] = 0xFF;
eq->Deity[char_index] = emu_cse->Deity;
eq->GoHome[char_index] = emu_cse->GoHome;
eq->Tutorial[char_index] = emu_cse->Tutorial;
eq->Beard[char_index] = emu_cse->Beard;
eq->Unknown902[char_index] = 0xFF;
eq->PrimaryIDFile[char_index] = emu_cse->PrimaryIDFile;
eq->HairColor[char_index] = emu_cse->HairColor;
eq->Zone[char_index] = emu_cse->Zone;
eq->Class[char_index] = emu_cse->Class;
eq->Face[char_index] = emu_cse->Face;
memcpy(eq->Name[char_index], emu_cse->Name, 64);
eq->Gender[char_index] = emu_cse->Gender;
eq->EyeColor1[char_index] = emu_cse->EyeColor1;
eq->EyeColor2[char_index] = emu_cse->EyeColor2;
eq->Level[char_index] = emu_cse->Level;
emu_ptr += sizeof(CharacterSelectEntry_Struct);
}
for (; char_index < 10; ++char_index) {
eq->Race[char_index] = 0;
for (int index = 0; index < _MaterialCount; ++index) {
eq->CS_Colors[char_index][index].Color = 0;
}
eq->BeardColor[char_index] = 0;
eq->HairStyle[char_index] = 0;
for (int index = 0; index < _MaterialCount; ++index) {
eq->Equip[char_index][index] = 0;
}
eq->SecondaryIDFile[char_index] = 0;
eq->Unknown820[char_index] = 0xFF;
eq->Deity[char_index] = 0;
eq->GoHome[char_index] = 0;
eq->Tutorial[char_index] = 0;
eq->Beard[char_index] = 0;
eq->Unknown902[char_index] = 0xFF;
eq->PrimaryIDFile[char_index] = 0;
eq->HairColor[char_index] = 0;
eq->Zone[char_index] = 0;
eq->Class[char_index] = 0;
eq->Face[char_index] = 0;
//eq->Name[char_index][0] = '\0'; // Cleared above
eq->Gender[char_index] = 0;
eq->EyeColor1[char_index] = 0;
eq->EyeColor2[char_index] = 0;
eq->Level[char_index] = 0;
} }
FINISH_ENCODE(); FINISH_ENCODE();
@ -1383,7 +1462,7 @@ namespace Titanium
OUT(spawn_id); OUT(spawn_id);
OUT(material); OUT(material);
OUT(color.color); OUT(color.Color);
OUT(wear_slot_id); OUT(wear_slot_id);
FINISH_ENCODE(); FINISH_ENCODE();
@ -1475,8 +1554,8 @@ namespace Titanium
eq->guildrank = emu->guildrank; eq->guildrank = emu->guildrank;
// eq->unknown0194[3] = emu->unknown0194[3]; // eq->unknown0194[3] = emu->unknown0194[3];
for (k = 0; k < 9; k++) { for (k = 0; k < 9; k++) {
eq->equipment[k] = emu->equipment[k].material; eq->equipment[k] = emu->equipment[k].Material;
eq->colors[k].color = emu->colors[k].color; eq->colors[k].Color = emu->colors[k].Color;
} }
for (k = 0; k < 8; k++) { for (k = 0; k < 8; k++) {
eq->set_to_0xFF[k] = 0xFF; eq->set_to_0xFF[k] = 0xFF;
@ -1952,7 +2031,7 @@ namespace Titanium
IN(spawn_id); IN(spawn_id);
IN(material); IN(material);
IN(color.color); IN(color.Color);
IN(wear_slot_id); IN(wear_slot_id);
emu->unknown06 = 0; emu->unknown06 = 0;
emu->elite_material = 0; emu->elite_material = 0;

View File

@ -100,6 +100,8 @@ namespace Titanium {
} }
namespace consts { namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 8; // Hard-coded in client - DO NOT ALTER
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 16; static const uint16 MAP_BANK_SIZE = 16;
static const uint16 MAP_SHARED_BANK_SIZE = 2; static const uint16 MAP_SHARED_BANK_SIZE = 2;
@ -173,9 +175,10 @@ namespace Titanium {
static const uint16 ITEM_COMMON_SIZE = 5; static const uint16 ITEM_COMMON_SIZE = 5;
static const uint16 ITEM_CONTAINER_SIZE = 10; static const uint16 ITEM_CONTAINER_SIZE = 10;
static const uint32 BANDOLIERS_COUNT = 4; // count = number of bandolier instances static const size_t BANDOLIERS_SIZE = 4; // number of bandolier instances
static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 4;
static const size_t POTION_BELT_ITEM_COUNT = 4;
static const size_t TEXT_LINK_BODY_LENGTH = 45; static const size_t TEXT_LINK_BODY_LENGTH = 45;
} }

View File

@ -99,16 +99,14 @@ struct AdventureInfo {
*/ */
struct Color_Struct struct Color_Struct
{ {
union union {
{ struct {
struct uint8 Blue;
{ uint8 Green;
uint8 blue; uint8 Red;
uint8 green; uint8 UseTint; // if there's a tint this is FF
uint8 red; } RGB;
uint8 use_tint; // if there's a tint this is FF uint32 Color;
} rgb;
uint32 color;
}; };
}; };
@ -117,31 +115,32 @@ struct Color_Struct
** Length: 1704 Bytes ** Length: 1704 Bytes
** **
*/ */
struct CharacterSelect_Struct { struct CharacterSelect_Struct
/*0000*/ uint32 race[10]; // Characters Race {
/*0040*/ Color_Struct cs_colors[10][9]; // Characters Equipment Colors /*0000*/ uint32 Race[10]; // Characters Race
/*0400*/ uint8 beardcolor[10]; // Characters beard Color /*0040*/ Color_Struct CS_Colors[10][9]; // Characters Equipment Colors
/*0410*/ uint8 hairstyle[10]; // Characters hair style /*0400*/ uint8 BeardColor[10]; // Characters beard Color
/*0420*/ uint32 equip[10][9]; // 0=helm, 1=chest, 2=arm, 3=bracer, 4=hand, 5=leg, 6=boot, 7=melee1, 8=melee2 (Might not be) /*0410*/ uint8 HairStyle[10]; // Characters hair style
/*0780*/ uint32 secondary[10]; // Characters secondary IDFile number /*0420*/ uint32 Equip[10][9]; // 0=helm, 1=chest, 2=arm, 3=bracer, 4=hand, 5=leg, 6=boot, 7=melee1, 8=melee2 (Might not be)
/*0820*/ uint8 unknown820[10]; // 10x ff /*0780*/ uint32 SecondaryIDFile[10]; // Characters secondary IDFile number
/*0830*/ uint8 unknown830[2]; // 2x 00 /*0820*/ uint8 Unknown820[10]; // 10x ff
/*0832*/ uint32 deity[10]; // Characters Deity /*0830*/ uint8 Unknown830[2]; // 2x 00
/*0872*/ uint8 gohome[10]; // 1=Go Home available, 0=not /*0832*/ uint32 Deity[10]; // Characters Deity
/*0882*/ uint8 tutorial[10]; // 1=Tutorial available, 0=not /*0872*/ uint8 GoHome[10]; // 1=Go Home available, 0=not
/*0892*/ uint8 beard[10]; // Characters Beard Type /*0882*/ uint8 Tutorial[10]; // 1=Tutorial available, 0=not
/*0902*/ uint8 unknown902[10]; // 10x ff /*0892*/ uint8 Beard[10]; // Characters Beard Type
/*0912*/ uint32 primary[10]; // Characters primary IDFile number /*0902*/ uint8 Unknown902[10]; // 10x ff
/*0952*/ uint8 haircolor[10]; // Characters Hair Color /*0912*/ uint32 PrimaryIDFile[10]; // Characters primary IDFile number
/*0962*/ uint8 unknown0962[2]; // 2x 00 /*0952*/ uint8 HairColor[10]; // Characters Hair Color
/*0964*/ uint32 zone[10]; // Characters Current Zone /*0962*/ uint8 Unknown0962[2]; // 2x 00
/*1004*/ uint8 class_[10]; // Characters Classes /*0964*/ uint32 Zone[10]; // Characters Current Zone
/*1014*/ uint8 face[10]; // Characters Face Type /*1004*/ uint8 Class[10]; // Characters Classes
/*1024*/ char name[10][64]; // Characters Names /*1014*/ uint8 Face[10]; // Characters Face Type
/*1664*/ uint8 gender[10]; // Characters Gender /*1024*/ char Name[10][64]; // Characters Names
/*1674*/ uint8 eyecolor1[10]; // Characters Eye Color /*1664*/ uint8 Gender[10]; // Characters Gender
/*1684*/ uint8 eyecolor2[10]; // Characters Eye 2 Color /*1674*/ uint8 EyeColor1[10]; // Characters Eye Color
/*1694*/ uint8 level[10]; // Characters Levels /*1684*/ uint8 EyeColor2[10]; // Characters Eye 2 Color
/*1694*/ uint8 Level[10]; // Characters Levels
/*1704*/ /*1704*/
}; };
@ -586,34 +585,48 @@ struct Disciplines_Struct {
}; };
static const uint32 MAX_PLAYER_TRIBUTES = 5; static const uint32 MAX_PLAYER_TRIBUTES = 5;
static const uint32 MAX_PLAYER_BANDOLIER = 4;
static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4;
static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; static const uint32 TRIBUTE_NONE = 0xFFFFFFFF;
struct Tribute_Struct { struct Tribute_Struct {
uint32 tribute; uint32 tribute;
uint32 tier; uint32 tier;
}; };
//len = 72 // Bandolier item positions
struct BandolierItem_Struct { enum
uint32 item_id; {
uint32 icon; bandolierPrimary = 0,
char item_name[64]; bandolierSecondary,
};
//len = 320
enum { //bandolier item positions
bandolierMainHand = 0,
bandolierOffHand,
bandolierRange, bandolierRange,
bandolierAmmo bandolierAmmo
}; };
struct Bandolier_Struct {
char name[32]; //len = 72
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; struct BandolierItem_Struct
{
uint32 ID;
uint32 Icon;
char Name[64];
}; };
struct PotionBelt_Struct {
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; //len = 320
struct Bandolier_Struct
{
char Name[32];
BandolierItem_Struct Items[consts::BANDOLIER_ITEM_COUNT];
};
//len = 72
struct PotionBeltItem_Struct
{
uint32 ID;
uint32 Icon;
char Name[64];
};
//len = 288
struct PotionBelt_Struct
{
PotionBeltItem_Struct Items[consts::POTION_BELT_ITEM_COUNT];
}; };
static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16;
@ -817,7 +830,7 @@ struct PlayerProfile_Struct
/*06152*/ uint32 aapoints_spent; // Number of spent AA points /*06152*/ uint32 aapoints_spent; // Number of spent AA points
/*06156*/ uint32 aapoints; // Unspent AA points /*06156*/ uint32 aapoints; // Unspent AA points
/*06160*/ uint8 unknown06160[4]; /*06160*/ uint8 unknown06160[4];
/*06164*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // bandolier contents /*06164*/ Bandolier_Struct bandoliers[consts::BANDOLIERS_SIZE]; // bandolier contents
/*07444*/ uint8 unknown07444[5120]; /*07444*/ uint8 unknown07444[5120];
/*12564*/ PotionBelt_Struct potionbelt; // potion belt /*12564*/ PotionBelt_Struct potionbelt; // potion belt
/*12852*/ uint8 unknown12852[8]; /*12852*/ uint8 unknown12852[8];
@ -3030,30 +3043,35 @@ struct DynamicWall_Struct {
/*80*/ /*80*/
}; };
enum { //bandolier actions // Bandolier actions
BandolierCreate = 0, enum
BandolierRemove = 1, {
BandolierSet = 2 bandolierCreate = 0,
bandolierRemove,
bandolierSet
}; };
struct BandolierCreate_Struct { struct BandolierCreate_Struct
/*00*/ uint32 action; //0 for create {
/*04*/ uint8 number; /*00*/ uint32 Action; //0 for create
/*05*/ char name[32]; /*04*/ uint8 Number;
/*37*/ uint16 unknown37; //seen 0x93FD /*05*/ char Name[32];
/*39*/ uint8 unknown39; //0 /*37*/ uint16 Unknown37; //seen 0x93FD
/*39*/ uint8 Unknown39; //0
}; };
struct BandolierDelete_Struct { struct BandolierDelete_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct BandolierSet_Struct { struct BandolierSet_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct Arrow_Struct { struct Arrow_Struct {

View File

@ -1791,13 +1791,13 @@ namespace UF
OUT(beard); OUT(beard);
// OUT(unknown00178[10]); // OUT(unknown00178[10]);
for (r = 0; r < 9; r++) { for (r = 0; r < 9; r++) {
eq->equipment[r].material = emu->item_material[r]; eq->equipment[r].Material = emu->item_material[r];
eq->equipment[r].unknown1 = 0; eq->equipment[r].Unknown1 = 0;
eq->equipment[r].elitematerial = 0; eq->equipment[r].EliteMaterial = 0;
//eq->colors[r].color = emu->colors[r].color; //eq->colors[r].color = emu->colors[r].color;
} }
for (r = 0; r < 7; r++) { for (r = 0; r < 7; r++) {
OUT(item_tint[r].color); OUT(item_tint[r].Color);
} }
// OUT(unknown00224[48]); // OUT(unknown00224[48]);
//NOTE: new client supports 300 AAs, our internal rep/PP //NOTE: new client supports 300 AAs, our internal rep/PP
@ -1868,26 +1868,46 @@ namespace UF
OUT(endurance); OUT(endurance);
OUT(aapoints_spent); OUT(aapoints_spent);
OUT(aapoints); OUT(aapoints);
// OUT(unknown06160[4]); // OUT(unknown06160[4]);
//NOTE: new client supports 20 bandoliers, our internal rep
//only supports 4.. // Copy bandoliers where server and client indexes converge
for (r = 0; r < 4; r++) { for (r = 0; r < EmuConstants::BANDOLIERS_SIZE && r < consts::BANDOLIERS_SIZE; ++r) {
OUT_str(bandoliers[r].name); OUT_str(bandoliers[r].Name);
uint32 k; for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { OUT(bandoliers[r].Items[k].ID);
OUT(bandoliers[r].items[k].item_id); OUT(bandoliers[r].Items[k].Icon);
OUT(bandoliers[r].items[k].icon); OUT_str(bandoliers[r].Items[k].Name);
OUT_str(bandoliers[r].items[k].item_name);
} }
} }
// OUT(unknown07444[5120]); // Nullify bandoliers where server and client indexes diverge, with a client bias
for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { for (r = EmuConstants::BANDOLIERS_SIZE; r < consts::BANDOLIERS_SIZE; ++r) {
OUT(potionbelt.items[r].item_id); eq->bandoliers[r].Name[0] = '\0';
OUT(potionbelt.items[r].icon); for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
OUT_str(potionbelt.items[r].item_name); eq->bandoliers[r].Items[k].ID = 0;
eq->bandoliers[r].Items[k].Icon = 0;
eq->bandoliers[r].Items[k].Name[0] = '\0';
}
} }
// OUT(unknown07444[5120]);
// Copy potion belt where server and client indexes converge
for (r = 0; r < EmuConstants::POTION_BELT_ITEM_COUNT && r < consts::POTION_BELT_ITEM_COUNT; ++r) {
OUT(potionbelt.Items[r].ID);
OUT(potionbelt.Items[r].Icon);
OUT_str(potionbelt.Items[r].Name);
}
// Nullify potion belt where server and client indexes diverge, with a client bias
for (r = EmuConstants::POTION_BELT_ITEM_COUNT; r < consts::POTION_BELT_ITEM_COUNT; ++r) {
eq->potionbelt.Items[r].ID = 0;
eq->potionbelt.Items[r].Icon = 0;
eq->potionbelt.Items[r].Name[0] = '\0';
}
// OUT(unknown12852[8]); // OUT(unknown12852[8]);
// OUT(unknown12864[76]); // OUT(unknown12864[76]);
OUT_str(name); OUT_str(name);
OUT_str(last_name); OUT_str(last_name);
OUT(guild_id); OUT(guild_id);
@ -2174,77 +2194,103 @@ namespace UF
ENCODE(OP_SendCharInfo) ENCODE(OP_SendCharInfo)
{ {
ENCODE_LENGTH_EXACT(CharacterSelect_Struct); ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_VAR_ENCODE(CharacterSelect_Struct); SETUP_VAR_ENCODE(CharacterSelect_Struct);
//EQApplicationPacket *packet = *p; // Zero-character count shunt
//const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; if (emu->CharCount == 0) {
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
eq->CharCount = emu->CharCount;
eq->TotalChars = eq->TotalChars;
int char_count; if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
int namelen = 0; eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
for (char_count = 0; char_count < 10; char_count++) {
if (emu->name[char_count][0] == '\0') // Special Underfoot adjustment - field should really be 'AdditionalChars' or 'BonusChars'
break; uint32 adjusted_total = eq->TotalChars - 8; // Yes, it rolls under for '< 8' - probably an int32 field
if (strcmp(emu->name[char_count], "<none>") == 0) eq->TotalChars = adjusted_total;
break;
namelen += strlen(emu->name[char_count]); FINISH_ENCODE();
return;
}
unsigned char *emu_ptr = __emu_buffer;
emu_ptr += sizeof(CharacterSelect_Struct);
CharacterSelectEntry_Struct *emu_cse = (CharacterSelectEntry_Struct *)nullptr;
size_t names_length = 0;
size_t character_count = 0;
for (; character_count < emu->CharCount && character_count < consts::CHARACTER_CREATION_LIMIT; ++character_count) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
names_length += strlen(emu_cse->Name);
emu_ptr += sizeof(CharacterSelectEntry_Struct);
} }
int total_length = sizeof(structs::CharacterSelect_Struct) size_t total_length = sizeof(structs::CharacterSelect_Struct)
+ char_count * sizeof(structs::CharacterSelectEntry_Struct) + character_count * sizeof(structs::CharacterSelectEntry_Struct)
+ namelen; + names_length;
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length);
structs::CharacterSelectEntry_Struct *eq_cse = (structs::CharacterSelectEntry_Struct *)nullptr;
//unsigned char *eq_buffer = new unsigned char[total_length]; eq->CharCount = character_count;
//structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; eq->TotalChars = emu->TotalChars;
eq->char_count = char_count; if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
eq->total_chars = 10; eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
unsigned char *bufptr = (unsigned char *)eq->entries; // Special Underfoot adjustment - field should really be 'AdditionalChars' or 'BonusChars' in this client
int r; uint32 adjusted_total = eq->TotalChars - 8; // Yes, it rolls under for '< 8' - probably an int32 field
for (r = 0; r < char_count; r++) { eq->TotalChars = adjusted_total;
{ //pre-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; emu_ptr = __emu_buffer;
eq2->level = emu->level[r]; emu_ptr += sizeof(CharacterSelect_Struct);
eq2->hairstyle = emu->hairstyle[r];
eq2->gender = emu->gender[r]; unsigned char *eq_ptr = __packet->pBuffer;
memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); eq_ptr += sizeof(structs::CharacterSelect_Struct);
}
//adjust for name. for (int counter = 0; counter < character_count; ++counter) {
bufptr += strlen(emu->name[r]); emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
{ //post-name section... eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr;
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
eq2->beard = emu->beard[r]; eq_cse->Level = emu_cse->Level;
eq2->haircolor = emu->haircolor[r]; eq_cse->HairStyle = emu_cse->HairStyle;
eq2->face = emu->face[r]; eq_cse->Gender = emu_cse->Gender;
int k;
for (k = 0; k < _MaterialCount; k++) { strcpy(eq_cse->Name, emu_cse->Name);
eq2->equip[k].material = emu->equip[r][k].material; eq_ptr += strlen(eq_cse->Name);
eq2->equip[k].unknown1 = emu->equip[r][k].unknown1; eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr;
eq2->equip[k].elitematerial = emu->equip[r][k].elitematerial;
eq2->equip[k].color.color = emu->equip[r][k].color.color; eq_cse->Beard = emu_cse->Beard;
} eq_cse->HairColor = emu_cse->HairColor;
eq2->primary = emu->primary[r]; eq_cse->Face = emu_cse->Face;
eq2->secondary = emu->secondary[r];
eq2->tutorial = emu->tutorial[r]; // was u15 for (int equip_index = 0; equip_index < _MaterialCount; equip_index++) {
eq2->u15 = 0xff; eq_cse->Equip[equip_index].Material = emu_cse->Equip[equip_index].Material;
eq2->deity = emu->deity[r]; eq_cse->Equip[equip_index].Unknown1 = emu_cse->Equip[equip_index].Unknown1;
eq2->zone = emu->zone[r]; eq_cse->Equip[equip_index].EliteMaterial = emu_cse->Equip[equip_index].EliteMaterial;
eq2->u19 = 0xFF; eq_cse->Equip[equip_index].Color.Color = emu_cse->Equip[equip_index].Color.Color;
eq2->race = emu->race[r];
eq2->gohome = emu->gohome[r];
eq2->class_ = emu->class_[r];
eq2->eyecolor1 = emu->eyecolor1[r];
eq2->beardcolor = emu->beardcolor[r];
eq2->eyecolor2 = emu->eyecolor2[r];
eq2->drakkin_heritage = emu->drakkin_heritage[r];
eq2->drakkin_tattoo = emu->drakkin_tattoo[r];
eq2->drakkin_details = emu->drakkin_details[r];
} }
bufptr += sizeof(structs::CharacterSelectEntry_Struct); eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile;
eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile;
eq_cse->Tutorial = emu_cse->Tutorial;
eq_cse->Unknown15 = emu_cse->Unknown15;
eq_cse->Deity = emu_cse->Deity;
eq_cse->Zone = emu_cse->Zone;
eq_cse->Unknown19 = emu_cse->Unknown19;
eq_cse->Race = emu_cse->Race;
eq_cse->GoHome = emu_cse->GoHome;
eq_cse->Class = emu_cse->Class;
eq_cse->EyeColor1 = emu_cse->EyeColor1;
eq_cse->BeardColor = emu_cse->BeardColor;
eq_cse->EyeColor2 = emu_cse->EyeColor2;
eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage;
eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo;
eq_cse->DrakkinDetails = emu_cse->DrakkinDetails;
emu_ptr += sizeof(CharacterSelectEntry_Struct);
eq_ptr += sizeof(structs::CharacterSelectEntry_Struct);
} }
FINISH_ENCODE(); FINISH_ENCODE();
@ -2621,7 +2667,7 @@ namespace UF
OUT(material); OUT(material);
OUT(unknown06); OUT(unknown06);
OUT(elite_material); OUT(elite_material);
OUT(color.color); OUT(color.Color);
OUT(wear_slot_id); OUT(wear_slot_id);
FINISH_ENCODE(); FINISH_ENCODE();
@ -2983,7 +3029,7 @@ namespace UF
for (k = 0; k < 9; ++k) for (k = 0; k < 9; ++k)
{ {
{ {
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].Color);
} }
} }
} }
@ -2993,11 +3039,11 @@ namespace UF
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary].material); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary].Material);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary].material); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary].Material);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
} }
@ -3007,9 +3053,9 @@ namespace UF
structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer;
for (k = 0; k < 9; k++) { for (k = 0; k < 9; k++) {
Equipment[k].material = emu->equipment[k].material; Equipment[k].Material = emu->equipment[k].Material;
Equipment[k].unknown1 = emu->equipment[k].unknown1; Equipment[k].Unknown1 = emu->equipment[k].Unknown1;
Equipment[k].elitematerial = emu->equipment[k].elitematerial; Equipment[k].EliteMaterial = emu->equipment[k].EliteMaterial;
} }
Buffer += (sizeof(structs::EquipStruct) * 9); Buffer += (sizeof(structs::EquipStruct) * 9);
@ -3709,7 +3755,7 @@ namespace UF
IN(material); IN(material);
IN(unknown06); IN(unknown06);
IN(elite_material); IN(elite_material);
IN(color.color); IN(color.Color);
IN(wear_slot_id); IN(wear_slot_id);
emu->hero_forge_model = 0; emu->hero_forge_model = 0;
emu->unknown18 = 0; emu->unknown18 = 0;

View File

@ -101,6 +101,8 @@ namespace UF {
} }
namespace consts { namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 12;
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount; static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 24; static const uint16 MAP_BANK_SIZE = 24;
static const uint16 MAP_SHARED_BANK_SIZE = 2; static const uint16 MAP_SHARED_BANK_SIZE = 2;
@ -174,9 +176,10 @@ namespace UF {
static const uint16 ITEM_COMMON_SIZE = 5; static const uint16 ITEM_COMMON_SIZE = 5;
static const uint16 ITEM_CONTAINER_SIZE = 10; static const uint16 ITEM_CONTAINER_SIZE = 10;
static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances static const size_t BANDOLIERS_SIZE = 20; // number of bandolier instances
static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 5;
static const size_t POTION_BELT_ITEM_COUNT = 5;
static const size_t TEXT_LINK_BODY_LENGTH = 50; static const size_t TEXT_LINK_BODY_LENGTH = 50;
} }

View File

@ -103,53 +103,53 @@ struct AdventureInfo {
*/ */
struct Color_Struct struct Color_Struct
{ {
union union {
{ struct {
struct uint8 blue;
{ uint8 Green;
uint8 blue; uint8 Red;
uint8 green; uint8 UseTint; // if there's a tint this is FF
uint8 red; } RGB;
uint8 use_tint; // if there's a tint this is FF uint32 Color;
} rgb;
uint32 color;
}; };
}; };
struct CharSelectEquip { struct CharSelectEquip
uint32 material; {
uint32 unknown1; uint32 Material;
uint32 elitematerial; uint32 Unknown1;
Color_Struct color; uint32 EliteMaterial;
Color_Struct Color;
}; };
struct CharacterSelectEntry_Struct { struct CharacterSelectEntry_Struct
/*0000*/ uint8 level; // {
/*0000*/ uint8 hairstyle; // /*0000*/ uint8 Level; //
/*0002*/ uint8 gender; // /*0000*/ uint8 HairStyle; //
/*0003*/ char name[1]; //variable length, edi+0 /*0002*/ uint8 Gender; //
/*0000*/ uint8 beard; // /*0003*/ char Name[1]; // variable length, edi+0
/*0001*/ uint8 haircolor; // /*0000*/ uint8 Beard; //
/*0000*/ uint8 face; // /*0001*/ uint8 HairColor; //
/*0000*/ CharSelectEquip equip[9]; /*0000*/ uint8 Face; //
/*0000*/ uint32 primary; // /*0000*/ CharSelectEquip Equip[9];
/*0000*/ uint32 secondary; // /*0000*/ uint32 PrimaryIDFile; //
/*0000*/ uint8 u15; // 0xff /*0000*/ uint32 SecondaryIDFile; //
/*0000*/ uint32 deity; // /*0000*/ uint8 Unknown15; // 0xff
/*0000*/ uint16 zone; // /*0000*/ uint32 Deity; //
/*0000*/ uint16 instance; /*0000*/ uint16 Zone; //
/*0000*/ uint8 gohome; // /*0000*/ uint16 Instance;
/*0000*/ uint8 u19; // 0xff /*0000*/ uint8 GoHome; //
/*0000*/ uint32 race; // /*0000*/ uint8 Unknown19; // 0xff
/*0000*/ uint8 tutorial; // /*0000*/ uint32 Race; //
/*0000*/ uint8 class_; // /*0000*/ uint8 Tutorial; //
/*0000*/ uint8 eyecolor1; // /*0000*/ uint8 Class; //
/*0000*/ uint8 beardcolor; // /*0000*/ uint8 EyeColor1; //
/*0000*/ uint8 eyecolor2; // /*0000*/ uint8 BeardColor; //
/*0000*/ uint32 drakkin_heritage; // Drakkin Heritage /*0000*/ uint8 EyeColor2; //
/*0000*/ uint32 drakkin_tattoo; // Drakkin Tattoo /*0000*/ uint32 DrakkinHeritage; // Drakkin Heritage
/*0000*/ uint32 drakkin_details; // Drakkin Details (Facial Spikes) /*0000*/ uint32 DrakkinTattoo; // Drakkin Tattoo
/*0000*/ uint8 unknown; // New field to Underfoot /*0000*/ uint32 DrakkinDetails; // Drakkin Details (Facial Spikes)
/*0000*/ uint8 Unknown; // New field to Underfoot
}; };
@ -157,20 +157,22 @@ struct CharacterSelectEntry_Struct {
** Character Selection Struct ** Character Selection Struct
** **
*/ */
struct CharacterSelect_Struct { struct CharacterSelect_Struct
/*0000*/ uint32 char_count; //number of chars in this packet {
/*0004*/ uint32 total_chars; //total number of chars allowed? /*0000*/ uint32 CharCount; //number of chars in this packet
/*0008*/ CharacterSelectEntry_Struct entries[0]; /*0004*/ uint32 TotalChars; //total number of chars allowed?
/*0008*/ CharacterSelectEntry_Struct Entries[0];
}; };
/* /*
* Visible equiptment. * Visible equiptment.
* Size: 12 Octets * Size: 12 Octets
*/ */
struct EquipStruct { struct EquipStruct
/*00*/ uint32 material; {
/*04*/ uint32 unknown1; /*00*/ uint32 Material;
/*08*/ uint32 elitematerial; /*04*/ uint32 Unknown1;
/*08*/ uint32 EliteMaterial;
/*12*/ /*12*/
}; };
@ -722,9 +724,6 @@ struct Disciplines_Struct {
}; };
static const uint32 MAX_PLAYER_TRIBUTES = 5; static const uint32 MAX_PLAYER_TRIBUTES = 5;
static const uint32 MAX_PLAYER_BANDOLIER = 20;
static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4;
static const uint32 MAX_POTIONS_IN_BELT = 5;
static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; static const uint32 TRIBUTE_NONE = 0xFFFFFFFF;
struct Tribute_Struct { struct Tribute_Struct {
@ -732,26 +731,42 @@ struct Tribute_Struct {
uint32 tier; uint32 tier;
}; };
//len = 72 // Bandolier item positions
struct BandolierItem_Struct { enum
uint32 item_id; {
uint32 icon; bandolierPrimary = 0,
char item_name[64]; bandolierSecondary,
};
//len = 320
enum { //bandolier item positions
bandolierMainHand = 0,
bandolierOffHand,
bandolierRange, bandolierRange,
bandolierAmmo bandolierAmmo
}; };
struct Bandolier_Struct {
char name[32]; //len = 72
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; struct BandolierItem_Struct
{
uint32 ID;
uint32 Icon;
char Name[64];
}; };
struct PotionBelt_Struct {
BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; //len = 320
struct Bandolier_Struct
{
char Name[32];
BandolierItem_Struct Items[consts::BANDOLIER_ITEM_COUNT];
};
//len = 72
struct PotionBeltItem_Struct
{
uint32 ID;
uint32 Icon;
char Name[64];
};
//len = 288
struct PotionBelt_Struct
{
PotionBeltItem_Struct Items[consts::POTION_BELT_ITEM_COUNT];
}; };
static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16;
@ -974,7 +989,7 @@ struct PlayerProfile_Struct
/*11236*/ uint32 aapoints_spent; // Number of spent AA points /*11236*/ uint32 aapoints_spent; // Number of spent AA points
/*11240*/ uint32 aapoints; // Unspent AA points /*11240*/ uint32 aapoints; // Unspent AA points
/*11244*/ uint8 unknown11244[4]; /*11244*/ uint8 unknown11244[4];
/*11248*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // [6400] bandolier contents /*11248*/ Bandolier_Struct bandoliers[consts::BANDOLIERS_SIZE]; // [6400] bandolier contents
/*17648*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot /*17648*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot
/*18008*/ uint8 unknown18008[8]; /*18008*/ uint8 unknown18008[8];
/*18016*/ uint32 available_slots; /*18016*/ uint32 available_slots;
@ -3758,30 +3773,35 @@ struct DynamicWall_Struct {
/*80*/ /*80*/
}; };
enum { //bandolier actions // Bandolier actions
BandolierCreate = 0, enum
BandolierRemove = 1, {
BandolierSet = 2 bandolierCreate = 0,
bandolierRemove,
bandolierSet
}; };
struct BandolierCreate_Struct { struct BandolierCreate_Struct
/*00*/ uint32 action; //0 for create {
/*04*/ uint8 number; /*00*/ uint32 Action; //0 for create
/*05*/ char name[32]; /*04*/ uint8 Number;
/*37*/ uint16 unknown37; //seen 0x93FD /*05*/ char Name[32];
/*39*/ uint8 unknown39; //0 /*37*/ uint16 Unknown37; //seen 0x93FD
/*39*/ uint8 Unknown39; //0
}; };
struct BandolierDelete_Struct { struct BandolierDelete_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
struct BandolierSet_Struct { struct BandolierSet_Struct
/*00*/ uint32 action; {
/*04*/ uint8 number; /*00*/ uint32 Action;
/*05*/ uint8 unknown05[35]; /*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
}; };
// Not 100% sure on this struct. Live as of 1/1/11 is different than UF. Seems to work 'OK' // Not 100% sure on this struct. Live as of 1/1/11 is different than UF. Seems to work 'OK'

View File

@ -16,6 +16,7 @@
#include "../common/string_util.h" #include "../common/string_util.h"
#include "../common/clientversions.h" #include "../common/clientversions.h"
#include "../common/random.h" #include "../common/random.h"
#include "../common/shareddb.h"
#include "client.h" #include "client.h"
#include "worlddb.h" #include "worlddb.h"
@ -86,11 +87,11 @@ Client::Client(EQStreamInterface* ieqs)
charid = 0; charid = 0;
pwaitingforbootup = 0; pwaitingforbootup = 0;
StartInTutorial = false; StartInTutorial = false;
ClientVersionBit = 0;
numclients++;
if (eqs->GetClientVersion() != ClientVersion::Unknown) m_ClientVersion = eqs->GetClientVersion();
ClientVersionBit = 1 << (static_cast<unsigned int>(eqs->GetClientVersion()) - 1); m_ClientVersionBit = ClientBitFromVersion(m_ClientVersion);
numclients++;
} }
Client::~Client() { Client::~Client() {
@ -161,32 +162,34 @@ void Client::SendCharInfo() {
cle->SetOnline(CLE_Status_CharSelect); cle->SetOnline(CLE_Status_CharSelect);
} }
if (ClientVersionBit & BIT_RoFAndLater) if (m_ClientVersionBit & BIT_RoFAndLater) {
{ SendMaxCharCreate();
// Can make max char per account into a rule - New to VoA
SendMaxCharCreate(10);
SendMembership(); SendMembership();
SendMembershipSettings(); SendMembershipSettings();
} }
seencharsel = true; seencharsel = true;
// Send OP_SendCharInfo // Send OP_SendCharInfo
auto outapp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); EQApplicationPacket *outapp = nullptr;
CharacterSelect_Struct* cs = (CharacterSelect_Struct*)outapp->pBuffer; database.GetCharSelectInfo(GetAccountID(), &outapp, m_ClientVersionBit);
database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit); if (outapp) {
QueuePacket(outapp);
QueuePacket(outapp); }
else {
Log.Out(Logs::General, Logs::World_Server, "[Error] Database did not return an OP_SendCharInfo packet for account %u", GetAccountID());
}
safe_delete(outapp); safe_delete(outapp);
} }
void Client::SendMaxCharCreate(int max_chars) { void Client::SendMaxCharCreate() {
auto outapp = new EQApplicationPacket(OP_SendMaxCharacters, sizeof(MaxCharacters_Struct)); auto outapp = new EQApplicationPacket(OP_SendMaxCharacters, sizeof(MaxCharacters_Struct));
MaxCharacters_Struct* mc = (MaxCharacters_Struct*)outapp->pBuffer; MaxCharacters_Struct* mc = (MaxCharacters_Struct*)outapp->pBuffer;
mc->max_chars = max_chars; mc->max_chars = EQLimits::CharacterCreationLimit(m_ClientVersion);
if (mc->max_chars > EmuConstants::CHARACTER_CREATION_LIMIT)
mc->max_chars = EmuConstants::CHARACTER_CREATION_LIMIT;
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
@ -671,7 +674,7 @@ bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) {
} }
else else
{ {
if(ClientVersionBit & BIT_TitaniumAndEarlier) if (m_ClientVersionBit & BIT_TitaniumAndEarlier)
StartInTutorial = true; StartInTutorial = true;
SendCharInfo(); SendCharInfo();
} }
@ -716,66 +719,71 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
return true; return true;
} }
if(!pZoning && ew->return_home && !ew->tutorial) { // This can probably be moved outside and have another method return requested info (don't forget to remove the #include "../common/shareddb.h" above)
auto cs = new CharacterSelect_Struct; if (!pZoning) {
memset(cs, 0, sizeof(CharacterSelect_Struct)); size_t character_limit = EQLimits::CharacterCreationLimit(eqs->GetClientVersion());
database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit); if (character_limit > EmuConstants::CHARACTER_CREATION_LIMIT) { character_limit = EmuConstants::CHARACTER_CREATION_LIMIT; }
bool home_enabled = false; if (eqs->GetClientVersion() == ClientVersion::Titanium) { character_limit = 8; }
for(int x = 0; x < 10; ++x) std::string tgh_query = StringFormat(
{ "SELECT "
if(strcasecmp(cs->name[x], char_name) == 0) "`id`, "
{ "name, "
if(cs->gohome[x] == 1) "`level`, "
{ "last_login, "
home_enabled = true; "FROM "
break; "character_data "
"WHERE `account_id` = %i ORDER BY `name` LIMIT %u", GetAccountID(), character_limit);
auto tgh_results = database.QueryDatabase(tgh_query);
/* Check GoHome */
if (ew->return_home && !ew->tutorial) {
bool home_enabled = false;
for (auto row = tgh_results.begin(); row != tgh_results.end(); ++row) {
if (strcasecmp(row[1], char_name) == 0) {
if (RuleB(World, EnableTutorialButton) && ((uint8)atoi(row[2]) <= RuleI(World, MaxLevelForTutorial))) {
home_enabled = true;
break;
}
} }
} }
}
safe_delete(cs);
if(home_enabled) { if (home_enabled) {
zoneID = database.MoveCharacterToBind(charid,4); zoneID = database.MoveCharacterToBind(charid, 4);
} }
else { else {
Log.Out(Logs::Detail, Logs::World_Server,"'%s' is trying to go home before they're able...",char_name); Log.Out(Logs::Detail, Logs::World_Server, "'%s' is trying to go home before they're able...", char_name);
database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able."); database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able.");
eqs->Close(); eqs->Close();
return true; return true;
}
}
if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) {
auto cs = new CharacterSelect_Struct;
memset(cs, 0, sizeof(CharacterSelect_Struct));
database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit);
bool tutorial_enabled = false;
for(int x = 0; x < 10; ++x)
{
if(strcasecmp(cs->name[x], char_name) == 0)
{
if(cs->tutorial[x] == 1)
{
tutorial_enabled = true;
break;
}
} }
} }
safe_delete(cs);
if(tutorial_enabled) /* Check Tutorial*/
{ if (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial)) {
zoneID = RuleI(World, TutorialZoneID); bool tutorial_enabled = false;
database.MoveCharacterToZone(charid, database.GetZoneName(zoneID)); for (auto row = tgh_results.begin(); row != tgh_results.end(); ++row) {
} if (strcasecmp(row[1], char_name) == 0) {
else if (RuleB(World, EnableReturnHomeButton)) {
{ int now = time(nullptr);
Log.Out(Logs::Detail, Logs::World_Server,"'%s' is trying to go to tutorial but are not allowed...",char_name); if ((now - atoi(row[3])) >= RuleI(World, MinOfflineTimeToReturnHome)) {
database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character."); tutorial_enabled = true;
eqs->Close(); break;
return true; }
}
}
}
if (tutorial_enabled) {
zoneID = RuleI(World, TutorialZoneID);
database.MoveCharacterToZone(charid, database.GetZoneName(zoneID));
}
else {
Log.Out(Logs::Detail, Logs::World_Server, "'%s' is trying to go to tutorial but are not allowed...", char_name);
database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character.");
eqs->Close();
return true;
}
} }
} }
@ -846,9 +854,9 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
char ConnectionType; char ConnectionType;
if(ClientVersionBit & BIT_UFAndLater) if (m_ClientVersionBit & BIT_UFAndLater)
ConnectionType = 'U'; ConnectionType = 'U';
else if(ClientVersionBit & BIT_SoFAndLater) else if (m_ClientVersionBit & BIT_SoFAndLater)
ConnectionType = 'S'; ConnectionType = 'S';
else else
ConnectionType = 'C'; ConnectionType = 'C';
@ -872,7 +880,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
outapp2 = new EQApplicationPacket(OP_SetChatServer2); outapp2 = new EQApplicationPacket(OP_SetChatServer2);
if(ClientVersionBit & BIT_TitaniumAndEarlier) if (m_ClientVersionBit & BIT_TitaniumAndEarlier)
ConnectionType = 'M'; ConnectionType = 'M';
sprintf(buffer,"%s,%i,%s.%s,%c%08X", sprintf(buffer,"%s,%i,%s.%s,%c%08X",
@ -906,7 +914,7 @@ bool Client::HandleDeleteCharacterPacket(const EQApplicationPacket *app) {
bool Client::HandleZoneChangePacket(const EQApplicationPacket *app) { bool Client::HandleZoneChangePacket(const EQApplicationPacket *app) {
// HoT sends this to world while zoning and wants it echoed back. // HoT sends this to world while zoning and wants it echoed back.
if(ClientVersionBit & BIT_RoFAndLater) if (m_ClientVersionBit & BIT_RoFAndLater)
{ {
QueuePacket(app); QueuePacket(app);
} }
@ -1370,7 +1378,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
Log.Out(Logs::Detail, Logs::World_Server, "Beard: %d Beardcolor: %d", cc->beard, cc->beardcolor); Log.Out(Logs::Detail, Logs::World_Server, "Beard: %d Beardcolor: %d", cc->beard, cc->beardcolor);
/* Validate the char creation struct */ /* Validate the char creation struct */
if (ClientVersionBit & BIT_SoFAndLater) { if (m_ClientVersionBit & BIT_SoFAndLater) {
if (!CheckCharCreateInfoSoF(cc)) { if (!CheckCharCreateInfoSoF(cc)) {
Log.Out(Logs::Detail, Logs::World_Server,"CheckCharCreateInfo did not validate the request (bad race/class/stats)"); Log.Out(Logs::Detail, Logs::World_Server,"CheckCharCreateInfo did not validate the request (bad race/class/stats)");
return false; return false;
@ -1438,7 +1446,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
pp.pvp = database.GetServerType() == 1 ? 1 : 0; pp.pvp = database.GetServerType() == 1 ? 1 : 0;
/* If it is an SoF Client and the SoF Start Zone rule is set, send new chars there */ /* If it is an SoF Client and the SoF Start Zone rule is set, send new chars there */
if (ClientVersionBit & BIT_SoFAndLater) { if (m_ClientVersionBit & BIT_SoFAndLater) {
Log.Out(Logs::Detail, Logs::World_Server,"Found 'SoFStartZoneID' rule setting: %i", RuleI(World, SoFStartZoneID)); Log.Out(Logs::Detail, Logs::World_Server,"Found 'SoFStartZoneID' rule setting: %i", RuleI(World, SoFStartZoneID));
if (RuleI(World, SoFStartZoneID) > 0) { if (RuleI(World, SoFStartZoneID) > 0) {
pp.zone_id = RuleI(World, SoFStartZoneID); pp.zone_id = RuleI(World, SoFStartZoneID);
@ -1454,7 +1462,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
} }
} }
/* use normal starting zone logic to either get defaults, or if startzone was set, load that from the db table.*/ /* use normal starting zone logic to either get defaults, or if startzone was set, load that from the db table.*/
bool ValidStartZone = database.GetStartZone(&pp, cc, ClientVersionBit & BIT_TitaniumAndEarlier); bool ValidStartZone = database.GetStartZone(&pp, cc, m_ClientVersionBit & BIT_TitaniumAndEarlier);
if (!ValidStartZone){ if (!ValidStartZone){
return false; return false;

View File

@ -41,7 +41,7 @@ public:
bool Process(); bool Process();
void ReceiveData(uchar* buf, int len); void ReceiveData(uchar* buf, int len);
void SendCharInfo(); void SendCharInfo();
void SendMaxCharCreate(int max_chars); void SendMaxCharCreate();
void SendMembership(); void SendMembership();
void SendMembershipSettings(); void SendMembershipSettings();
void EnterWorld(bool TryBootup = true); void EnterWorld(bool TryBootup = true);
@ -84,7 +84,8 @@ private:
uint32 pwaitingforbootup; uint32 pwaitingforbootup;
bool StartInTutorial; bool StartInTutorial;
uint32 ClientVersionBit; ClientVersion m_ClientVersion;
uint32 m_ClientVersionBit;
bool OPCharCreate(char *name, CharCreate_Struct *cc); bool OPCharCreate(char *name, CharCreate_Struct *cc);
void SetClassStartingSkills( PlayerProfile_Struct *pp ); void SetClassStartingSkills( PlayerProfile_Struct *pp );

View File

@ -33,20 +33,20 @@ extern std::vector<RaceClassCombos> character_create_race_class_combos;
// the current stuff is at the bottom of this function // the current stuff is at the bottom of this function
void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs, uint32 ClientVersion) { void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit)
Inventory *inv; {
uint8 has_home = 0; /* Set Character Creation Limit */
uint8 has_bind = 0; ClientVersion client_version = ClientVersionFromBit(clientVersionBit);
size_t character_limit = EQLimits::CharacterCreationLimit(client_version);
/* Initialize Variables */
for (int i=0; i<10; i++) { // Validate against absolute server max
strcpy(cs->name[i], "<none>"); if (character_limit > EmuConstants::CHARACTER_CREATION_LIMIT)
cs->zone[i] = 0; character_limit = EmuConstants::CHARACTER_CREATION_LIMIT;
cs->level[i] = 0;
cs->tutorial[i] = 0;
cs->gohome[i] = 0;
}
// Force Titanium clients to use '8'
if (client_version == ClientVersion::Titanium)
character_limit = 8;
/* Get Character Info */ /* Get Character Info */
std::string cquery = StringFormat( std::string cquery = StringFormat(
"SELECT " "SELECT "
@ -72,52 +72,93 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*
"zone_id " // 19 "zone_id " // 19
"FROM " "FROM "
"character_data " "character_data "
"WHERE `account_id` = %i ORDER BY `name` LIMIT 10 ", account_id); "WHERE `account_id` = %i ORDER BY `name` LIMIT %u", accountID, character_limit);
auto results = database.QueryDatabase(cquery); int char_num = 0; auto results = database.QueryDatabase(cquery);
size_t character_count = results.RowCount();
if (character_count == 0) {
*outApp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
CharacterSelect_Struct *cs = (CharacterSelect_Struct *)(*outApp)->pBuffer;
cs->CharCount = 0;
cs->TotalChars = character_limit;
return;
}
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count);
*outApp = new EQApplicationPacket(OP_SendCharInfo, packet_size);
unsigned char *buff_ptr = (*outApp)->pBuffer;
CharacterSelect_Struct *cs = (CharacterSelect_Struct *)buff_ptr;
cs->CharCount = character_count;
cs->TotalChars = character_limit;
buff_ptr += sizeof(CharacterSelect_Struct);
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
CharacterSelectEntry_Struct *cse = (CharacterSelectEntry_Struct *)buff_ptr;
PlayerProfile_Struct pp; PlayerProfile_Struct pp;
Inventory inv;
uint32 character_id = (uint32)atoi(row[0]);
uint8 has_home = 0;
uint8 has_bind = 0;
memset(&pp, 0, sizeof(PlayerProfile_Struct)); memset(&pp, 0, sizeof(PlayerProfile_Struct));
/* Fill CharacterSelectEntry_Struct */
strcpy(cse->Name, row[1]);
cse->Class = (uint8)atoi(row[4]);
cse->Race = (uint32)atoi(row[3]);
cse->Level = (uint8)atoi(row[5]);
cse->ShroudClass = cse->Class;
cse->ShroudRace = cse->Race;
cse->Zone = (uint16)atoi(row[19]);
cse->Instance = 0;
cse->Gender = (uint8)atoi(row[2]);
cse->Face = (uint8)atoi(row[15]);
cse->Equip[0] = { 0 }; // Processed below
cse->Unknown15 = 0xFF;
cse->Unknown19 = 0xFF;
cse->DrakkinTattoo = (uint32)atoi(row[17]);
cse->DrakkinDetails = (uint32)atoi(row[18]);
cse->Deity = (uint32)atoi(row[6]);
cse->PrimaryIDFile = 0; // Processed Below
cse->SecondaryIDFile = 0; // Processed Below
cse->HairColor = (uint8)atoi(row[9]);
cse->BeardColor = (uint8)atoi(row[10]);
cse->EyeColor1 = (uint8)atoi(row[11]);
cse->EyeColor2 = (uint8)atoi(row[12]);
cse->HairStyle = (uint8)atoi(row[13]);
cse->Beard = (uint8)atoi(row[14]);
cse->Enabled = 1;
cse->Tutorial = 0; // Processed Below
cse->DrakkinHeritage = (uint32)atoi(row[16]);
cse->Unknown1 = 0;
cse->GoHome = 0; // Processed Below
cse->LastLogin = (uint32)atoi(row[7]); // RoF2 value: 1212696584
cse->Unknown2 = 0;
/* Fill End */
uint32 character_id = atoi(row[0]); if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) {
strcpy(cs->name[char_num], row[1]); cse->Tutorial = 1;
uint8 lvl = atoi(row[5]); }
cs->level[char_num] = lvl;
cs->class_[char_num] = atoi(row[4]);
cs->race[char_num] = atoi(row[3]);
cs->gender[char_num] = atoi(row[2]);
cs->deity[char_num] = atoi(row[6]);
cs->zone[char_num] = atoi(row[19]);
cs->face[char_num] = atoi(row[15]);
cs->haircolor[char_num] = atoi(row[9]);
cs->beardcolor[char_num] = atoi(row[10]);
cs->eyecolor2[char_num] = atoi(row[12]);
cs->eyecolor1[char_num] = atoi(row[11]);
cs->hairstyle[char_num] = atoi(row[13]);
cs->beard[char_num] = atoi(row[14]);
cs->drakkin_heritage[char_num] = atoi(row[16]);
cs->drakkin_tattoo[char_num] = atoi(row[17]);
cs->drakkin_details[char_num] = atoi(row[18]);
if (RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial)))
cs->tutorial[char_num] = 1;
if (RuleB(World, EnableReturnHomeButton)) { if (RuleB(World, EnableReturnHomeButton)) {
int now = time(nullptr); int now = time(nullptr);
if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome))
cs->gohome[char_num] = 1; cse->GoHome = 1;
} }
/* Set Bind Point Data for any character that may possibly be missing it for any reason */ /* Set Bind Point Data for any character that may possibly be missing it for any reason */
cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %i LIMIT 2", character_id); cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %i LIMIT 2", character_id);
auto results_bind = database.QueryDatabase(cquery); has_home = 0; has_bind = 0; auto results_bind = database.QueryDatabase(cquery);
for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) {
if (row_b[6] && atoi(row_b[6]) == 1){ has_home = 1; } if (row_b[6] && atoi(row_b[6]) == 1){ has_home = 1; }
if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; } if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; }
} }
if (has_home == 0 || has_bind == 0){ if (has_home == 0 || has_bind == 0) {
cquery = StringFormat("SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i", cquery = StringFormat("SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i",
cs->class_[char_num], cs->deity[char_num], cs->race[char_num]); cse->Class, cse->Deity, cse->Race);
auto results_bind = database.QueryDatabase(cquery); auto results_bind = database.QueryDatabase(cquery);
for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) {
/* If a bind_id is specified, make them start there */ /* If a bind_id is specified, make them start there */
@ -133,118 +174,96 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*
float z = atof(row_d[4]); float z = atof(row_d[4]);
if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); } if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); }
pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z; pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z;
} }
} }
pp.binds[0] = pp.binds[4]; pp.binds[0] = pp.binds[4];
/* If no home bind set, set it */ /* If no home bind set, set it */
if (has_home == 0){ if (has_home == 0) {
std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)"
" VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)",
character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 1); character_id, pp.binds[4].zoneId, 0, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z, pp.binds[4].heading, 1);
auto results_bset = QueryDatabase(query); auto results_bset = QueryDatabase(query);
} }
/* If no regular bind set, set it */ /* If no regular bind set, set it */
if (has_bind == 0){ if (has_bind == 0) {
std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)"
" VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)",
character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0); character_id, pp.binds[0].zoneId, 0, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z, pp.binds[0].heading, 0);
auto results_bset = QueryDatabase(query); auto results_bset = QueryDatabase(query);
} }
} }
/* Bind End */ /* Bind End */
/* Load Character Material Data for Char Select */
cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id);
auto results_b = database.QueryDatabase(cquery); uint8 slot = 0;
for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b)
{
slot = atoi(row_b[0]);
pp.item_tint[slot].rgb.red = atoi(row_b[1]);
pp.item_tint[slot].rgb.green = atoi(row_b[2]);
pp.item_tint[slot].rgb.blue = atoi(row_b[3]);
pp.item_tint[slot].rgb.use_tint = atoi(row_b[4]);
}
/* Load Inventory */ /* Load Inventory */
inv = new Inventory; // If we ensure that the material data is updated appropriately, we can do away with inventory loads
if (GetInventory(account_id, cs->name[char_num], inv)) if (GetInventory(accountID, cse->Name, &inv)) {
{
const Item_Struct* item = nullptr; const Item_Struct* item = nullptr;
const ItemInst* inst = nullptr; const ItemInst* inst = nullptr;
int16 invslot = 0; int16 invslot = 0;
for (uint32 matslot = 0; matslot < _MaterialCount; matslot++) for (uint32 matslot = 0; matslot < _MaterialCount; matslot++) {
{
invslot = Inventory::CalcSlotFromMaterial(matslot); invslot = Inventory::CalcSlotFromMaterial(matslot);
if (invslot == INVALID_INDEX) { continue; } if (invslot == INVALID_INDEX) { continue; }
inst = inv.GetItem(invslot);
inst = inv->GetItem(invslot);
if (inst == nullptr) { continue; } if (inst == nullptr) { continue; }
item = inst->GetItem(); item = inst->GetItem();
if (item == nullptr) { continue; } if (item == nullptr) { continue; }
if (matslot > 6) if (matslot > 6) {
{
uint32 idfile = 0; uint32 idfile = 0;
// Weapon Models // Weapon Models
if (inst->GetOrnamentationIDFile() != 0) if (inst->GetOrnamentationIDFile() != 0) {
{
idfile = inst->GetOrnamentationIDFile(); idfile = inst->GetOrnamentationIDFile();
cs->equip[char_num][matslot].material = idfile; cse->Equip[matslot].Material = idfile;
} }
else else {
{ if (strlen(item->IDFile) > 2) {
if (strlen(item->IDFile) > 2)
{
idfile = atoi(&item->IDFile[2]); idfile = atoi(&item->IDFile[2]);
cs->equip[char_num][matslot].material = idfile; cse->Equip[matslot].Material = idfile;
} }
} }
if (matslot == MaterialPrimary) if (matslot == MaterialPrimary) {
{ cse->PrimaryIDFile = idfile;
cs->primary[char_num] = idfile;
} }
else else {
{ cse->SecondaryIDFile = idfile;
cs->secondary[char_num] = idfile;
} }
} }
else else {
{
uint32 color = 0; uint32 color = 0;
if (pp.item_tint[matslot].rgb.use_tint) if (pp.item_tint[matslot].RGB.UseTint) {
{ color = pp.item_tint[matslot].Color;
color = pp.item_tint[matslot].color;
} }
else else {
{
color = inst->GetColor(); color = inst->GetColor();
} }
// Armor Materials/Models // Armor Materials/Models
cs->equip[char_num][matslot].material = item->Material; cse->Equip[matslot].Material = item->Material;
cs->equip[char_num][matslot].elitematerial = item->EliteMaterial; cse->Equip[matslot].EliteMaterial = item->EliteMaterial;
cs->equip[char_num][matslot].heroforgemodel = inst->GetOrnamentHeroModel(matslot); cse->Equip[matslot].HeroForgeModel = inst->GetOrnamentHeroModel(matslot);
cs->equip[char_num][matslot].color.color = color; cse->Equip[matslot].Color.Color = color;
} }
} }
} }
else else {
{ printf("Error loading inventory for %s\n", cse->Name);
printf("Error loading inventory for %s\n", cs->name[char_num]); }
/* Load Inventory End */
/* Load Character Material Data for Char Select */
cquery = StringFormat("SELECT slot, red, green, blue, use_tint, color FROM `character_material` WHERE `id` = %u", character_id);
auto results_b = database.QueryDatabase(cquery); uint8 slot = 0;
for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) {
slot = atoi(row_b[0]);
pp.item_tint[slot].RGB.Red = atoi(row_b[1]);
pp.item_tint[slot].RGB.Green = atoi(row_b[2]);
pp.item_tint[slot].RGB.Blue = atoi(row_b[3]);
pp.item_tint[slot].RGB.UseTint = atoi(row_b[4]);
} }
safe_delete(inv); buff_ptr += sizeof(CharacterSelectEntry_Struct);
if (++char_num > 10)
{
break;
}
} }
return;
} }
int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum) { int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum) {

View File

@ -20,6 +20,7 @@
#include "../common/shareddb.h" #include "../common/shareddb.h"
#include "../common/zone_numbers.h" #include "../common/zone_numbers.h"
#include "../common/eq_packet.h"
struct PlayerProfile_Struct; struct PlayerProfile_Struct;
struct CharCreate_Struct; struct CharCreate_Struct;
@ -29,7 +30,7 @@ struct CharacterSelect_Struct;
class WorldDatabase : public SharedDatabase { class WorldDatabase : public SharedDatabase {
public: public:
bool GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc, bool isTitanium); bool GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc, bool isTitanium);
void GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*, uint32 ClientVersion); void GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit);
int MoveCharacterToBind(int CharID, uint8 bindnum = 0); int MoveCharacterToBind(int CharID, uint8 bindnum = 0);
void GetLauncherList(std::vector<std::string> &result); void GetLauncherList(std::vector<std::string> &result);

View File

@ -4395,24 +4395,24 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
item = inst->GetItem(); item = inst->GetItem();
if (item != 0) if (item != 0)
{ {
ns->spawn.equipment[i].material = item->Material; ns->spawn.equipment[i].Material = item->Material;
ns->spawn.equipment[i].elitematerial = item->EliteMaterial; ns->spawn.equipment[i].EliteMaterial = item->EliteMaterial;
ns->spawn.equipment[i].heroforgemodel = item->HerosForgeModel; ns->spawn.equipment[i].HeroForgeModel = item->HerosForgeModel;
if (armor_tint[i]) if (armor_tint[i])
{ {
ns->spawn.colors[i].color = armor_tint[i]; ns->spawn.colors[i].Color = armor_tint[i];
} }
else else
{ {
ns->spawn.colors[i].color = item->Color; ns->spawn.colors[i].Color = item->Color;
} }
} }
else else
{ {
if (armor_tint[i]) if (armor_tint[i])
{ {
ns->spawn.colors[i].color = armor_tint[i]; ns->spawn.colors[i].Color = armor_tint[i];
} }
} }
} }
@ -4426,9 +4426,9 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
{ {
if(strlen(item->IDFile) > 2) if(strlen(item->IDFile) > 2)
{ {
ns->spawn.equipment[MaterialPrimary].material = atoi(&item->IDFile[2]); ns->spawn.equipment[MaterialPrimary].Material = atoi(&item->IDFile[2]);
} }
ns->spawn.colors[MaterialPrimary].color = GetEquipmentColor(MaterialPrimary); ns->spawn.colors[MaterialPrimary].Color = GetEquipmentColor(MaterialPrimary);
} }
} }
@ -4440,9 +4440,9 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
{ {
if(strlen(item->IDFile) > 2) if(strlen(item->IDFile) > 2)
{ {
ns->spawn.equipment[MaterialSecondary].material = atoi(&item->IDFile[2]); ns->spawn.equipment[MaterialSecondary].Material = atoi(&item->IDFile[2]);
} }
ns->spawn.colors[MaterialSecondary].color = GetEquipmentColor(MaterialSecondary); ns->spawn.colors[MaterialSecondary].Color = GetEquipmentColor(MaterialSecondary);
} }
} }
} }
@ -5061,7 +5061,7 @@ void Bot::SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32
wc->spawn_id = GetID(); wc->spawn_id = GetID();
wc->material = material; wc->material = material;
wc->color.color = color; wc->color.Color = color;
wc->wear_slot_id = material_slot; wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp); entity_list.QueueClients(this, outapp);

View File

@ -250,7 +250,7 @@ Client::Client(EQStreamInterface* ieqs)
AttemptedMessages = 0; AttemptedMessages = 0;
TotalKarma = 0; TotalKarma = 0;
m_ClientVersion = ClientVersion::Unknown; m_ClientVersion = ClientVersion::Unknown;
ClientVersionBit = 0; m_ClientVersionBit = 0;
AggroCount = 0; AggroCount = 0;
RestRegenHP = 0; RestRegenHP = 0;
RestRegenMana = 0; RestRegenMana = 0;
@ -3039,7 +3039,7 @@ void Client::Tell_StringID(uint32 string_id, const char *who, const char *messag
void Client::SetTint(int16 in_slot, uint32 color) { void Client::SetTint(int16 in_slot, uint32 color) {
Color_Struct new_color; Color_Struct new_color;
new_color.color = color; new_color.Color = color;
SetTint(in_slot, new_color); SetTint(in_slot, new_color);
database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color); database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color);
} }
@ -3050,8 +3050,8 @@ void Client::SetTint(int16 in_slot, Color_Struct& color) {
uint8 matslot = Inventory::CalcMaterialFromSlot(in_slot); uint8 matslot = Inventory::CalcMaterialFromSlot(in_slot);
if (matslot != _MaterialInvalid) if (matslot != _MaterialInvalid)
{ {
m_pp.item_tint[matslot].color = color.color; m_pp.item_tint[matslot].Color = color.Color;
database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.color); database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.Color);
} }
} }
@ -7468,7 +7468,7 @@ void Client::SendClearMercInfo()
void Client::DuplicateLoreMessage(uint32 ItemID) void Client::DuplicateLoreMessage(uint32 ItemID)
{ {
if(!(ClientVersionBit & BIT_RoFAndLater)) if (!(m_ClientVersionBit & BIT_RoFAndLater))
{ {
Message_StringID(0, PICK_LORE); Message_StringID(0, PICK_LORE);
return; return;

View File

@ -1022,7 +1022,7 @@ public:
inline int CompletedTasksInSet(int TaskSet) { return (taskstate ? taskstate->CompletedTasksInSet(TaskSet) :0); } inline int CompletedTasksInSet(int TaskSet) { return (taskstate ? taskstate->CompletedTasksInSet(TaskSet) :0); }
inline const ClientVersion GetClientVersion() const { return m_ClientVersion; } inline const ClientVersion GetClientVersion() const { return m_ClientVersion; }
inline const uint32 GetClientVersionBit() const { return ClientVersionBit; } inline const uint32 GetClientVersionBit() const { return m_ClientVersionBit; }
inline void SetClientVersion(ClientVersion in) { m_ClientVersion = in; } inline void SetClientVersion(ClientVersion in) { m_ClientVersion = in; }
/** Adventure Stuff **/ /** Adventure Stuff **/
@ -1140,7 +1140,7 @@ public:
void HandleLFGuildResponse(ServerPacket *pack); void HandleLFGuildResponse(ServerPacket *pack);
void SendLFGuildStatus(); void SendLFGuildStatus();
void SendGuildLFGuildStatus(); void SendGuildLFGuildStatus();
inline bool XTargettingAvailable() const { return ((ClientVersionBit & BIT_UFAndLater) && RuleB(Character, EnableXTargetting)); } inline bool XTargettingAvailable() const { return ((m_ClientVersionBit & BIT_UFAndLater) && RuleB(Character, EnableXTargetting)); }
inline uint8 GetMaxXTargets() const { return MaxXTargets; } inline uint8 GetMaxXTargets() const { return MaxXTargets; }
void SetMaxXTargets(uint8 NewMax); void SetMaxXTargets(uint8 NewMax);
bool IsXTarget(const Mob *m) const; bool IsXTarget(const Mob *m) const;
@ -1517,7 +1517,7 @@ private:
uint32 AttemptedMessages; uint32 AttemptedMessages;
ClientVersion m_ClientVersion; ClientVersion m_ClientVersion;
uint32 ClientVersionBit; uint32 m_ClientVersionBit;
int XPRate; int XPRate;

View File

@ -1191,8 +1191,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
conn_state = ReceivedZoneEntry; conn_state = ReceivedZoneEntry;
SetClientVersion(Connection()->GetClientVersion()); SetClientVersion(Connection()->GetClientVersion());
if (m_ClientVersion != ClientVersion::Unknown) m_ClientVersionBit = ClientBitFromVersion(Connection()->GetClientVersion());
ClientVersionBit = 1 << (static_cast<unsigned int>(m_ClientVersion) - 1);
bool siv = m_inv.SetInventoryVersion(m_ClientVersion); bool siv = m_inv.SetInventoryVersion(m_ClientVersion);
Log.Out(Logs::General, Logs::None, "%s inventory version to %s(%i)", (siv ? "Succeeded in setting" : "Failed to set"), ClientVersionName(m_ClientVersion), m_ClientVersion); Log.Out(Logs::General, Logs::None, "%s inventory version to %s(%i)", (siv ? "Succeeded in setting" : "Failed to set"), ClientVersionName(m_ClientVersion), m_ClientVersion);
@ -1307,9 +1306,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* Set item material tint */ /* Set item material tint */
for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++)
{ {
if (m_pp.item_tint[i].rgb.use_tint == 1 || m_pp.item_tint[i].rgb.use_tint == 255) if (m_pp.item_tint[i].RGB.UseTint == 1 || m_pp.item_tint[i].RGB.UseTint == 255)
{ {
m_pp.item_tint[i].rgb.use_tint = 0xFF; m_pp.item_tint[i].RGB.UseTint = 0xFF;
} }
} }
@ -1736,7 +1735,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
safe_delete(outapp); safe_delete(outapp);
} }
if (ClientVersionBit & BIT_UFAndLater) { if (m_ClientVersionBit & BIT_UFAndLater) {
outapp = new EQApplicationPacket(OP_XTargetResponse, 8); outapp = new EQApplicationPacket(OP_XTargetResponse, 8);
outapp->WriteUInt32(GetMaxXTargets()); outapp->WriteUInt32(GetMaxXTargets());
outapp->WriteUInt32(0); outapp->WriteUInt32(0);
@ -3171,7 +3170,6 @@ void Client::Handle_OP_AutoFire(const EQApplicationPacket *app)
void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) void Client::Handle_OP_Bandolier(const EQApplicationPacket *app)
{ {
// Although there are three different structs for OP_Bandolier, they are all the same size. // Although there are three different structs for OP_Bandolier, they are all the same size.
// //
if (app->size != sizeof(BandolierCreate_Struct)) { if (app->size != sizeof(BandolierCreate_Struct)) {
@ -3183,19 +3181,20 @@ void Client::Handle_OP_Bandolier(const EQApplicationPacket *app)
BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer;
switch (bs->action) { switch (bs->Action)
case BandolierCreate: {
case bandolierCreate:
CreateBandolier(app); CreateBandolier(app);
break; break;
case BandolierRemove: case bandolierRemove:
RemoveBandolier(app); RemoveBandolier(app);
break; break;
case BandolierSet: case bandolierSet:
SetBandolier(app); SetBandolier(app);
break; break;
default: default:
Log.Out(Logs::General, Logs::None, "Uknown Bandolier action %i", bs->action); Log.Out(Logs::General, Logs::None, "Unknown Bandolier action %i", bs->Action);
break;
} }
} }
@ -10442,16 +10441,16 @@ void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app)
if (mptbs->Action == 0) { if (mptbs->Action == 0) {
const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID); const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID);
if (BaseItem) { if (BaseItem) {
m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID; m_pp.potionbelt.Items[mptbs->SlotNumber].ID = BaseItem->ID;
m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon; m_pp.potionbelt.Items[mptbs->SlotNumber].Icon = BaseItem->Icon;
strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, BaseItem->Name, sizeof(BaseItem->Name)); strn0cpy(m_pp.potionbelt.Items[mptbs->SlotNumber].Name, BaseItem->Name, sizeof(BaseItem->Name));
database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.items[mptbs->SlotNumber].item_id, m_pp.potionbelt.items[mptbs->SlotNumber].icon); database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.Items[mptbs->SlotNumber].ID, m_pp.potionbelt.Items[mptbs->SlotNumber].Icon);
} }
} }
else { else {
m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 0; m_pp.potionbelt.Items[mptbs->SlotNumber].ID = 0;
m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0; m_pp.potionbelt.Items[mptbs->SlotNumber].Icon = 0;
strncpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, "\0", 1); m_pp.potionbelt.Items[mptbs->SlotNumber].Name[0] = '\0';
} }
} }

View File

@ -968,7 +968,7 @@ void Client::BulkSendInventoryItems()
void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
const Item_Struct* handyitem = nullptr; const Item_Struct* handyitem = nullptr;
uint32 numItemSlots = 80; //The max number of items passed in the transaction. uint32 numItemSlots = 80; //The max number of items passed in the transaction.
if (ClientVersionBit & BIT_RoFAndLater) { // RoF+ can send 200 items if (m_ClientVersionBit & BIT_RoFAndLater) { // RoF+ can send 200 items
numItemSlots = 200; numItemSlots = 200;
} }
const Item_Struct *item; const Item_Struct *item;

View File

@ -7020,7 +7020,7 @@ void Client::Undye() {
database.SaveInventory(CharacterID(), inst, slot2); database.SaveInventory(CharacterID(), inst, slot2);
} }
m_pp.item_tint[cur_slot].color = 0; m_pp.item_tint[cur_slot].Color = 0;
SendWearChange(cur_slot); SendWearChange(cur_slot);
} }

View File

@ -113,15 +113,15 @@ Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std:
pc->Lock(); pc->Lock();
/* Load Item Tints */ /* Load Item Tints */
pc->item_tint[0].color = pcs->item_tint[0].color; pc->item_tint[0].Color = pcs->item_tint[0].Color;
pc->item_tint[1].color = pcs->item_tint[1].color; pc->item_tint[1].Color = pcs->item_tint[1].Color;
pc->item_tint[2].color = pcs->item_tint[2].color; pc->item_tint[2].Color = pcs->item_tint[2].Color;
pc->item_tint[3].color = pcs->item_tint[3].color; pc->item_tint[3].Color = pcs->item_tint[3].Color;
pc->item_tint[4].color = pcs->item_tint[4].color; pc->item_tint[4].Color = pcs->item_tint[4].Color;
pc->item_tint[5].color = pcs->item_tint[5].color; pc->item_tint[5].Color = pcs->item_tint[5].Color;
pc->item_tint[6].color = pcs->item_tint[6].color; pc->item_tint[6].Color = pcs->item_tint[6].Color;
pc->item_tint[7].color = pcs->item_tint[7].color; pc->item_tint[7].Color = pcs->item_tint[7].Color;
pc->item_tint[8].color = pcs->item_tint[8].color; pc->item_tint[8].Color = pcs->item_tint[8].Color;
/* Load Physical Appearance */ /* Load Physical Appearance */
pc->haircolor = pcs->haircolor; pc->haircolor = pcs->haircolor;
@ -1413,8 +1413,8 @@ uint32 Corpse::GetEquipmentColor(uint8 material_slot) const {
item = database.GetItem(GetEquipment(material_slot)); item = database.GetItem(GetEquipment(material_slot));
if(item != NO_ITEM) { if(item != NO_ITEM) {
return item_tint[material_slot].rgb.use_tint ? return item_tint[material_slot].RGB.UseTint ?
item_tint[material_slot].color : item_tint[material_slot].Color :
item->Color; item->Color;
} }

View File

@ -1984,9 +1984,9 @@ void Client::QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call) {
void Client::DyeArmor(DyeStruct* dye){ void Client::DyeArmor(DyeStruct* dye){
int16 slot=0; int16 slot=0;
for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_TINT_END; i++) { for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_TINT_END; i++) {
if (m_pp.item_tint[i].rgb.blue != dye->dye[i].rgb.blue || if (m_pp.item_tint[i].RGB.Blue != dye->dye[i].RGB.Blue ||
m_pp.item_tint[i].rgb.red != dye->dye[i].rgb.red || m_pp.item_tint[i].RGB.Red != dye->dye[i].RGB.Red ||
m_pp.item_tint[i].rgb.green != dye->dye[i].rgb.green m_pp.item_tint[i].RGB.Green != dye->dye[i].RGB.Green
) { ) {
slot = m_inv.HasItem(32557, 1, invWherePersonal); slot = m_inv.HasItem(32557, 1, invWherePersonal);
if (slot != INVALID_INDEX){ if (slot != INVALID_INDEX){
@ -1994,18 +1994,18 @@ void Client::DyeArmor(DyeStruct* dye){
uint8 slot2=SlotConvert(i); uint8 slot2=SlotConvert(i);
ItemInst* inst = this->m_inv.GetItem(slot2); ItemInst* inst = this->m_inv.GetItem(slot2);
if(inst){ if(inst){
uint32 armor_color = ((uint32)dye->dye[i].rgb.red << 16) | ((uint32)dye->dye[i].rgb.green << 8) | ((uint32)dye->dye[i].rgb.blue); uint32 armor_color = ((uint32)dye->dye[i].RGB.Red << 16) | ((uint32)dye->dye[i].RGB.Green << 8) | ((uint32)dye->dye[i].RGB.Blue);
inst->SetColor(armor_color); inst->SetColor(armor_color);
database.SaveCharacterMaterialColor(this->CharacterID(), i, armor_color); database.SaveCharacterMaterialColor(this->CharacterID(), i, armor_color);
database.SaveInventory(CharacterID(),inst,slot2); database.SaveInventory(CharacterID(),inst,slot2);
if(dye->dye[i].rgb.use_tint) if(dye->dye[i].RGB.UseTint)
m_pp.item_tint[i].rgb.use_tint = 0xFF; m_pp.item_tint[i].RGB.UseTint = 0xFF;
else else
m_pp.item_tint[i].rgb.use_tint=0x00; m_pp.item_tint[i].RGB.UseTint=0x00;
} }
m_pp.item_tint[i].rgb.blue=dye->dye[i].rgb.blue; m_pp.item_tint[i].RGB.Blue=dye->dye[i].RGB.Blue;
m_pp.item_tint[i].rgb.red=dye->dye[i].rgb.red; m_pp.item_tint[i].RGB.Red=dye->dye[i].RGB.Red;
m_pp.item_tint[i].rgb.green=dye->dye[i].rgb.green; m_pp.item_tint[i].RGB.Green=dye->dye[i].RGB.Green;
SendWearChange(i); SendWearChange(i);
} }
else{ else{
@ -2420,7 +2420,7 @@ uint32 Client::GetEquipmentColor(uint8 material_slot) const
const Item_Struct *item = database.GetItem(GetEquipment(material_slot)); const Item_Struct *item = database.GetItem(GetEquipment(material_slot));
if(item != nullptr) if(item != nullptr)
return ((m_pp.item_tint[material_slot].rgb.use_tint) ? m_pp.item_tint[material_slot].color : item->Color); return ((m_pp.item_tint[material_slot].RGB.UseTint) ? m_pp.item_tint[material_slot].Color : item->Color);
return 0; return 0;
} }
@ -2476,97 +2476,99 @@ EQApplicationPacket* Client::ReturnItemPacket(int16 slot_id, const ItemInst* ins
return outapp; return outapp;
} }
static int16 BandolierSlotToWeaponSlot(int BandolierSlot) { static int16 BandolierSlotToWeaponSlot(int BandolierSlot)
{
switch(BandolierSlot) { switch (BandolierSlot)
case bandolierMainHand: {
return MainPrimary; case bandolierPrimary:
case bandolierOffHand: return MainPrimary;
return MainSecondary; case bandolierSecondary:
case bandolierRange: return MainSecondary;
return MainRange; case bandolierRange:
default: return MainRange;
return MainAmmo; default:
return MainAmmo;
} }
} }
void Client::CreateBandolier(const EQApplicationPacket *app) { void Client::CreateBandolier(const EQApplicationPacket *app)
{
// Store bandolier set with the number and name passed by the client, along with the items that are currently // Store bandolier set with the number and name passed by the client, along with the items that are currently
// in the players weapon slots. // in the players weapon slots.
BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s Creating Bandolier Set %i, Set Name: %s", GetName(), bs->number, bs->name); Log.Out(Logs::Detail, Logs::Inventory, "Char: %s Creating Bandolier Set %i, Set Name: %s", GetName(), bs->Number, bs->Name);
strcpy(m_pp.bandoliers[bs->number].name, bs->name); strcpy(m_pp.bandoliers[bs->Number].Name, bs->Name);
const ItemInst* InvItem = nullptr; const ItemInst* InvItem = nullptr;
const Item_Struct *BaseItem = nullptr; const Item_Struct *BaseItem = nullptr;
int16 WeaponSlot; int16 WeaponSlot = 0;
for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) { for(int BandolierSlot = bandolierPrimary; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot); WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot);
InvItem = GetInv()[WeaponSlot]; InvItem = GetInv()[WeaponSlot];
if(InvItem) { if(InvItem) {
BaseItem = InvItem->GetItem(); BaseItem = InvItem->GetItem();
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s adding item %s to slot %i", GetName(),BaseItem->Name, WeaponSlot); Log.Out(Logs::Detail, Logs::Inventory, "Char: %s adding item %s to slot %i", GetName(),BaseItem->Name, WeaponSlot);
m_pp.bandoliers[bs->number].items[BandolierSlot].item_id = BaseItem->ID; m_pp.bandoliers[bs->Number].Items[BandolierSlot].ID = BaseItem->ID;
m_pp.bandoliers[bs->number].items[BandolierSlot].icon = BaseItem->Icon; m_pp.bandoliers[bs->Number].Items[BandolierSlot].Icon = BaseItem->Icon;
database.SaveCharacterBandolier(this->CharacterID(), bs->number, BandolierSlot, m_pp.bandoliers[bs->number].items[BandolierSlot].item_id, m_pp.bandoliers[bs->number].items[BandolierSlot].icon, bs->name); database.SaveCharacterBandolier(this->CharacterID(), bs->Number, BandolierSlot, m_pp.bandoliers[bs->Number].Items[BandolierSlot].ID, m_pp.bandoliers[bs->Number].Items[BandolierSlot].Icon, bs->Name);
} }
else { else {
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s no item in slot %i", GetName(), WeaponSlot); Log.Out(Logs::Detail, Logs::Inventory, "Char: %s no item in slot %i", GetName(), WeaponSlot);
m_pp.bandoliers[bs->number].items[BandolierSlot].item_id = 0; m_pp.bandoliers[bs->Number].Items[BandolierSlot].ID = 0;
m_pp.bandoliers[bs->number].items[BandolierSlot].icon = 0; m_pp.bandoliers[bs->Number].Items[BandolierSlot].Icon = 0;
} }
} }
} }
void Client::RemoveBandolier(const EQApplicationPacket *app) { void Client::RemoveBandolier(const EQApplicationPacket *app)
{
BandolierDelete_Struct *bds = (BandolierDelete_Struct*)app->pBuffer; BandolierDelete_Struct *bds = (BandolierDelete_Struct*)app->pBuffer;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s removing set", GetName(), bds->number); Log.Out(Logs::Detail, Logs::Inventory, "Char: %s removing set", GetName(), bds->Number);
memset(m_pp.bandoliers[bds->number].name, 0, 32); memset(m_pp.bandoliers[bds->Number].Name, 0, 32);
for(int i = bandolierMainHand; i <= bandolierAmmo; i++) { for(int i = bandolierPrimary; i <= bandolierAmmo; i++) {
m_pp.bandoliers[bds->number].items[i].item_id = 0; m_pp.bandoliers[bds->Number].Items[i].ID = 0;
m_pp.bandoliers[bds->number].items[i].icon = 0; m_pp.bandoliers[bds->Number].Items[i].Icon = 0;
} }
database.DeleteCharacterBandolier(this->CharacterID(), bds->number); database.DeleteCharacterBandolier(this->CharacterID(), bds->Number);
} }
void Client::SetBandolier(const EQApplicationPacket *app) { void Client::SetBandolier(const EQApplicationPacket *app)
{
// Swap the weapons in the given bandolier set into the character's weapon slots and return // Swap the weapons in the given bandolier set into the character's weapon slots and return
// any items currently in the weapon slots to inventory. // any items currently in the weapon slots to inventory.
BandolierSet_Struct *bss = (BandolierSet_Struct*)app->pBuffer; BandolierSet_Struct *bss = (BandolierSet_Struct*)app->pBuffer;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s activating set %i", GetName(), bss->number); Log.Out(Logs::Detail, Logs::Inventory, "Char: %s activating set %i", GetName(), bss->Number);
int16 slot; int16 slot = 0;
int16 WeaponSlot; int16 WeaponSlot = 0;
ItemInst *BandolierItems[4]; // Temporary holding area for the weapons we pull out of their inventory ItemInst *BandolierItems[4]; // Temporary holding area for the weapons we pull out of their inventory
// First we pull the items for this bandolier set out of their inventory, this makes space to put the // First we pull the items for this bandolier set out of their inventory, this makes space to put the
// currently equipped items back. // currently equipped items back.
for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) { for(int BandolierSlot = bandolierPrimary; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
// If this bandolier set has an item in this position // If this bandolier set has an item in this position
if(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id) { if(m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID) {
WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot); WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot);
// Check if the player has the item specified in the bandolier set on them. // Check if the player has the item specified in the bandolier set on them.
// //
slot = m_inv.HasItem(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id, 1, slot = m_inv.HasItem(m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID, 1,
invWhereWorn|invWherePersonal); invWhereWorn|invWherePersonal);
// removed 'invWhereCursor' argument from above and implemented slots 30, 331-340 checks here // removed 'invWhereCursor' argument from above and implemented slots 30, 331-340 checks here
if (slot == INVALID_INDEX) { if (slot == INVALID_INDEX) {
if (m_inv.GetItem(MainCursor)) { if (m_inv.GetItem(MainCursor)) {
if (m_inv.GetItem(MainCursor)->GetItem()->ID == m_pp.bandoliers[bss->number].items[BandolierSlot].item_id && if (m_inv.GetItem(MainCursor)->GetItem()->ID == m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID &&
m_inv.GetItem(MainCursor)->GetCharges() >= 1) { // '> 0' the same, but this matches Inventory::_HasItem conditional check m_inv.GetItem(MainCursor)->GetCharges() >= 1) { // '> 0' the same, but this matches Inventory::_HasItem conditional check
slot = MainCursor; slot = MainCursor;
} }
else if (m_inv.GetItem(MainCursor)->GetItem()->ItemClass == 1) { else if (m_inv.GetItem(MainCursor)->GetItem()->ItemClass == 1) {
for(int16 CursorBagSlot = EmuConstants::CURSOR_BAG_BEGIN; CursorBagSlot <= EmuConstants::CURSOR_BAG_END; CursorBagSlot++) { for(int16 CursorBagSlot = EmuConstants::CURSOR_BAG_BEGIN; CursorBagSlot <= EmuConstants::CURSOR_BAG_END; CursorBagSlot++) {
if (m_inv.GetItem(CursorBagSlot)) { if (m_inv.GetItem(CursorBagSlot)) {
if (m_inv.GetItem(CursorBagSlot)->GetItem()->ID == m_pp.bandoliers[bss->number].items[BandolierSlot].item_id && if (m_inv.GetItem(CursorBagSlot)->GetItem()->ID == m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID &&
m_inv.GetItem(CursorBagSlot)->GetCharges() >= 1) { // ditto m_inv.GetItem(CursorBagSlot)->GetCharges() >= 1) { // ditto
slot = CursorBagSlot; slot = CursorBagSlot;
break; break;
@ -2630,14 +2632,14 @@ void Client::SetBandolier(const EQApplicationPacket *app) {
// Now we move the required weapons into the character weapon slots, and return any items we are replacing // Now we move the required weapons into the character weapon slots, and return any items we are replacing
// back to inventory. // back to inventory.
// //
for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) { for(int BandolierSlot = bandolierPrimary; BandolierSlot <= bandolierAmmo; BandolierSlot++) {
// Find the inventory slot corresponding to this bandolier slot // Find the inventory slot corresponding to this bandolier slot
WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot); WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot);
// if there is an item in this Bandolier slot ? // if there is an item in this Bandolier slot ?
if(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id) { if(m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID) {
// if the player has this item in their inventory, and it is not already where it needs to be // if the player has this item in their inventory, and it is not already where it needs to be
if(BandolierItems[BandolierSlot]) { if(BandolierItems[BandolierSlot]) {
// Pull the item that we are going to replace // Pull the item that we are going to replace

View File

@ -961,10 +961,10 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
// Only Player Races Wear Armor // Only Player Races Wear Armor
if (Mob::IsPlayerRace(race) || i > 6) if (Mob::IsPlayerRace(race) || i > 6)
{ {
ns->spawn.equipment[i].material = GetEquipmentMaterial(i); ns->spawn.equipment[i].Material = GetEquipmentMaterial(i);
ns->spawn.equipment[i].elitematerial = IsEliteMaterialItem(i); ns->spawn.equipment[i].EliteMaterial = IsEliteMaterialItem(i);
ns->spawn.equipment[i].heroforgemodel = GetHerosForgeModel(i); ns->spawn.equipment[i].HeroForgeModel = GetHerosForgeModel(i);
ns->spawn.colors[i].color = GetEquipmentColor(i); ns->spawn.colors[i].Color = GetEquipmentColor(i);
} }
} }
@ -2562,7 +2562,7 @@ void Mob::SendWearChange(uint8 material_slot)
wc->material = GetEquipmentMaterial(material_slot); wc->material = GetEquipmentMaterial(material_slot);
wc->elite_material = IsEliteMaterialItem(material_slot); wc->elite_material = IsEliteMaterialItem(material_slot);
wc->hero_forge_model = GetHerosForgeModel(material_slot); wc->hero_forge_model = GetHerosForgeModel(material_slot);
wc->color.color = GetEquipmentColor(material_slot); wc->color.Color = GetEquipmentColor(material_slot);
wc->wear_slot_id = material_slot; wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp); entity_list.QueueClients(this, outapp);
@ -2577,9 +2577,9 @@ void Mob::SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model, uin
wc->spawn_id = this->GetID(); wc->spawn_id = this->GetID();
wc->material = texture; wc->material = texture;
if (this->IsClient()) if (this->IsClient())
wc->color.color = GetEquipmentColor(slot); wc->color.Color = GetEquipmentColor(slot);
else else
wc->color.color = this->GetArmorTint(slot); wc->color.Color = this->GetArmorTint(slot);
wc->wear_slot_id = slot; wc->wear_slot_id = slot;
wc->unknown06 = unknown06; wc->unknown06 = unknown06;
@ -2607,7 +2607,7 @@ void Mob::SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uin
wc->spawn_id = this->GetID(); wc->spawn_id = this->GetID();
wc->material = GetEquipmentMaterial(material_slot); wc->material = GetEquipmentMaterial(material_slot);
wc->hero_forge_model = GetHerosForgeModel(material_slot); wc->hero_forge_model = GetHerosForgeModel(material_slot);
wc->color.color = color; wc->color.Color = color;
wc->wear_slot_id = material_slot; wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp); entity_list.QueueClients(this, outapp);
@ -2624,7 +2624,7 @@ void Mob::WearChange(uint8 material_slot, uint16 texture, uint32 color, uint32 h
wc->spawn_id = this->GetID(); wc->spawn_id = this->GetID();
wc->material = texture; wc->material = texture;
wc->hero_forge_model = hero_forge_model; wc->hero_forge_model = hero_forge_model;
wc->color.color = color; wc->color.Color = color;
wc->wear_slot_id = material_slot; wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp); entity_list.QueueClients(this, outapp);

View File

@ -1167,29 +1167,47 @@ bool ZoneDatabase::LoadCharacterMaterialColor(uint32 character_id, PlayerProfile
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
r = 0; r = 0;
i = atoi(row[r]); /* Slot */ r++; i = atoi(row[r]); /* Slot */ r++;
pp->item_tint[i].rgb.blue = atoi(row[r]); r++; pp->item_tint[i].RGB.Blue = atoi(row[r]); r++;
pp->item_tint[i].rgb.green = atoi(row[r]); r++; pp->item_tint[i].RGB.Green = atoi(row[r]); r++;
pp->item_tint[i].rgb.red = atoi(row[r]); r++; pp->item_tint[i].RGB.Red = atoi(row[r]); r++;
pp->item_tint[i].rgb.use_tint = atoi(row[r]); pp->item_tint[i].RGB.UseTint = atoi(row[r]);
} }
return true; return true;
} }
bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp){ bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp)
std::string query = StringFormat("SELECT `bandolier_id`, `bandolier_slot`, `item_id`, `icon`, `bandolier_name` FROM `character_bandolier` WHERE `id` = %u LIMIT 16", character_id); {
std::string query = StringFormat("SELECT `bandolier_id`, `bandolier_slot`, `item_id`, `icon`, `bandolier_name` FROM `character_bandolier` WHERE `id` = %u LIMIT %u",
character_id, EmuConstants::BANDOLIERS_SIZE);
auto results = database.QueryDatabase(query); int i = 0; int r = 0; int si = 0; auto results = database.QueryDatabase(query); int i = 0; int r = 0; int si = 0;
for (i = 0; i < EmuConstants::BANDOLIERS_COUNT; i++) for (i = 0; i < EmuConstants::BANDOLIERS_SIZE; i++) {
for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++) pp->bandoliers[i].Name[0] = '\0';
pp->bandoliers[i].items[si].icon = 0; for (int si = 0; si < EmuConstants::BANDOLIER_ITEM_COUNT; si++) {
pp->bandoliers[i].Items[si].ID = 0;
pp->bandoliers[i].Items[si].Icon = 0;
pp->bandoliers[i].Items[si].Name[0] = '\0';
}
}
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
r = 0; r = 0;
i = atoi(row[r]); /* Bandolier ID */ r++; i = atoi(row[r]); /* Bandolier ID */ r++;
si = atoi(row[r]); /* Bandolier Slot */ r++; si = atoi(row[r]); /* Bandolier Slot */ r++;
pp->bandoliers[i].items[si].item_id = atoi(row[r]); r++;
pp->bandoliers[i].items[si].icon = atoi(row[r]); r++; const Item_Struct* item_data = database.GetItem(atoi(row[r]));
strcpy(pp->bandoliers[i].name, row[r]); r++; if (item_data) {
si++; pp->bandoliers[i].Items[si].ID = item_data->ID; r++;
pp->bandoliers[i].Items[si].Icon = atoi(row[r]); r++; // Must use db value in case an Ornamentation is assigned
strncpy(pp->bandoliers[i].Items[si].Name, item_data->Name, 64);
}
else {
pp->bandoliers[i].Items[si].ID = 0; r++;
pp->bandoliers[i].Items[si].Icon = 0; r++;
pp->bandoliers[i].Items[si].Name[0] = '\0';
}
strcpy(pp->bandoliers[i].Name, row[r]); r++;
si++; // What is this for!?
} }
return true; return true;
} }
@ -1213,26 +1231,23 @@ bool ZoneDatabase::LoadCharacterTribute(uint32 character_id, PlayerProfile_Struc
return true; return true;
} }
bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp){ bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp)
std::string query = StringFormat("SELECT `potion_id`, `item_id`, `icon` FROM `character_potionbelt` WHERE `id` = %u LIMIT 4", character_id); {
std::string query = StringFormat("SELECT `potion_id`, `item_id`, `icon` FROM `character_potionbelt` WHERE `id` = %u LIMIT %u",
character_id, EmuConstants::POTION_BELT_ITEM_COUNT);
auto results = database.QueryDatabase(query); int i = 0; auto results = database.QueryDatabase(query); int i = 0;
for (i = 0; i < EmuConstants::POTION_BELT_SIZE; i++){ for (i = 0; i < EmuConstants::POTION_BELT_ITEM_COUNT; i++){
pp->potionbelt.items[i].icon = 0; pp->potionbelt.Items[i].Icon = 0;
pp->potionbelt.items[i].item_id = 0; pp->potionbelt.Items[i].ID = 0;
strncpy(pp->potionbelt.items[i].item_name, "\0", 1); pp->potionbelt.Items[i].Name[0] = '\0';
} }
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]); /* Potion belt slot number */ const Item_Struct *item_data = database.GetItem(atoi(row[1]));
uint32 item_id = atoi(row[1]); if (item_data == nullptr) { continue; }
const Item_Struct *item = database.GetItem(item_id); pp->potionbelt.Items[i].ID = item_data->ID;
pp->potionbelt.Items[i].Icon = atoi(row[2]);
if(!item) strncpy(pp->potionbelt.Items[i].Name, item_data->Name, 64);
continue;
pp->potionbelt.items[i].item_id = item_id;
pp->potionbelt.items[i].icon = atoi(row[2]);
strncpy(pp->potionbelt.items[i].item_name, item->Name, 64);
} }
return true; return true;
@ -1326,7 +1341,8 @@ bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struc
return true; return true;
} }
bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name){ bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name)
{
char bandolier_name_esc[64]; char bandolier_name_esc[64];
DoEscapeString(bandolier_name_esc, bandolier_name, strlen(bandolier_name)); DoEscapeString(bandolier_name_esc, bandolier_name, strlen(bandolier_name));
std::string query = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%u, %u, %u, %u, %u,'%s')", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name_esc); std::string query = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%u, %u, %u, %u, %u,'%s')", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name_esc);
@ -1335,7 +1351,8 @@ bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_i
return true; return true;
} }
bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon) { bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon)
{
std::string query = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%u, %u, %u, %u)", character_id, potion_id, item_id, icon); std::string query = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%u, %u, %u, %u)", character_id, potion_id, item_id, icon);
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
return true; return true;
@ -3529,9 +3546,9 @@ uint32 ZoneDatabase::UpdateCharacterCorpse(uint32 db_id, uint32 char_id, const c
dbpc->plat, dbpc->haircolor, dbpc->beardcolor, dbpc->eyecolor1, dbpc->plat, dbpc->haircolor, dbpc->beardcolor, dbpc->eyecolor1,
dbpc->eyecolor2, dbpc->hairstyle, dbpc->face, dbpc->beard, dbpc->eyecolor2, dbpc->hairstyle, dbpc->face, dbpc->beard,
dbpc->drakkin_heritage, dbpc->drakkin_tattoo, dbpc->drakkin_details, dbpc->drakkin_heritage, dbpc->drakkin_tattoo, dbpc->drakkin_details,
dbpc->item_tint[0].color, dbpc->item_tint[1].color, dbpc->item_tint[2].color, dbpc->item_tint[0].Color, dbpc->item_tint[1].Color, dbpc->item_tint[2].Color,
dbpc->item_tint[3].color, dbpc->item_tint[4].color, dbpc->item_tint[5].color, dbpc->item_tint[3].Color, dbpc->item_tint[4].Color, dbpc->item_tint[5].Color,
dbpc->item_tint[6].color, dbpc->item_tint[7].color, dbpc->item_tint[8].color, dbpc->item_tint[6].Color, dbpc->item_tint[7].Color, dbpc->item_tint[8].Color,
db_id); db_id);
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
@ -3622,15 +3639,15 @@ uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, ui
dbpc->drakkin_heritage, dbpc->drakkin_heritage,
dbpc->drakkin_tattoo, dbpc->drakkin_tattoo,
dbpc->drakkin_details, dbpc->drakkin_details,
dbpc->item_tint[0].color, dbpc->item_tint[0].Color,
dbpc->item_tint[1].color, dbpc->item_tint[1].Color,
dbpc->item_tint[2].color, dbpc->item_tint[2].Color,
dbpc->item_tint[3].color, dbpc->item_tint[3].Color,
dbpc->item_tint[4].color, dbpc->item_tint[4].Color,
dbpc->item_tint[5].color, dbpc->item_tint[5].Color,
dbpc->item_tint[6].color, dbpc->item_tint[6].Color,
dbpc->item_tint[7].color, dbpc->item_tint[7].Color,
dbpc->item_tint[8].color dbpc->item_tint[8].Color
); );
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
uint32 last_insert_id = results.LastInsertedID(); uint32 last_insert_id = results.LastInsertedID();
@ -3802,15 +3819,15 @@ bool ZoneDatabase::LoadCharacterCorpseData(uint32 corpse_id, PlayerCorpse_Struct
pcs->drakkin_heritage = atoul(row[i++]); // drakkin_heritage, pcs->drakkin_heritage = atoul(row[i++]); // drakkin_heritage,
pcs->drakkin_tattoo = atoul(row[i++]); // drakkin_tattoo, pcs->drakkin_tattoo = atoul(row[i++]); // drakkin_tattoo,
pcs->drakkin_details = atoul(row[i++]); // drakkin_details, pcs->drakkin_details = atoul(row[i++]); // drakkin_details,
pcs->item_tint[0].color = atoul(row[i++]); // wc_1, pcs->item_tint[0].Color = atoul(row[i++]); // wc_1,
pcs->item_tint[1].color = atoul(row[i++]); // wc_2, pcs->item_tint[1].Color = atoul(row[i++]); // wc_2,
pcs->item_tint[2].color = atoul(row[i++]); // wc_3, pcs->item_tint[2].Color = atoul(row[i++]); // wc_3,
pcs->item_tint[3].color = atoul(row[i++]); // wc_4, pcs->item_tint[3].Color = atoul(row[i++]); // wc_4,
pcs->item_tint[4].color = atoul(row[i++]); // wc_5, pcs->item_tint[4].Color = atoul(row[i++]); // wc_5,
pcs->item_tint[5].color = atoul(row[i++]); // wc_6, pcs->item_tint[5].Color = atoul(row[i++]); // wc_6,
pcs->item_tint[6].color = atoul(row[i++]); // wc_7, pcs->item_tint[6].Color = atoul(row[i++]); // wc_7,
pcs->item_tint[7].color = atoul(row[i++]); // wc_8, pcs->item_tint[7].Color = atoul(row[i++]); // wc_8,
pcs->item_tint[8].color = atoul(row[i++]); // wc_9 pcs->item_tint[8].Color = atoul(row[i++]); // wc_9
} }
query = StringFormat( query = StringFormat(
"SELECT \n" "SELECT \n"